(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=$('<p>')[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={}, preloadPrev=new Image(), preloadNext=new Image(),
closeBtn, image, video, prevBtn, nextBtn, caption, captionText, pbLoader, autoplayBtn, thumbs, wrapper,
defaults={
loop:       true,
thumbs:     true,
counter:    true,
title:      true,
autoplay:   false,
time:       3000,
history:    true,
hideFlash:  true,
zoomable:   true,
keys: {
close: '27, 88, 67',
prev:  '37, 80',
next:  '39, 78'
}},
overlay=$('<div id="pbOverlay">').append(pbLoader=$('<div class="pbLoader"><b></b><b></b><b></b></div>'),
prevBtn=$('<div id="pbPrevBtn" class="prevNext"><b></b></div>').on('click', next_prev),
nextBtn=$('<div id="pbNextBtn" class="prevNext"><b></b></div>').on('click', next_prev),
wrapper=$('<div class="wrapper">').append(image=$('<img>'),
video=$('<div>')
),
closeBtn=$('<div id="pbCloseBtn">').on('click', close)[0],
autoplayBtn=$('<div id="pbAutoplayBtn">').append($('<div class="pbProgress">')
),
caption=$('<div id="pbCaption">').append(captionText=$('<div class="pbCaptionText">').append('<div class="title"></div><div class="counter">'),
thumbs=$('<div>').addClass('pbThumbs')
)
);
function prepareDOM(){
isOldIE&&overlay.addClass('msie');
noPointerEvents&&overlay.hide();
autoplayBtn.off().on('click', APControl.toggle);
thumbs.off().on('click', 'a', thumbsStripe.click);
isMobile&&thumbs.css('overflow', 'auto');
overlay.off().on('click', 'img', function(e){
e.stopPropagation();
});
$(doc.body).prepend($(overlay));
docElm=doc.documentElement;
}
$.fn.photobox=function(target, settings, callback){
if($(this).data('_photobox'))
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);
$(this).data('_photobox', pb);
pb.callback=callback;
photoboxes.push(pb);
return this;
}
Photobox=function(_options, object, target){
this.options=$.extend({}, _options);
this.target=target;
this.selector=$(object||doc);
this.thumbsList=null;
var filtered=this.imageLinksFilter(object.find(target));
this.imageLinks=filtered[0];
this.images=filtered[1];
this.init();
};
Photobox.prototype={
init:function(){
var that=this;
if(this.options.thumbs)
this.thumbsList=thumbsStripe.generate(this.imageLinks);
this.selector.on('click.photobox', this.target, function(e){
e.preventDefault();
that.open(this);
});
this.observerTimeout=null;
if(this.selector[0].nodeType==1)
that.observeDOM(that.selector[0] ,function(){
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(startImage==-1) return false;
options=this.options;
images=this.images;
imageLinks=this.imageLinks;
photobox=this;
this.setup(1);
overlay.on(transitionend, function(){
overlay.off(transitionend).addClass('on');
changeImage(startImage, true);
}).addClass('show');
if(isOldIE)
overlay.trigger('MSTransitionEnd');
return false;
},
imageLinksFilter:function(obj){
var images=[], caption={}, captionlink;
return [obj.filter(function(i){
var link=this, img=$(link).find('img')[0];
if(!img) return false;
caption.content="<span>" +(img.getAttribute('alt')||img.getAttribute('title')||'') + "</span>";
captionlink=img.getAttribute('data-pb-captionlink')
if(captionlink){
captionlink=captionlink.split('[');
if(captionlink.length==2){
caption.linkText=captionlink[0];
caption.linkHref=captionlink[1].slice(0,-1);
}else{
caption.linkText=captionlink;
caption.linkHref=captionlink;
}
caption.content +=' <a href="'+ caption.linkHref +'">' + caption.linkText + '</a>';
}
images.push([link.href, caption.content]);
return true;
}), images];
},
observeDOM:(function(){
var MutationObserver=win.MutationObserver||win.WebKitMutationObserver,
eventListenerSupported=win.addEventListener;
return function(obj, callback){
if(MutationObserver){
var obs=new MutationObserver(function(mutations, observer){
if(mutations[0].addedNodes.length||mutations[0].removedNodes.length)
callback();
});
obs.observe(obj, { childList:true, subtree:true });
}
else if(eventListenerSupported){
obj.addEventListener('DOMNodeInserted', callback, false);
obj.addEventListener('DOMNodeRemoved', callback, false);
}}
})(),
setup:function (open){
var fn=open ? "on":"off";
image[0].src=blankImg;
if(open){
image.css({'transition':'0s'}).removeAttr('style');
overlay.show();
thumbs.html(this.thumbsList);
overlay[options.thumbs ? 'addClass':'removeClass']('thumbs');
if(options.thumbs){
activeThumb.removeAttr('class');
$(win).on('resize.photobox', thumbsStripe.calc);
thumbsStripe.calc();
}
if(this.images.length < 2)
overlay.removeClass('thumbs hasArrows hasCounter hasAutoplay');
else{
overlay.addClass('hasArrows hasCounter')
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');
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;
}}
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');
}
thumbsStripe={
generate:function(imageLinks){
var thumbsList=$('<ul>'), 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('<li'+ type +'><a href="'+ link.href +'"><img src="'+ image[0].src +'" alt="" title="'+ title +'" /></a></li>');
};
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,
changeActive:function(index, delay, thumbClick){
var lastIndex=activeThumb.index();
activeThumb.removeClass('active');
activeThumb=thumbs.find('li').eq(index).addClass('active');
if(thumbClick) return;
clearTimeout(this.changeActiveTimeout);
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);
},
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:function(e){
var ratio=thumbsTotalWidth / thumbsContainerWidth;
thumbs[0].scrollLeft=e.pageX * ratio - 500;
}};
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');
}},
setTitle:function(text){
if(text)
autoplayBtn.prop('title', text + ' (every ' + options.time/1000 + ' seconds)');
},
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;
return ok.close.indexOf(code) >=0&&close() ||
ok.next.indexOf(code) >=0&&changeImage(nextImage) ||
ok.prev.indexOf(code) >=0&&changeImage(prevImage)||true;
}
function next_prev(){
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);
stop();
video.empty();
preload.onerror=null;
image.add(video).data('zoom', 1);
activeType=imageLinks[imageIndex].rel=='video' ? 'video':'image';
if(activeType=='video'){
video.html(newVideo()).addClass('hide');
showContent(firstTime);
}else{
var loaderTimeout=setTimeout(function(){ overlay.addClass('pbLoading'); },50);
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');
options.autoplay&&APControl.progress.reset();
preload=new Image();
preload.onload=function(){ clearTimeout(loaderTimeout); showContent(firstTime); };
preload.onerror=imageError;
preload.src=activeURL;
}
captionText.on(transitionend, captionTextChange).addClass('change');
if(firstTime||isOldIE) captionTextChange();
if(options.thumbs)
thumbsStripe.changeActive(imageIndex, firstTime, thumbClick);
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});
}
function captionTextChange(){
captionText.off(transitionend).removeClass('change');
options.counter&&caption.find('.counter').text('(' + (activeImage + 1) + ' / ' + images.length + ')');
options.title&&caption.find('.title').html(images[activeImage][1]);
}
var history={
save:function(){
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
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);
}};
window.onpopstate=(function(){
var cached=window.onpopstate;
return function(event){
cached&&cached.apply(this, arguments);
if(event.state=='photobox')
history.load();
}})();
function imageError(){
overlay.addClass('error');
image[0].src=blankImg;
preload.onerror=null;
}
function showContent(firstTime){
var out, showSaftyTimer;
showSaftyTimer=setTimeout(show, 2000);
overlay.removeClass("pbLoading").addClass('hide');
image.add(video).removeAttr('style').removeClass('zoomable');
if(!firstTime&&imageLinks[lastActive].rel=='video'){
out=video;
image.addClass('prepare');
}
else
out=image;
if(firstTime||isOldIE)
show();
else
out.on(transitionend, show);
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' });
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();
}, 0);
},50);
}}
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 +')'});
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();
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();
}
function imageReposition(e){
var y=(e.clientY / docElm.clientHeight) * (docElm.clientHeight + 200) - 100,
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;
overlay.removeClass('show hide error pbLoading');
image.removeAttr('class').removeAttr('style').off().data('zoom',1);
if(noPointerEvents)
setTimeout(function(){ overlay.hide(); }, 200);
}
setTimeout(hide, 500);
}
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";
if(orgEvent.wheelDelta){ delta=orgEvent.wheelDelta/120; }
if(orgEvent.detail){ delta=-orgEvent.detail/3; }
deltaY=delta;
if(orgEvent.axis!==undefined&&orgEvent.axis===orgEvent.HORIZONTAL_AXIS){
deltaY=0;
deltaX=-1*delta;
}
if(orgEvent.wheelDeltaY!==undefined){ deltaY=orgEvent.wheelDeltaY/120; }
if(orgEvent.wheelDeltaX!==undefined){ deltaX=-1*orgEvent.wheelDeltaX/120; }
args.unshift(event, delta, deltaX, deltaY);
return ($.event.dispatch||$.event.handle).apply(this, args);
}
$.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),
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);
return ($.event.dispatch||$.event.handle).apply(that, args);
}}
};
$(doc).ready(prepareDOM);
window._photobox={
history:history
};})(jQuery, document, window);