PageSourceSearch

https://annalappe.com/wp-content/themes/thestory/js/portfolio-gallery.js?ver=1.9.14

js annalappe.com collected 2026-10-05 18:01:39 UTC 61,268 bytes, 1,753 lines download raw bytes

1/*!
2 * jScrollPane - v2.0.22 - 2015-04-25
3 * http://jscrollpane.kelvinluck.com/
4 *
5 * Copyright (c) 2014 Kelvin Luck
6 * Dual licensed under the MIT or GPL licenses.
7 */
8!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.fn.jScrollPane=function(b){function c(b,c){function d(c){var f,h,j,k,l,o,p=!1,q=!1;if(N=c,void 0===O)l=b.scrollTop(),o=b.scrollLeft(),b.css({overflow:"hidden",padding:0}),P=b.innerWidth()+ra,Q=b.innerHeight(),b.width(P),O=a('<div class="jspPane" />').css("padding",qa).append(b.children()),R=a('<div class="jspContainer" />').css({width:P+"px",height:Q+"px"}).append(O).appendTo(b);else{if(b.css("width",""),p=N.stickToBottom&&A(),q=N.stickToRight&&B(),k=b.innerWidth()+ra!=P||b.outerHeight()!=Q,k&&(P=b.innerWidth()+ra,Q=b.innerHeight(),R.css({width:P+"px",height:Q+"px"})),!k&&sa==S&&O.outerHeight()==T)return void b.width(P);sa=S,O.css("width",""),b.width(P),R.find(">.jspVerticalBar,>.jspHorizontalBar").remove().end()}O.css("overflow","auto"),S=c.contentWidth?c.contentWidth:O[0].scrollWidth,T=O[0].scrollHeight,O.css("overflow",""),U=S/P,V=T/Q,W=V>1,X=U>1,X||W?(b.addClass("jspScrollable"),f=N.maintainPosition&&($||ba),f&&(h=y(),j=z()),e(),g(),i(),f&&(w(q?S-P:h,!1),v(p?T-Q:j,!1)),F(),C(),L(),N.enableKeyboardNavigation&&H(),N.clickOnTrack&&m(),J(),N.hijackInternalLinks&&K()):(b.removeClass("jspScrollable"),O.css({top:0,left:0,width:R.width()-ra}),D(),G(),I(),n()),N.autoReinitialise&&!pa?pa=setInterval(function(){d(N)},N.autoReinitialiseDelay):!N.autoReinitialise&&pa&&clearInterval(pa),l&&b.scrollTop(0)&&v(l,!1),o&&b.scrollLeft(0)&&w(o,!1),b.trigger("jsp-initialised",[X||W])}function e(){W&&(R.append(a('<div class="jspVerticalBar" />').append(a('<div class="jspCap jspCapTop" />'),a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragTop" />'),a('<div class="jspDragBottom" />'))),a('<div class="jspCap jspCapBottom" />'))),ca=R.find(">.jspVerticalBar"),da=ca.find(">.jspTrack"),Y=da.find(">.jspDrag"),N.showArrows&&(ha=a('<a class="jspArrow jspArrowUp" />').bind("mousedown.jsp",k(0,-1)).bind("click.jsp",E),ia=a('<a class="jspArrow jspArrowDown" />').bind("mousedown.jsp",k(0,1)).bind("click.jsp",E),N.arrowScrollOnHover&&(ha.bind("mouseover.jsp",k(0,-1,ha)),ia.bind("mouseover.jsp",k(0,1,ia))),j(da,N.verticalArrowPositions,ha,ia)),fa=Q,R.find(">.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow").each(function(){fa-=a(this).outerHeight()}),Y.hover(function(){Y.addClass("jspHover")},function(){Y.removeClass("jspHover")}).bind("mousedown.jsp",function(b){a("html").bind("dragstart.jsp selectstart.jsp",E),Y.addClass("jspActive");var c=b.pageY-Y.position().top;return a("html").bind("mousemove.jsp",function(a){p(a.pageY-c,!1)}).bind("mouseup.jsp mouseleave.jsp",o),!1}),f())}function f(){da.height(fa+"px"),$=0,ea=N.verticalGutter+da.outerWidth(),O.width(P-ea-ra);try{0===ca.position().left&&O.css("margin-left",ea+"px")}catch(a){}}function g(){X&&(R.append(a('<div class="jspHorizontalBar" />').append(a('<div class="jspCap jspCapLeft" />'),a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragLeft" />'),a('<div class="jspDragRight" />'))),a('<div class="jspCap jspCapRight" />'))),ja=R.find(">.jspHorizontalBar"),ka=ja.find(">.jspTrack"),_=ka.find(">.jspDrag"),N.showArrows&&(na=a('<a class="jspArrow jspArrowLeft" />').bind("mousedown.jsp",k(-1,0)).bind("click.jsp",E),oa=a('<a class="jspArrow jspArrowRight" />').bind("mousedown.jsp",k(1,0)).bind("click.jsp",E),N.arrowScrollOnHover&&(na.bind("mouseover.jsp",k(-1,0,na)),oa.bind("mouseover.jsp",k(1,0,oa))),j(ka,N.horizontalArrowPositions,na,oa)),_.hover(function(){_.addClass("jspHover")},function(){_.removeClass("jspHover")}).bind("mousedown.jsp",function(b){a("html").bind("dragstart.jsp selectstart.jsp",E),_.addClass("jspActive");var c=b.pageX-_.position().left;return a("html").bind("mousemove.jsp",function(a){r(a.pageX-c,!1)}).bind("mouseup.jsp mouseleave.jsp",o),!1}),la=R.innerWidth(),h())}function h(){R.find(">.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow").each(function(){la-=a(this).outerWidth()}),ka.width(la+"px"),ba=0}function i(){if(X&&W){var b=ka.outerHeight(),c=da.outerWidth();fa-=b,a(ja).find(">.jspCap:visible,>.jspArrow").each(function(){la+=a(this).outerWidth()}),la-=c,Q-=c,P-=b,ka.parent().append(a('<div class="jspCorner" />').css("width",b+"px")),f(),h()}X&&O.width(R.outerWidth()-ra+"px"),T=O.outerHeight(),V=T/Q,X&&(ma=Math.ceil(1/U*la),ma>N.horizontalDragMaxWidth?ma=N.horizontalDragMaxWidth:ma<N.horizontalDragMinWidth&&(ma=N.horizontalDragMinWidth),_.width(ma+"px"),aa=la-ma,s(ba)),W&&(ga=Math.ceil(1/V*fa),ga>N.verticalDragMaxHeight?ga=N.verticalDragMaxHeight:ga<N.verticalDragMinHeight&&(ga=N.verticalDragMinHeight),Y.height(ga+"px"),Z=fa-ga,q($))}function j(a,b,c,d){var e,f="before",g="after";"os"==b&&(b=/Mac/.test(navigator.platform)?"after":"split"),b==f?g=b:b==g&&(f=b,e=c,c=d,d=e),a[f](c)[g](d)}function k(a,b,c){return function(){return l(a,b,this,c),this.blur(),!1}}function l(b,c,d,e){d=a(d).addClass("jspActive");var f,g,h=!0,i=function(){0!==b&&ta.scrollByX(b*N.arrowButtonSpeed),0!==c&&ta.scrollByY(c*N.arrowButtonSpeed),g=setTimeout(i,h?N.initialDelay:N.arrowRepeatFreq),h=!1};
8i(),f=e?"mouseout.jsp":"mouseup.jsp",e=e||a("html"),e.bind(f,function(){d.removeClass("jspActive"),g&&clearTimeout(g),g=null,e.unbind(f)})}function m(){n(),W&&da.bind("mousedown.jsp",function(b){if(void 0===b.originalTarget||b.originalTarget==b.currentTarget){var c,d=a(this),e=d.offset(),f=b.pageY-e.top-$,g=!0,h=function(){var a=d.offset(),e=b.pageY-a.top-ga/2,j=Q*N.scrollPagePercent,k=Z*j/(T-Q);if(0>f)$-k>e?ta.scrollByY(-j):p(e);else{if(!(f>0))return void i();e>$+k?ta.scrollByY(j):p(e)}c=setTimeout(h,g?N.initialDelay:N.trackClickRepeatFreq),g=!1},i=function(){c&&clearTimeout(c),c=null,a(document).unbind("mouseup.jsp",i)};return h(),a(document).bind("mouseup.jsp",i),!1}}),X&&ka.bind("mousedown.jsp",function(b){if(void 0===b.originalTarget||b.originalTarget==b.currentTarget){var c,d=a(this),e=d.offset(),f=b.pageX-e.left-ba,g=!0,h=function(){var a=d.offset(),e=b.pageX-a.left-ma/2,j=P*N.scrollPagePercent,k=aa*j/(S-P);if(0>f)ba-k>e?ta.scrollByX(-j):r(e);else{if(!(f>0))return void i();e>ba+k?ta.scrollByX(j):r(e)}c=setTimeout(h,g?N.initialDelay:N.trackClickRepeatFreq),g=!1},i=function(){c&&clearTimeout(c),c=null,a(document).unbind("mouseup.jsp",i)};return h(),a(document).bind("mouseup.jsp",i),!1}})}function n(){ka&&ka.unbind("mousedown.jsp"),da&&da.unbind("mousedown.jsp")}function o(){a("html").unbind("dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp"),Y&&Y.removeClass("jspActive"),_&&_.removeClass("jspActive")}function p(a,b){W&&(0>a?a=0:a>Z&&(a=Z),void 0===b&&(b=N.animateScroll),b?ta.animate(Y,"top",a,q):(Y.css("top",a),q(a)))}function q(a){void 0===a&&(a=Y.position().top),R.scrollTop(0),$=a||0;var c=0===$,d=$==Z,e=a/Z,f=-e*(T-Q);(ua!=c||wa!=d)&&(ua=c,wa=d,b.trigger("jsp-arrow-change",[ua,wa,va,xa])),t(c,d),O.css("top",f),b.trigger("jsp-scroll-y",[-f,c,d]).trigger("scroll")}function r(a,b){X&&(0>a?a=0:a>aa&&(a=aa),void 0===b&&(b=N.animateScroll),b?ta.animate(_,"left",a,s):(_.css("left",a),s(a)))}function s(a){void 0===a&&(a=_.position().left),R.scrollTop(0),ba=a||0;var c=0===ba,d=ba==aa,e=a/aa,f=-e*(S-P);(va!=c||xa!=d)&&(va=c,xa=d,b.trigger("jsp-arrow-change",[ua,wa,va,xa])),u(c,d),O.css("left",f),b.trigger("jsp-scroll-x",[-f,c,d]).trigger("scroll")}function t(a,b){N.showArrows&&(ha[a?"addClass":"removeClass"]("jspDisabled"),ia[b?"addClass":"removeClass"]("jspDisabled"))}function u(a,b){N.showArrows&&(na[a?"addClass":"removeClass"]("jspDisabled"),oa[b?"addClass":"removeClass"]("jspDisabled"))}function v(a,b){var c=a/(T-Q);p(c*Z,b)}function w(a,b){var c=a/(S-P);r(c*aa,b)}function x(b,c,d){var e,f,g,h,i,j,k,l,m,n=0,o=0;try{e=a(b)}catch(p){return}for(f=e.outerHeight(),g=e.outerWidth(),R.scrollTop(0),R.scrollLeft(0);!e.is(".jspPane");)if(n+=e.position().top,o+=e.position().left,e=e.offsetParent(),/^body|html$/i.test(e[0].nodeName))return;h=z(),j=h+Q,h>n||c?l=n-N.horizontalGutter:n+f>j&&(l=n-Q+f+N.horizontalGutter),isNaN(l)||v(l,d),i=y(),k=i+P,i>o||c?m=o-N.horizontalGutter:o+g>k&&(m=o-P+g+N.horizontalGutter),isNaN(m)||w(m,d)}function y(){return-O.position().left}function z(){return-O.position().top}function A(){var a=T-Q;return a>20&&a-z()<10}function B(){var a=S-P;return a>20&&a-y()<10}function C(){R.unbind(za).bind(za,function(a,b,c,d){ba||(ba=0),$||($=0);var e=ba,f=$,g=a.deltaFactor||N.mouseWheelSpeed;return ta.scrollBy(c*g,-d*g,!1),e==ba&&f==$})}function D(){R.unbind(za)}function E(){return!1}function F(){O.find(":input,a").unbind("focus.jsp").bind("focus.jsp",function(a){x(a.target,!1)})}function G(){O.find(":input,a").unbind("focus.jsp")}function H(){function c(){var a=ba,b=$;switch(d){case 40:ta.scrollByY(N.keyboardSpeed,!1);break;case 38:ta.scrollByY(-N.keyboardSpeed,!1);break;case 34:case 32:ta.scrollByY(Q*N.scrollPagePercent,!1);break;case 33:ta.scrollByY(-Q*N.scrollPagePercent,!1);break;case 39:ta.scrollByX(N.keyboardSpeed,!1);break;case 37:ta.scrollByX(-N.keyboardSpeed,!1)}return e=a!=ba||b!=$}var d,e,f=[];X&&f.push(ja[0]),W&&f.push(ca[0]),O.bind("focus.jsp",function(){b.focus()}),b.attr("tabindex",0).unbind("keydown.jsp keypress.jsp").bind("keydown.jsp",function(b){if(b.target===this||f.length&&a(b.target).closest(f).length){var g=ba,h=$;switch(b.keyCode){case 40:case 38:case 34:case 32:case 33:case 39:case 37:d=b.keyCode,c();break;case 35:v(T-Q),d=null;break;case 36:v(0),d=null}return e=b.keyCode==d&&g!=ba||h!=$,!e}}).bind("keypress.jsp",function(a){return a.keyCode==d&&c(),!e}),N.hideFocus?(b.css("outline","none"),"hideFocus"in R[0]&&b.attr("hideFocus",!0)):(b.css("outline",""),"hideFocus"in R[0]&&b.attr("hideFocus",!1))}function I(){b.attr("tabindex","-1").removeAttr("tabindex").unbind("
8keydown.jsp keypress.jsp"),O.unbind(".jsp")}function J(){if(location.hash&&location.hash.length>1){var b,c,d=escape(location.hash.substr(1));try{b=a("#"+d+', a[name="'+d+'"]')}catch(e){return}b.length&&O.find(d)&&(0===R.scrollTop()?c=setInterval(function(){R.scrollTop()>0&&(x(b,!0),a(document).scrollTop(R.position().top),clearInterval(c))},50):(x(b,!0),a(document).scrollTop(R.position().top)))}}function K(){a(document.body).data("jspHijack")||(a(document.body).data("jspHijack",!0),a(document.body).delegate("a[href*=#]","click",function(b){var c,d,e,f,g,h,i=this.href.substr(0,this.href.indexOf("#")),j=location.href;if(-1!==location.href.indexOf("#")&&(j=location.href.substr(0,location.href.indexOf("#"))),i===j){c=escape(this.href.substr(this.href.indexOf("#")+1));try{d=a("#"+c+', a[name="'+c+'"]')}catch(k){return}d.length&&(e=d.closest(".jspScrollable"),f=e.data("jsp"),f.scrollToElement(d,!0),e[0].scrollIntoView&&(g=a(window).scrollTop(),h=d.offset().top,(g>h||h>g+a(window).height())&&e[0].scrollIntoView()),b.preventDefault())}}))}function L(){var a,b,c,d,e,f=!1;R.unbind("touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick").bind("touchstart.jsp",function(g){var h=g.originalEvent.touches[0];a=y(),b=z(),c=h.pageX,d=h.pageY,e=!1,f=!0}).bind("touchmove.jsp",function(g){if(f){var h=g.originalEvent.touches[0],i=ba,j=$;return ta.scrollTo(a+c-h.pageX,b+d-h.pageY),e=e||Math.abs(c-h.pageX)>5||Math.abs(d-h.pageY)>5,i==ba&&j==$}}).bind("touchend.jsp",function(a){f=!1}).bind("click.jsp-touchclick",function(a){return e?(e=!1,!1):void 0})}function M(){var a=z(),c=y();b.removeClass("jspScrollable").unbind(".jsp"),O.unbind(".jsp"),b.replaceWith(ya.append(O.children())),ya.scrollTop(a),ya.scrollLeft(c),pa&&clearInterval(pa)}var N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea,fa,ga,ha,ia,ja,ka,la,ma,na,oa,pa,qa,ra,sa,ta=this,ua=!0,va=!0,wa=!1,xa=!1,ya=b.clone(!1,!1).empty(),za=a.fn.mwheelIntent?"mwheelIntent.jsp":"mousewheel.jsp";"border-box"===b.css("box-sizing")?(qa=0,ra=0):(qa=b.css("paddingTop")+" "+b.css("paddingRight")+" "+b.css("paddingBottom")+" "+b.css("paddingLeft"),ra=(parseInt(b.css("paddingLeft"),10)||0)+(parseInt(b.css("paddingRight"),10)||0)),a.extend(ta,{reinitialise:function(b){b=a.extend({},N,b),d(b)},scrollToElement:function(a,b,c){x(a,b,c)},scrollTo:function(a,b,c){w(a,c),v(b,c)},scrollToX:function(a,b){w(a,b)},scrollToY:function(a,b){v(a,b)},scrollToPercentX:function(a,b){w(a*(S-P),b)},scrollToPercentY:function(a,b){v(a*(T-Q),b)},scrollBy:function(a,b,c){ta.scrollByX(a,c),ta.scrollByY(b,c)},scrollByX:function(a,b){var c=y()+Math[0>a?"floor":"ceil"](a),d=c/(S-P);r(d*aa,b)},scrollByY:function(a,b){var c=z()+Math[0>a?"floor":"ceil"](a),d=c/(T-Q);p(d*Z,b)},positionDragX:function(a,b){r(a,b)},positionDragY:function(a,b){p(a,b)},animate:function(a,b,c,d){var e={};e[b]=c,a.animate(e,{duration:N.animateDuration,easing:N.animateEase,queue:!1,step:d})},getContentPositionX:function(){return y()},getContentPositionY:function(){return z()},getContentWidth:function(){return S},getContentHeight:function(){return T},getPercentScrolledX:function(){return y()/(S-P)},getPercentScrolledY:function(){return z()/(T-Q)},getIsScrollableH:function(){return X},getIsScrollableV:function(){return W},getContentPane:function(){return O},scrollToBottom:function(a){p(Z,a)},hijackInternalLinks:a.noop,destroy:function(){M()}}),d(c)}return b=a.extend({},a.fn.jScrollPane.defaults,b),a.each(["arrowButtonSpeed","trackClickSpeed","keyboardSpeed"],function(){b[this]=b[this]||b.speed}),this.each(function(){var d=a(this),e=d.data("jsp");e?e.reinitialise(b):(a("script",d).filter('[type="text/javascript"],:not([type])').remove(),e=new c(d,b),d.data("jsp",e))})},a.fn.jScrollPane.defaults={showArrows:!1,maintainPosition:!0,stickToBottom:!1,stickToRight:!1,clickOnTrack:!0,autoReinitialise:!1,autoReinitialiseDelay:500,verticalDragMinHeight:0,verticalDragMaxHeight:99999,horizontalDragMinWidth:0,horizontalDragMaxWidth:99999,contentWidth:void 0,animateScroll:!1,animateDuration:300,animateEase:"linear",hijackInternalLinks:!1,verticalGutter:4,horizontalGutter:4,mouseWheelSpeed:3,arrowButtonSpeed:0,arrowRepeatFreq:50,arrowScrollOnHover:!1,trackClickSpeed:0,trackClickRepeatFreq:70,verticalArrowPositions:"split",horizontalArrowPositions:"split",enableKeyboardNavigation:!0,hideFocus:!1,keyboardSpeed:0,initialDelay:300,speed:30,scrollPagePercent:.8}});
9
10
11
12(function($) {
13	"use strict";
14
15	var sliderId = 0;
16
17	/**
18	 * Pexeto Portfolio Slider. Displays an image slider with content on the side
19	 * or below it.
20	 * Dependencies:
21	 * - jQuery
22	 * - jQuery Easing : http://gsgd.co.uk/sandbox/jquery/easing/
23	 * - Images Loaded : http://github.com/desandro/imagesloaded
24	 * - Touchwipe by Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
25	 * - jScrollPane : http://jscrollpane.kelvinluck.com/
26	 * - MouseWheel : http://adomas.org/javascript-mouse-wheel/
27	 *
28	 * @author Pexeto
29	 * http://pexetothemes.com
30	 */
31	$.fn.pexetoPortfolioSlider = function(options) {
32		sliderId++;
33
34		var defaults = {
35			images            : [],
36			navigation        : null,
37			easing            : 'easeOutExpo',
38			//number of images to load on portions
39			loadPortions      : 5,
40			animationSpeed    : 700,
41			//events namespace
42			namespace         : 'pexslider' + sliderId,
43			minHeight         : 200,
44			
45			//selectors, IDs and classes
46			imgContainerSel   : '.ps-images:first',
47			contentSel        : '.ps-content:first',
48			contentTexSel     : '.ps-content-text:first',
49			leftArrowClass    : 'ps-left-arrow',
50			rightArrowClass   : 'ps-right-arrow',
51			loadingClass      : 'ps-loading',
52			navigationSel     : '.ps-navigation',
53			fullwidthClass    : 'ps-fullwidth',
54			navLoadingClass   : 'ps-nav-loading',
55			descClass         : 'ps-desc',
56			numClass          : 'ps-imgnum',
57			videoContainerSel : '.ps-video:first',
58			shareSel          : '.ps-share'
59		},
60			//define some helper variables that will be used globally by the plugin
61		o                 = $.extend(defaults, options),
62		$root             = this,
63		$mediaContainer   = $root.find(o.imgContainerSel),
64		$contentContainer = $root.find(o.contentSel),
65		$navWrapper       = $('.pg-nav-wrapper'),
66		inAnimation       = false,
67		$larrow           = null,
68		$rarrow           = null,
69		images            = o.images,
70		imgContWidth      = $mediaContainer.width(),
71		imgContHeight     = 0,
72		lastLoaded        = 0,
73		imgNum            = images.length,
74		pendingImg        = -1,
75		current           = 0,
76		fullwidth         = $root.hasClass(o.fullwidthClass),
77		contentPadding    = parseInt($contentContainer.css('paddingTop'), 10) + parseInt($contentContainer.css('border-top-width'), 10),
78		$desc             = $('<div />', {
79		'class'           : o.descClass
80		}).appendTo($mediaContainer),
81		$numContainer     = null,
82		video             = false,
83		jscrollApi        = null;
84
85		/**
86		 * Inits the main functionality - calls the initialization functions.
87		 */
88		function init() {
89
90			PEXETO.init.tabs();
91
92			var $share = $contentContainer.find(o.shareSel);
93			if($share.length) {
94				PEXETO.init.share($share);
95			}
96
97			if(!$mediaContainer.length) {
98				//it's a video slider
99				$mediaContainer = $root.find(o.videoContainerSel);
100				video = true;
101
102				$root.trigger("sliderLoaded");
103				PEXETO.init.ieIframeFix();
104
105			} else {
106				//it's an image slider, load the images
107				loadSlider();
108				if(imgNum > 1) {
109					addNavigation();
110				}
111				loadNextImages();
112			}
113
114			bindEventHandlers();
115
116			PEXETO.init.lightbox(null, {deeplinking:false});
117		}
118
119		/**
120		 * Loads the slider once all the images are loaded.
121		 */
122		function loadSlider() {
123			var img, $img, doOnImgLoaded;
124
125			//add the first image
126			img = new Image();
127			img.setAttribute("src", images[0].img);
128			$img = $(img);
129			images[0].el = $img;
130
131			$mediaContainer.append($img, false);
132			
133			doOnImgLoaded = function() {
134				//the first image has been loaded, show the slider
135				$root.css({
136					opacity: 0,
137					display: 'block'
138				});
139				imgContWidth = $mediaContainer.width();
140
141				$img.css({
142					opacity: 1
143				});
144				setContainerHeight($img, true);
145				$root.trigger("sliderLoaded");
146				showDescription(images[0].desc);
147			};
148
149			$mediaContainer.find('img').pexetoOnImgLoaded({callback: doOnImgLoaded});
150
151			if(imgNum > 1) {
152				$numContainer = $('<div />', {
153					'class': o.numClass,
154					'html': '1 / ' + imgNum
155				}).appendTo($mediaContainer);
156			}
157
158		}
159
160
161		/**
162		 * Binds event handlers.
163		 */
164		function bindEventHandlers() {
165			if(!video && imgNum > 1) {
166				//navigation event handlers
167				$larrow.on('click.' + o.namespace, doOnPreviousClicked);
168				$rarrow.on('click.' + o.namespace, doOnNextClicked);
169
170				$mediaContainer.touchwipe({
171					wipeLeft: doOnNextClicked,
172					wipeRight: doOnPreviousClicked,
173					preventDefaultEvents: false
174				});
175			}
176
177			$(window).on('resize.' + o.namespace, doOnWindowResize);
178			$root.on('destroy.' + o.namespace, destroySlider);
179			
180
181			var obj = video ? null : $root.find('img').eq(0);
182			$root.on('sliderVisible.' + o.namespace, function() {
183				addSliderNavigation();
184				setContainerHeight(obj, false);
185			});
186
187			
188		}
189
190		function addSliderNavigation(){
191			if(o.navigation){
192
193				$navWrapper.append(o.navigation).find(o.navigationSel).on('click.' + o.namespace, 'a', doOnNavigationClick);
194				
195			}
196		}
197
198		/**
199		 * Loads the next portion of images. The images are loaded in background
200		 * on portions of n-images.
201		 * - creates an image element
202		 * - binds on images loaded event
203		 */
204		function loadNextImages() {
205			var i, image, len = (lastLoaded + o.loadPortions >= imgNum) ? 
206				(imgNum - 1) : (lastLoaded + o.loadPortions);
207
208			for(i = lastLoaded + 1; i <= len; i += 1) {
209				if(!images[i].loaded) {
210					image = new Image();
211					image.setAttribute("src", images[i].img);
212
213					(function(i) {
214						images[i].el = $(image).imagesLoaded(function() {
215							images[i].loaded = true;
216							if(pendingImg === i) {
217								//image has been selected to show, but wasn't loaded yet
218								pendingImg = -1;
219								hideLoading();
220								showImage(true);
221							}
222						});
223					})(i);
224				}
225			}
226
227			lastLoaded = len;
228		}
229
230		/**
231		 * Displays an image in the slider.
232		 * @param  {boolean} next sets whether it is the next image (when it
233		 * is set to true) or the previous one (when it is set to false)
234		 */
235		function showImage(next) {
236			inAnimation = true;
237			var i = next ? 1 : -1,
238				$img = images[current + i].el.appendTo($mediaContainer).css({
239					left: imgContWidth * i
240				});
241
242			setContainerHeight($img, true);
243
244			if(imgNum > 1) {
245				$numContainer.html((current + i + 1) + ' / ' + imgNum);
246			}
247
248			//hide the current image
249			images[current].el.animate({
250				opacity: 0.5,
251				left: -imgContWidth * i
252			}, o.animationSpeed, o.easing, function() {
253				$(this).detach();
254			});
255
256			//show the next umage
257			$img.animate({
258				left: 0,
259				opacity: 1
260			}, o.animationSpeed, o.easing, function() {
261				current = current + i;
262				inAnimation = false;
263			});
264
265			showDescription(images[current + i].desc);
266		}
267
268		/**
269		 * Displays the descriptuon of the current image.
270		 * @param  {string} desc the description text
271		 */
272		function showDescription(desc) {
273			if(desc) {
274				$desc.html(desc).fadeIn();
275			} else {
276				$desc.fadeOut();
277			}
278		}
279
280		/**
281		 * Calculates and sets the container height according to the current
282		 * image height.
283		 * @param {object} $img    the current image displayed, it can be null
284		 * if it is a video currently displayed.
285		 * @param {boolean} animate sets whether to animate the container to the
286		 * new height or just change it with CSS.
287		 */
288		function setContainerHeight($img, animate) {
289			var height = video ? $mediaContainer.height() : Math.max($img.get(0).clientHeight, o.minHeight),
290				func = animate ? $.fn.animate : $.fn.css,
291				args = [{
292					height: height
293				}],
294				width, infoHeight = 0,
295				textHeight, full = fullwidth || $contentContainer.css('width') == $mediaContainer.css('width');
296
297			if(!video && full && PEXETO.utils.checkIfMobile()){
298				args[0]['height'] = $img.get(0).clientHeight;
299			}
300
301
302			if(animate) {
303				args.push(o.animationSpeed);
304			}
305
306			if(!video) {
307				func.apply($mediaContainer, args);
308			}
309
310			if($img && PEXETO.getBrowser().msie && (PEXETO.getBrowser().version=='10.0' ||  PEXETO.getBrowser().version=='9.0')){
311				//fix a stretching image problem on IE
312				var params = PEXETO.url.getCustomUrlParameters($img.attr('src'));
313				if(params.h && parseInt(params.h,10)<height){
314					$mediaContainer.css({maxHeight:params.h+'px'});
315					$img.css({maxHeight:params.h+'px'});
316				}
317			}
318
319			if(!full) {
320
321				//it is not in a full-width layout
322				width = $contentContainer.width();
323				$contentContainer.children().not(o.contentTexSel).each(function() {
324					infoHeight += $(this).outerHeight();
325				});
326
327				func.apply($root, args);
328				textHeight = height - infoHeight - 2 * contentPadding;
329
330				//init the scrolling plugin for longer texts in the description
331				jscrollApi = $contentContainer.find(o.contentTexSel).css({
332					height: textHeight,
333					width: width
334				}).jScrollPane().data('jsp');
335
336				if(!fullwidth) {
337				$root.find('img').on('imgmasonryloaded.'+ o.namespace,function(){
338					jscrollApi = $contentContainer.find(o.contentTexSel).jScrollPane().data('jsp');
339				});
340			}
341
342			} else {
343
344				if(jscrollApi) {
345					var $text = $contentContainer.find(o.contentTexSel).clone();
346					//disable the scrolling plugin
347					jscrollApi.destroy();
348					if(!$contentContainer.find(o.contentTexSel).length){
349						
350						$text.insertAfter($contentContainer.find('.ps-categories'));
351					}
352				}
353
354				//set an automatic height to the text area
355				$contentContainer.find(o.contentTexSel).css({
356					height: 'auto',
357					width: 'auto'
358				});
359
360			}
361		}
362
363		/**
364		 * Displays a loader on the current image.
365		 */
366		function showLoading() {
367			$mediaContainer.append('<div class="' + o.loadingClass + '"></div>');
368		}
369
370		/**
371		 * Hides the loader on the current image.
372		 */
373		function hideLoading() {
374			$mediaContainer.find("." + o.loadingClass).remove();
375		}
376
377
378		/**
379		 * Adds the navigation elements.
380		 */
381		function addNavigation() {
382			//previous/next arrows
383			$larrow = $('<div class="' + o.leftArrowClass + '" ></div>').appendTo($mediaContainer);
384			$rarrow = $('<div class="' + o.rightArrowClass + '" ></div>').appendTo($mediaContainer);
385
386		}
387
388		/**
389		 * On next arrow click event handler. Shows the next image if there is 
390		 * one.
391		 */
392		function doOnNextClicked() {
393			if(!inAnimation) {
394				if((current + 1) < imgNum) {
395					//show next image
396					if(images[current + 1].loaded) {
397						showImage(images[current + 1].el);
398					} else {
399						pendingImg = current + 1;
400						showLoading();
401					}
402					if(current + 1 === lastLoaded && lastLoaded + 1 < imgNum) {
403						loadNextImages();
404					}
405				} else {
406					animateLastImage(true);
407				}
408			}
409		}
410
411		/**
412		 * On previous arrow click event handler. Shows the previous one if
413		 * there is one.
414		 */
415		function doOnPreviousClicked() {
416			if(!inAnimation) {
417				if(current !== 0) {
418					//show previous image
419					showImage(false);
420				} else {
421					animateLastImage(false);
422				}
423			}
424		}
425
426		/**
427		 * Animates the slider in a way to show that there isn't anymore images
428		 * to display.
429		 * @param  {boolean} last sets whether it is the last image (when set 
430		 * to true) or whether it is the first image (when set to false)
431		 */
432		function animateLastImage(last) {
433			var i = last ? -1 : 1;
434			images[current].el.stop().animate({
435				left: i * 10
436			}, 100, function() {
437				$(this).stop().animate({
438					left: 0
439				}, 300);
440			});
441		}
442
443		/**
444		 * On window resize event handler. Resets the container size.
445		 */
446		function doOnWindowResize() {
447			var currentObj = video ? null : images[current].el;
448			setContainerHeight(currentObj, false);
449		}
450
451		/**
452		 * On navigation click event handler. Fired when one of the 
453		 * "next project", "previous project" or "back to gallery" buttons
454		 * is clicked.
455		 * @param  {object} e the even object
456		 */
457		function doOnNavigationClick(e) {
458			e.preventDefault();
459
460			if(!$(this).hasClass('disabled')) {
461				$root.trigger('navigationClick', [$(this).attr('rel')]);
462				$(e.delegateTarget).addClass(o.navLoadingClass);
463			}
464		}
465
466		/**
467		 * Destroys the slider. Removes all the registered event handlers.
468		 */
469		function destroySlider() {
470			$(window).off('.' + o.namespace);
471			$root.off('.' + o.namespace).children().off('.' + o.namespace);
472			$navWrapper.off('.' + o.namespace).find(o.navigationSel).eq(0).remove();
473		}
474
475		init();
476
477
478	};
479}(jQuery));
480
481
482
483/**
484 * Portfolio gallery.
485 *
486 * Dependencies:
487 * - jQuery
488 * - jQuery Masonry : http://masonry.desandro.com
489 * - PEXETO.init.masonry
490 * - Images Loaded : http://github.com/desandro/imagesloaded
491 * - jQuery Easing : http://gsgd.co.uk/sandbox/jquery/easing/
492 *
493 * @author Pexeto
494 * http://pexetothemes.com
495 */
496(function($) {
497	"use strict";
498
499	$.fn.pexetoGallery = function(options) {
500		var defaults         = {
501			ajaxUrl              : '',
502			itemsPerPage         : 10,
503			currentPage          : 1,
504			currentCat           : '-1',
505			imgheight            : 200,
506			additionalWidth      : 8,
507			columns              : 3,
508			filterCats           : [],
509			filterType           : 'exclude',
510			masonry              : false,
511			enableAJAX           : true,
512			animationSpeed       : 800,
513			sliderAnimationSpeed : 700,
514			pageUrl              : '',
515			singleItem           : false,
516			relatedLightbox      : false,
517			categoryFilter       : true,
518			itemsMap             : [],
519			easing               : 'easeOutExpo',
520			
521			//selectors, classes and IDs
522			itemSel              : '.pg-item',
523			imgWrapperSel        : '.pg-img-wrapper',
524			itemInfoSel          : '.pg-details',
525			paginationSel        : '.pg-pagination:first',
526			categorySel          : '.pg-cat-filter:first',
527			itemsContainerSel    : '.pg-items:first',
528			itemsWrapperSel      : '.pg-items-wrapper:first',
529			pageWrapperClass     : 'pg-page-wrapper',
530			parentSel            : '#full-width',
531			currentClass         : 'current',
532			loadingClass         : 'pg-loading',
533			elementLoadingClass  : 'pg-element-loading',
534			sliderSel            : '.ps-wrapper',
535			carItemClass         : 'pc-item',
536			carSel               : '.portfolio-carousel:first',
537			noCatLoadingClass    : 'pg-nocat-loading',
538			filterBtnSel         : '.pg-filter-btn',
539			openedClass          : 'filter-opened',
540			galleryHiddenClass   : 'pg-gallery-hidden'
541		},
542		o                    = $.extend(defaults, options),
543		$root                = this,
544		$itemsContainer      = null,
545		$categoryContainer   = null,
546		$paginationContainer = null,
547		$itemsWrapper        = null,
548		$parent              = $root.parent(),
549		$loadingContainer    = null,
550		currentPage          = o.currentPage,
551		previousPage         = currentPage,
552		cachedItems          = [],
553		cachedSliderItems    = [],
554		currentXhr           = null,
555		$currentPage         = null,
556		containerWidth       = 0,
557		supportsHistory      = (window.history && window.history.pushState) ? true : false,
558		currentCat           = o.currentCat,
559		currentItem          = o.itemId || 0,
560		inAnimation          = false,
561		$slider              = null,
562		$prevSlider          = null,
563		$carousel            = null,
564		$prevCarousel        = null,
565		pendingLoadings      = [],
566		$filterBtn           = $(o.filterBtnSel),
567		filterDisplayed      = false,
568		filterInAnimation    = false,
569		galleryView          = false,
570		$navWrapper          = $('.pg-nav-wrapper'),
571		resizeManager        = null,
572		$pageTop             = null,
573		$itemTop             = null,
574		scrollOffset         = 0;
575		
576
577
578		/**
579		 * Inits the main functionality - inits a gallery view if the gallery
580		 * has been selected or a slider view if a slider has been selected.
581		 */
582		function init() {
583			var $pageWrapper = null,
584				data,
585				catName;
586
587			if(!o.singleItem) {
588				//load the main gallery elements
589				$itemsContainer = $root.find(o.itemsContainerSel);
590				$categoryContainer = $(o.categorySel);
591				$paginationContainer = $root.find(o.paginationSel);
592				$itemsWrapper = $root.find(o.itemsWrapperSel);
593			}
594
595			setScrollTop();
596			bindEventHandlers();
597
598			if(!o.singleItem) {
599				//it is a gallery view
600				$pageWrapper = $root.find('.' + o.pageWrapperClass);
601				galleryView = true;
602
603				//add the currently loaded items to the cache
604				addCachedItem({
605					cat: currentCat,
606					page: currentPage
607				}, {
608					pageWrapper: $pageWrapper,
609					paginationUl: $paginationContainer.find('ul')
610				});
611
612
613				$currentPage = $pageWrapper;
614				setContainerWidth($currentPage);
615
616				//display the gallery view
617				showCurrentItems(false);
618				$loadingContainer = $categoryContainer.length ? $categoryContainer : $('<div />', {
619					'class': o.noCatLoadingClass
620				}).insertBefore($parent);
621
622
623				addLoadingTo($loadingContainer, null);
624
625				//init masonry
626				resizeManager = new PEXETO.utils.resizableImageGallery(o.itemSel, {
627					parent: $currentPage,
628					masonry : o.masonry
629				}).init();
630
631				//init lightbox
632				initLightbox($currentPage);
633
634				//set the current category name to the filter button
635				if(o.categoryFilter){
636					catName = $categoryContainer.find('a[data-cat="'+currentCat+'"]').html();
637					setFilterBtnText(catName);
638				}
639
640			} else {
641				//it is a single slider item, load the slider and the images
642				$slider = $root.find(o.sliderSel);
643				$carousel = $root.find(o.carSel);
644
645				if(o.video) {
646					initSlider([], null);
647					$slider.trigger('sliderVisible');
648				} else {
649					//load the slider images
650					data = {
651						action: 'pexeto_get_slider_images',
652						itemid: o.itemId
653					};
654
655					getJsonAjax(data).done(function(res) {
656						var images = res || [];
657
658						initSlider(images, null);
659					});
660				}
661			}
662
663
664		}
665
666
667		/***********************************************************************
668		 * GENERAL FUNCTIONS
669		 **********************************************************************/
670
671
672		 /**
673		  * Retrieves the current page items depending on the category and page
674		  * selected. First checks if these items have been already opened and
675		  * added to the cache and if so, loads the items from the cache. If not,
676		  * makes an AJAX request to load the items.
677		  * @param  {object} args containing the arguments for the page:
678		  * - cat : setting the category to load
679		  * - page : setting the number of the page to load
680		  * Once the items are retrieved, calls a function to display them.
681		  */
682		function getPageItems(args) {
683			var data, callback = (args.cat && !args.page) ? showCatItems : showPageItems,
684				page = args.page || 1,
685				cat = args.cat || '-1',
686				cachedItem = getCachedItem(args),
687				requireNav = (cachedItem && cachedItem.pagination) ? false : true,
688				$newPagination = null,
689				callbackArgs = {};
690
691			if(cachedItem && cachedItem.pages[page]) {
692				//the items with these arguments are already loaded
693				//show the cached items
694				if(cat !== currentCat) {
695					callbackArgs.pagination = cachedItem.pagination;
696				}
697				callbackArgs.page = cachedItem.pages[page];
698				callbackArgs.cached = true;
699				setCurrentVars(args);
700				callback.call(null, callbackArgs);
701			} else {
702				//prepare the data for an AJAX request
703				data = {
704					page: page,
705					number: o.itemsPerPage,
706					imgheight: o.imgheight,
707					columns: o.columns,
708					filter_cats: o.filterCats,
709					filter_type: o.filterType,
710					page_url: o.pageUrl,
711					require_nav: requireNav,
712					orderby: o.orderby,
713					order: o.order,
714					action: 'pexeto_get_portfolio_items'
715				};
716
717				if(cat !== '-1') {
718					data.cat = cat;
719				}
720
721				//make the AJAX request
722				getJsonAjax(data).done(function(res) {
723					//the AJAX request was successfull
724					var cacheItems = {},
725						$pagination = null,
726						$newPage = null,
727						args = {
728							cat: cat,
729							page: page
730						};
731
732
733					if(res.items !== undefined) {
734						setCurrentVars(args);
735						$newPage = $('<div />', {
736							'class': o.pageWrapperClass
737						}).append(res.items);
738
739						$newPage.find(o.itemSel).css({
740							opacity: 1
741						});
742						cacheItems.pageWrapper = $newPage;
743						callbackArgs.page = $newPage;
744
745						if(requireNav) {
746							//set the navigation
747							$pagination = $(res.pagination);
748							cacheItems.paginationUl = $pagination;
749							callbackArgs.pagination = $pagination;
750						}
751
752						initLightbox($newPage);
753						addCachedItem(args, cacheItems);
754						callback.call(null, callbackArgs);
755					}
756				});
757			}
758		}
759
760		/**
761		 * Loads a slider item. Checks if the item has been already loaded
762		 * and added to the cache and if so, uses the cached item. If not,
763		 * makes an AJAX request to load the slider data. After the data
764		 * is loaded, adds the slider element to the content wrapper and
765		 * initializes it.
766		 * @param  {int} itemId the ID of the item to be displayed as a slider
767		 */
768		function loadSliderItem(itemId) {
769			var data, doOnSliderContentLoaded;
770
771			if(!inAnimation && currentItem !== itemId) {
772				abortPendingRequests();
773
774				data = {
775					itemid: itemId,
776					single: o.singleItem,
777					action: 'pexeto_get_portfolio_slider_item'
778				};
779
780				if(!o.singleItem && getSiblingId(true, itemId) === -1) {
781					data.next = false;
782				}
783
784				if(!o.singleItem && getSiblingId(false, itemId) === -1) {
785					data.prev = false;
786				}
787
788				//set a callback function that will be called when the slider
789				//data is retrieved
790				doOnSliderContentLoaded = function(res) {
791					var images, itemLink, navigation;
792
793					if(res) {
794						$prevSlider = $slider || null;
795						$prevCarousel = $carousel || null;
796						currentItem = itemId;
797
798						if(supportsHistory) {
799							//change the URL in the browser
800							itemLink = res.permalink;
801							if($prevSlider) {
802								window.history.replaceState({
803									item: itemId
804								}, null, itemLink);
805							} else {
806								window.history.pushState({
807									item: itemId
808								}, null, itemLink);
809							}
810						}
811
812						//create the slider object
813						$slider = $(res.slider).appendTo($root).eq(0);
814
815						//create the carousel object
816						if(res.carousel) {
817							$carousel = $(res.carousel);
818							$carousel.insertAfter($slider);
819						}
820						images = res.images || [];
821						navigation = res.slider_nav || null;
822						initSlider(images, navigation);
823
824
825
826						if(!cachedSliderItems[itemId]) {
827							//cache the result
828							cachedSliderItems[itemId] = res;
829						}
830
831					}
832				};
833
834				if(cachedSliderItems[itemId]) {
835					//the slider item is cached, use the cache data
836					doOnSliderContentLoaded(cachedSliderItems[itemId]);
837				} else {
838					//load the data with AJAX
839					getJsonAjax(data).done(doOnSliderContentLoaded);
840				}
841			}
842		}
843
844		/**
845		 * Initializes the slider and carousel script.
846		 * @param  {array} images the images that the slider will display
847		 */
848		function initSlider(images, navigation) {
849			//init the slider
850			$slider.pexetoPortfolioSlider({
851				images: images,
852				navigation : navigation
853			});
854			//init the carousel
855			if($carousel) {
856				$carousel.pexetoCarousel({
857					selfDisplay: false
858				});
859			}
860		}
861
862		
863		/***********************************************************************
864		 * EVENT HANDLER FUNCTIONS
865		 **********************************************************************/
866
867		/**
868		 * Binds the event handlers.
869		 */
870		function bindEventHandlers() {
871
872			if(o.enableAJAX) {
873
874				if(!o.singleItem) {
875					//pagination events
876					$paginationContainer.on('click', 'a', doOnPaginationClick);
877
878					if(o.categoryFilter){
879						//category events
880						$categoryContainer.on('click', 'a', doOnCategoryClick);
881
882					}
883				}
884
885				//item click events
886				$root.on('click', 
887					o.itemSel + '[data-type="smallslider"],.' 
888						+ o.carItemClass + '[data-type="smallslider"], ' 
889						+ o.itemSel + '[data-type="fullslider"],.' 
890						+ o.carItemClass + '[data-type="fullslider"]', 
891					doOnItemSliderClick)
892				.on('click', 
893					o.itemSel + '[data-type="smallvideo"],.' 
894						+ o.carItemClass + '[data-type="smallvideo"], ' 
895						+ o.itemSel + '[data-type="fullvideo"],.' 
896						+ o.carItemClass + '[data-type="fullvideo"]', 
897					doOnItemVideoClick)
898				.on('navigationClick', o.sliderSel, doOnNavigationClick);
899
900				if(supportsHistory) {
901					window.onpopstate = function(e) {
902						if(!galleryView && !o.singleItem) {
903							backToGallery();
904						}
905					};
906				}
907			}
908
909			if(o.categoryFilter){
910				$filterBtn.on('click', doOnFilterClick);
911			}
912
913			//lightbox click events
914			$root.on('click', 
915				o.itemSel + '[data-type="lightbox"],.' 
916					+ o.carItemClass + '[data-type="lightbox"]', 
917				doOnItemLightboxClick)
918			.on('sliderLoaded', o.sliderSel, showSlider);
919
920			$(window).on('resize', function() {
921				if($currentPage) {
922					setContainerWidth($currentPage);
923				}
924
925				if(o.categoryFilter){
926					setFilterVisibility();
927				}
928			});
929		}
930
931		/**
932		 * On pagination click handler. If a new page has been requested, calls
933		 * a function to load the items from that page.
934		 * @param  {e} e the event object
935		 */
936		function doOnPaginationClick(e) {
937			e.preventDefault();
938			var page = $(this).data('page') || 1;
939
940			if(page !== currentPage && !inAnimation) {
941				abortPendingRequests();
942				addLoadingTo($paginationContainer, null);
943				getPageItems({
944					page: page,
945					cat: currentCat
946				});
947
948				//set the current CSS class to the selected page
949				$paginationContainer.find("." + o.currentClass).removeClass(o.currentClass);
950				$(this).addClass(o.currentClass);
951
952				if(supportsHistory) {
953					//change the URL in the browser
954					window.history.replaceState({
955						gallery: true
956					}, null, $(this).attr('href'));
957				}
958			}
959		}
960
961		/**
962		 * On category click handler. If a new category has been requested, 
963		 * calls a function to load the items from that category.
964		 * @param  {e} e the event object
965		 */
966		function doOnCategoryClick(e) {
967			e.preventDefault();
968			var cat = $(this).data('cat') || '-1';
969			cat = String(cat);
970
971			if(cat !== currentCat && !inAnimation) {
972				abortPendingRequests();
973
974				addLoadingTo($categoryContainer, null);
975				getPageItems({
976					cat: cat
977				});
978
979				//set the current CSS class to the selected category
980				$categoryContainer.find("." + o.currentClass).removeClass(o.currentClass);
981				$(this).addClass(o.currentClass);
982				
983				setFilterBtnText($(this).html());
984
985				if(filterDisplayed){
986					hideFilter();
987				}
988
989				if(supportsHistory) {
990					//change the URL in the browser
991					window.history.replaceState({
992						gallery: true
993					}, null, $(this).attr('href'));
994				}
995			}
996		}
997
998		
999		/**
1000		 * On slider item click event handler. Calls a function to load the 
1001		 * slider data of this item.
1002		 * @param  {object} e the event object
1003		 */
1004		function doOnItemSliderClick(e) {
1005			e.preventDefault();
1006
1007			var itemId = parseInt($(this).data('itemid'), 10);
1008
1009			loadSliderItem(itemId);
1010
1011			addLoadingTo(null, $(this));
1012		}
1013
1014		/**
1015		 * On video item click event handler. Calls a function to load the 
1016		 * video slider data of this item.
1017		 * @param  {object} e the event object
1018		 */
1019		function doOnItemVideoClick(e) {
1020			e.preventDefault();
1021
1022			var itemId = parseInt($(this).data('itemid'), 10);
1023
1024			loadSliderItem(itemId);
1025
1026			addLoadingTo(null, $(this));
1027		}
1028
1029		/**
1030		 * On lightbox item click event handler. Calls a function to load the 
1031		 * lightbox preview image of this item.
1032		 * @param  {object} e the event object
1033		 */
1034		function doOnItemLightboxClick(e) {
1035			var data,
1036				$el;
1037
1038			if(!o.relatedLightbox || $(this).hasClass(o.carItemClass)) {
1039				e.preventDefault();
1040
1041				data = {
1042					action: 'pexeto_get_slider_images',
1043					itemid: $(this).data('itemid')
1044				};
1045
1046				addLoadingTo(null, $(this));
1047
1048				//load the item images with an AJAX request
1049				getJsonAjax(data).done(function(res) {
1050					var images = res || [],
1051						pp_images = [],
1052						pp_titles = [],
1053						pp_descs = [],
1054						i, len;
1055
1056					for(i = 0, len = images.length; i < len; i++) {
1057						pp_images[i] = images[i].img;
1058						pp_titles[i] = '';
1059						pp_descs[i] = images[i].desc;
1060					}
1061
1062					removeLoading();
1063
1064					$.prettyPhoto.open(pp_images, pp_titles, pp_descs);
1065				});
1066			}
1067		}
1068
1069		/**
1070		 * Slider navigation click event handler - fired when one of the
1071		 * "next project", "previous project" or "back to gallery" button
1072		 * was clicked.
1073		 * @param  {object} e   the event object
1074		 * @param  {string} rel the rel attribute of the clicked button which
1075		 * sets the type of the action to perform.
1076		 */
1077		function doOnNavigationClick(e, rel) {
1078			var newItemId;
1079
1080			switch(rel) {
1081			case 'back':
1082				//back to gallery
1083				backToGallery();
1084				break;
1085			case 'prev':
1086				//load the previous item
1087				newItemId = getSiblingId(false, currentItem);
1088				if(newItemId !== -1) {
1089					loadSliderItem(newItemId);
1090				}
1091				break;
1092			case 'next':
1093				//load the next item
1094				newItemId = getSiblingId(true, currentItem);
1095				if(newItemId !== -1) {
1096					loadSliderItem(newItemId);
1097				}
1098				break;
1099			}
1100		}
1101
1102		function doOnFilterClick() {
1103			var that = $(this);
1104
1105			if(filterDisplayed){
1106				hideFilter();
1107			}else{
1108				showFilter();
1109			}
1110		}
1111
1112		function hideFilter(){
1113			if (!filterInAnimation) {
1114				filterInAnimation = true;
1115				$filterBtn.removeClass(o.openedClass);
1116				$categoryContainer.find('ul').animate({height:'hide'}, function() {
1117					filterInAnimation = false;
1118					filterDisplayed = false;
1119				});
1120			}
1121		}
1122
1123		function showFilter(){
1124			if (!filterInAnimation) {
1125				filterInAnimation = true;
1126				$filterBtn.addClass(o.openedClass);
1127				$categoryContainer.find('ul').animate({height:'show'}, function() {
1128					filterInAnimation = false;
1129					filterDisplayed = true;
1130				});
1131			}
1132		}
1133
1134
1135		/***********************************************************************
1136		 * AJAX FUNCTIONS
1137		 **********************************************************************/
1138
1139		/**
1140		 * Makes a JSON AJAX request with the specified data.
1141		 * @param  {object} data the data that will be sent in the request.
1142		 * @return {object}      the XHR object of the request
1143		 */
1144		function getJsonAjax(data) {
1145			if(!currentXhr) {
1146				currentXhr = $.ajax({
1147					url: o.ajaxUrl,
1148					data: data,
1149					dataType: 'json',
1150					type: 'GET'
1151				}).always(function() {
1152					currentXhr = null;
1153				});
1154			}
1155
1156			return currentXhr;
1157		}
1158
1159		/***********************************************************************
1160		 * ELEMENT ACCESS AND CHANGE FUNCTIONS
1161		 **********************************************************************/
1162
1163
1164		/**
1165		 * Displays the slider. Hides the current elements (can be a gallery
1166		 * or another slider) and then displays the slider with its carousel
1167		 * (if there is one set).
1168		 */
1169		function showSlider() {
1170			galleryView = false;
1171			removeLoading();
1172
1173			//set the display slider animation
1174			var displaySlider = function() {
1175				$slider.css({
1176					opacity: 0,
1177					display: 'block',
1178					marginTop: 200
1179				}).trigger('sliderVisible').animate({
1180					opacity: 1,
1181					marginTop: 0
1182				}, o.sliderAnimationSpeed);
1183				PEXETO.init.quickGallery();
1184				setTimeout(function() {
1185					
1186
1187					if(PEXETO.getBrowser().msie){
1188						$carousel.css({
1189							marginTop: 0,
1190							opacity: 1
1191						});
1192					}else{
1193						$carousel.css({
1194						opacity: 0,
1195						marginTop: 200
1196					}).animate({
1197							opacity: 1,
1198							marginTop: 0
1199						}, o.sliderAnimationSpeed);
1200					}
1201				}, o.sliderAnimationSpeed / 2);
1202
1203			};
1204
1205			if(o.singleItem && !$prevSlider) {
1206				//it is a single item page where just the slider should be
1207				//displayed
1208				displaySlider();
1209			} else {
1210				//scroll to the top, in case the slider has been opened
1211				//from the bottom of a long gallery
1212				$.scrollTo($pageTop, {
1213					duration: 500
1214					,offset: {
1215						top: scrollOffset
1216					}
1217				});
1218
1219
1220
1221				if($prevSlider) {
1222					//a slider is currently displayed, hide its elements
1223					
1224					//hide the carousel
1225					if($prevCarousel) {
1226						$prevCarousel.animate({
1227							opacity: 0
1228						}, o.sliderAnimationSpeed - 100, function() {
1229							$prevCarousel.trigger('destroy').remove();
1230						});
1231					}
1232
1233					//hide the slider
1234					$prevSlider.animate({
1235						marginTop: 500,
1236						opacity: 0
1237					}, o.sliderAnimationSpeed, function() {
1238						displaySlider();
1239						$(this).trigger('destroy').remove();
1240					});
1241
1242				} else {
1243					//a gallery is currently displayed, hide the gallery
1244					$itemsWrapper.animate({
1245						marginTop: 500,
1246						opacity: 0,
1247						height: 'hide'
1248					}, o.sliderAnimationSpeed, function() {
1249						$('body').addClass(o.galleryHiddenClass);
1250						displaySlider();
1251						if($categoryContainer.length){
1252							$categoryContainer.hide();
1253						}
1254						if(resizeManager){
1255							resizeManager.pause();
1256						}
1257					});
1258				}
1259			}
1260		}
1261
1262		/**
1263		 * Shows the gallery after a slider's back to gallery button has been
1264		 * clickec.
1265		 */
1266		function backToGallery() {
1267			if(o.singleItem || !$slider) {
1268				return false;
1269			}
1270
1271			removeLoading();
1272
1273			//hide the slider
1274			$slider.animate({
1275				marginTop: 500,
1276				opacity: 0
1277			}, o.sliderAnimationSpeed, function() {
1278				$slider.trigger('destroy').remove();
1279				$slider = null;
1280				currentItem = 0;
1281				galleryView = true;
1282
1283				if($categoryContainer.length){
1284					$categoryContainer.fadeIn();
1285				}
1286
1287				//show the gallery items
1288				$itemsWrapper.animate({
1289					height: 'show'
1290				}, 0);
1291				setContainerWidth($currentPage);
1292
1293				setMasonry($currentPage, false);
1294
1295				$itemsWrapper.animate({
1296					marginTop: 0,
1297					opacity: 1,
1298					height: 'show'
1299				}, o.sliderAnimationSpeed);
1300
1301				$('body').removeClass(o.galleryHiddenClass);
1302			});
1303
1304			//hide the carousel
1305			if($carousel) {
1306				$carousel.animate({
1307					marginTop: 500,
1308					opacity: 0
1309				}, o.sliderAnimationSpeed, function() {
1310					$carousel.trigger('destroy').remove();
1311					$carousel = null;
1312				});
1313			}
1314
1315			if(supportsHistory) {
1316				//change the URL of the current page in the browser address bar
1317				window.history.pushState({
1318					back: true
1319				}, null, o.pageUrl);
1320			}
1321		}
1322
1323		/**
1324		 * Shows a new set of items to be displayed.
1325		 * @param  {boolean} cached setting whether the items have been cached
1326		 * (loaded earlier) or they have been just loaded to the page.
1327		 * @return {object}        a $.Deferred object, which will be resolved
1328		 * once the animation has finished and the items are displayed.
1329		 */
1330		function showCurrentItems(cached) {
1331
1332			var loadedItems = [],
1333				$items = $currentPage.find(o.itemSel).css({
1334					opacity: 0
1335				}),
1336				itemNum = $items.length,
1337				pendingIndex = 0,
1338				def = new $.Deferred(),
1339				//timer animation function that will display the items one by one
1340				//with a delay after each item's animation
1341				timeoutAnimation = function(index) {
1342					setTimeout(function() {
1343						showItem(index);
1344					}, 100);
1345					if(index + 1 === itemNum) {
1346						def.resolve();
1347						removeLoading();
1348					}
1349				},
1350				//displays an item with the index set
1351				showItem = function(index) {
1352					var initArgs = o.masonry ? {marginTop:100} : {top:100},
1353						endArgs = o.masonry ? {marginTop:0} : {top:0};
1354
1355					endArgs.opacity = 1;
1356
1357					$items.eq(index).css(initArgs).animate(endArgs, function(){
1358						if(o.masonry && resizeManager){
1359							//refresh the masonry layout to position the items better
1360							resizeManager.refresh();
1361						}
1362					});
1363
1364					if(cached || loadedItems[index + 1]) {
1365						timeoutAnimation(index + 1);
1366					} else {
1367						pendingIndex = index + 1;
1368					}
1369				};
1370
1371			if(!cached) {
1372				$currentPage.find('img').on('imgmasonryloaded', function() {
1373					//an item's image has been loaded, set its index as loaded
1374					var $parent = $(this).parents(o.itemSel),
1375						index = $items.index($parent);
1376
1377					loadedItems[index] = true;
1378
1379					if(pendingIndex == index) {
1380						timeoutAnimation(index);
1381					}
1382				});
1383			} else {
1384				timeoutAnimation(0);
1385			}
1386
1387			return def.promise();
1388		}
1389
1390
1391		/**
1392		 * Displays a set of items after a new category has been selected.
1393		 * @param  {object} args arguments setting:
1394		 * - page - the jQuery page wrapper object
1395		 * - cached - boolean setting whether the items have been cached or just
1396		 * loaded to the page
1397		 * - pagination - a juery pagination object
1398		 */
1399		function showCatItems(args) {
1400			var $newPage = args.page,
1401				cached = args.cached,
1402				$pagination = args.pagination,
1403				def;
1404
1405			inAnimation = true;
1406
1407			//hide the current pagination
1408			$paginationContainer.css({
1409				opacity: 0
1410			});
1411
1412			//hide the current page
1413			$currentPage.animate({
1414				opacity: 0
1415			}, function() {
1416				//insert the new page
1417				$newPage.insertAfter($currentPage).css({
1418					opacity: 1,
1419					marginLeft: 0
1420				});
1421
1422				setContainerWidth($newPage);
1423
1424				$currentPage.detach();
1425				$currentPage = $newPage;
1426
1427				//animate the items in the page
1428				def = showCurrentItems(cached).done(function() {
1429					inAnimation = false;
1430					if($pagination) {
1431						$paginationContainer.css({
1432							opacity: 1
1433						}).html($pagination);
1434					}
1435				});
1436				$paginationContainer.find("." + o.currentClass).removeClass(o.currentClass);
1437				$paginationContainer.find('li:first a').addClass(o.currentClass);
1438
1439				setMasonry($newPage, false);
1440			});
1441		}
1442
1443		/**
1444		 * Displays a set of items after a new page has been selected.
1445		 * @param  {object} args arguments setting:
1446		 * - page - the jQuery page wrapper object
1447		 * - cached - boolean setting whether the items have been cached or just
1448		 * loaded to the page
1449		 */
1450		function showPageItems(args) {
1451			var $newPage = args.page,
1452				cached = args.cached,
1453				showNext = parseInt(previousPage, 10) > parseInt(currentPage, 10) ? false : true,
1454				currentHeight = 0,
1455				newPageMargin = showNext ? 0 : -containerWidth,
1456				currentPageMargin = showNext ? -containerWidth : 0;
1457
1458
1459			inAnimation = true;
1460
1461			$newPage.find('img').pexetoOnImgLoaded({callback:function() {
1462				//the images are loaded, show the new page
1463				//set the new page initial position for the animation
1464				//hide the loadings
1465				removeLoading();
1466
1467				setContainerWidth($newPage);
1468				$newPage.show().css({
1469					marginLeft: newPageMargin,
1470					opacity: 0
1471				});		
1472
1473				$.scrollTo($itemTop, {
1474					duration: 600,
1475					offset: {
1476						top: scrollOffset
1477					},
1478					onAfter : function(){
1479						//insert the new page
1480						if(showNext) {
1481							$newPage.insertAfter($currentPage).animate({
1482								opacity: 1
1483							});
1484						} else {
1485							$newPage.insertBefore($currentPage).animate({
1486								marginLeft: 0,
1487								opacity: 1
1488							}, o.animationSpeed, o.easing);
1489						}
1490
1491						$newPage.find(o.itemSel).css({opacity:1});	
1492
1493						setMasonry($newPage, false);
1494
1495						//set the container height according to the new page height
1496						setContainerHeight($newPage, true);
1497
1498						//animate the current page to get hidden
1499						$currentPage.animate({
1500							marginLeft: currentPageMargin,
1501							opacity: 0
1502						}, o.animationSpeed, o.easing, function() {
1503							$(this).detach();
1504							inAnimation = false;
1505						});
1506
1507						$currentPage = $newPage;
1508
1509					}});
1510			}});
1511		}
1512
1513		/**
1514		 * Sets the containerWidth variable and refreshes the current page 
1515		 * width with this value.
1516		 * @param {object} $page a jQuery object of the current page element
1517		 */
1518		function setContainerWidth($page) {
1519			if(galleryView) {
1520				containerWidth = $parent.width() + o.additionalWidth;
1521				$page.width(containerWidth);
1522			}
1523		}
1524
1525		/**
1526		 * Sets the items container height according to the wrapping page 
1527		 * height.
1528		 * @param {object} $page   a jQuery object of the current page element
1529		 * @param {boolean} animate sets whether to animate the height change
1530		 * or to just apply it with CSS.
1531		 */
1532		function setContainerHeight($page, animate) {
1533			var pageHeight = $page.height();
1534			if(animate) {
1535				$itemsContainer.animate({
1536					height: pageHeight
1537				}, o.animationSpeed);
1538			} else {
1539				$itemsContainer.css({
1540					height: pageHeight
1541				});
1542			}
1543		}
1544
1545		/**
1546		 * Populates the filter button text with the currently selected category
1547		 * name. This button is displayed on small screen devices only.
1548		 * @param {string} catName the name of the selected category
1549		 */
1550		function setFilterBtnText(catName){
1551			$filterBtn.find("span:first").html(catName);
1552		}
1553
1554		function setFilterVisibility(){
1555			if(galleryView && !$filterBtn.is(':visible') && !$categoryContainer.find('ul').is(':visible')){
1556				$categoryContainer.find('ul').css({display:'inline-block'});
1557			}
1558		}
1559
1560		/***********************************************************************
1561		 * HELPER FUNCTIONS
1562		 **********************************************************************/
1563
1564		/**
1565		 * Inits the Masonry script to the page wrapper element.
1566		 * @param {object} $page  jQuery page wrapper element that contains
1567		 * the masonry items
1568		 * @param {boolean} cached sets whether the items have been already
1569		 * cached and in this case will just refresh the script or whether
1570		 * they have been just loaded (will initialize the script)
1571		 */
1572		function setMasonry($page, cached) {
1573			if(cached && resizeManager) {
1574				//refresh the masonry
1575				resizeManager.resume();
1576				resizeManager.refresh();
1577			} else {
1578				//init the masonry
1579				resizeManager = new PEXETO.utils.resizableImageGallery(o.itemSel, {
1580					parent: $page,
1581					masonry : o.masonry
1582				}).init();
1583			}
1584		}
1585
1586
1587		/**
1588		 * Retrieves the ID of the next/previous sibling element.
1589		 * @param  {boolean} next   sets if it is the next item or the previous
1590		 * whose ID should be retrieved
1591		 * @param  {int}   itemId the ID of the current item
1592		 * @return {int}          the ID of the next/previous item if
1593		 * there is one or -1 of there isn't such an item.
1594		 */
1595		function getSiblingId(next, itemId) {
1596			var i = next ? 1 : -1,
1597				itemIndex = $.inArray(itemId, o.itemsMap),
1598				newItemId = -1;
1599			if(itemIndex !== -1 && o.itemsMap[itemIndex + i]) {
1600				newItemId = o.itemsMap[itemIndex + i];
1601			}
1602			return newItemId;
1603		}
1604
1605		/**
1606		 * Inits the lightbox when the preview images of all the items are set 
1607		 * to be related to each other in the lightbox.
1608		 * @param  {object} $wrapper a jQuery wrapper object that contains all
1609		 * the items
1610		 */
1611		function initLightbox($wrapper) {
1612			if(o.relatedLightbox) {
1613				PEXETO.init.lightbox($wrapper.find(o.itemSel 
1614					+ '[data-type="lightbox"] a,.' + o.carItemClass 
1615					+ '[data-type="lightbox"] a'), {deeplinking:false});
1616			}
1617		}
1618
1619		/**
1620		 * Sets the current page and category variables.
1621		 * @param {object} args containing the new variables that should be set:
1622		 * - page : the current page number
1623		 * - cat : the current category ID
1624		 */
1625		function setCurrentVars(args) {
1626			previousPage = currentPage;
1627			currentPage = args.page || 1;
1628			currentCat = args.cat || '-1';
1629		}
1630
1631		/**
1632		 * Adss a loaded page with items to the cache.
1633		 * @param {object} args  setting the current page and category:
1634		 * - page : the current page number
1635		 * - cat : the current category ID
1636		 * @param {object} items object containing the elements that will be
1637		 * cached with the following keys:
1638		 * - pageWrapper : the page wrapper object
1639		 * - paginationUL : the pagination object
1640		 */
1641		function addCachedItem(args, items) {
1642			var cat = args.cat || '-1',
1643				page = args.page || 1,
1644				cachedItem = null;
1645
1646			if(!cachedItems[cat]) {
1647				//add a cached item for this category
1648				cachedItems[cat] = {
1649					pages: [],
1650					pagination: null
1651				};
1652			}
1653			cachedItem = cachedItems[cat];
1654			if(!cachedItem.pages[page]) {
1655				//add a cached item for this page
1656				cachedItem.pages[page] = items.pageWrapper;
1657			}
1658
1659			if(items.paginationUl && !cachedItem.pagination) {
1660				//cache the pagination
1661				cachedItem.pagination = items.paginationUl;
1662			}
1663
1664		}
1665
1666		/**
1667		 * Retrieves an item from a cache according to the selected page and
1668		 * category.
1669		 * @param {object} args setting the current page and category:
1670		 * - page : the current page number
1671		 * - cat : the current category ID
1672		 * @return the cached object if it exists or null if it doesn't exist
1673		 */
1674		function getCachedItem(args) {
1675			var cat = args.cat || '-1',
1676				page = args.page || 1;
1677
1678			return cachedItems[cat] || null;
1679		}
1680
1681		/**
1682		 * Aborts the current pending requests.
1683		 */
1684		function abortPendingRequests() {
1685			if(currentXhr) {
1686				//there is a request pending, abort it and execute this one
1687				currentXhr.abort();
1688			}
1689			removeLoading();
1690		}
1691
1692		/**
1693		 * Appends a loader element to the specified element.
1694		 * @param {object} $el a jQuery element to which to append the loading
1695		 * @param {object} $parent the parent element to which to assign a loading
1696		 * class. If set to null, the loading class will be assigned to the $el
1697		 * element.
1698		 */
1699		function addLoadingTo($el, $parent) {
1700			var loadingEl = {};
1701
1702			if($el){
1703				var $pendingLoading = $('<div />', {
1704					"class": o.loadingClass
1705				});
1706				$el.append($pendingLoading).addClass(o.elementLoadingClass);
1707				loadingEl.loading = $pendingLoading;
1708			}
1709			
1710
1711			$parent = $parent || $el;
1712			$parent.addClass(o.elementLoadingClass);
1713			loadingEl.el = $parent;
1714
1715			pendingLoadings.push(loadingEl);
1716		}
1717
1718		/**
1719		 * Removes a loader element.
1720		 * @return {object} the jQuery loader element to remove.
1721		 */
1722		function removeLoading() {
1723			var pendingItem = null;
1724
1725			while(pendingLoadings.length) {
1726				pendingItem = pendingLoadings.pop();
1727				if(pendingItem.loading){
1728					pendingItem.loading.remove();
1729				}
1730				
1731				pendingItem.el.removeClass(o.elementLoadingClass);
1732
1733			}
1734		}
1735
1736		function setScrollTop(){
1737			$pageTop  = $navWrapper.length ? $navWrapper :  $('#content-container');
1738			$itemTop = $navWrapper;
1739
1740			if($('body').hasClass('fixed-header')){
1741				scrollOffset = -90;
1742			}else{
1743				scrollOffset = -15;
1744			}
1745
1746		}
1747
1748		init();
1749
1750		return this;
1751
1752	};
1753}(jQuery));

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.