PageSourceSearch

https://www.annaliesetassano.com/wp-content/plugins/jetpack/modules/carousel/jetpack-carousel.js?ver=20140505

js annaliesetassano.com collected 2026-09-27 11:13:48 UTC 52,317 bytes, 1,549 lines download raw bytes

1/* jshint sub: true, onevar: false, multistr: true, devel: true, smarttabs: true */
2/* global jetpackCarouselStrings, DocumentTouch, jetpackLikesWidgetQueue */
3
4
5jQuery(document).ready(function($) {
6
7	// gallery faded layer and container elements
8	var overlay, comments, gallery, container, nextButton, previousButton, info, transitionBegin,
9	caption, resizeTimeout, photo_info, close_hint, commentInterval, lastSelectedSlide,
10	screenPadding = 110, originalOverflow = $('body').css('overflow'), originalHOverflow = $('html').css('overflow'), proportion = 85,
11	last_known_location_hash = '', imageMeta, titleAndDescription, commentForm, leftColWrapper;
12
13	if ( window.innerWidth <= 760 ) {
14		screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 );
15
16		if ( screenPadding < 40 && ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) ) {
17			screenPadding = 0;
18		}
19	}
20
21	var keyListener = function(e){
22		switch(e.which){
23			case 38: // up
24				e.preventDefault();
25				container.scrollTop(container.scrollTop() - 100);
26				break;
27			case 40: // down
28				e.preventDefault();
29				container.scrollTop(container.scrollTop() + 100);
30				break;
31			case 39: // right
32				e.preventDefault();
33				gallery.jp_carousel('clearCommentTextAreaValue');
34				gallery.jp_carousel('next');
35				break;
36			case 37: // left
37			case 8: // backspace
38				e.preventDefault();
39				gallery.jp_carousel('clearCommentTextAreaValue');
40				gallery.jp_carousel('previous');
41				break;
42			case 27: // escape
43				e.preventDefault();
44				gallery.jp_carousel('clearCommentTextAreaValue');
45				container.jp_carousel('close');
46				break;
47			default:
48				// making jslint happy
49				break;
50		}
51	};
52
53	var resizeListener = function(/*e*/){
54		clearTimeout(resizeTimeout);
55		resizeTimeout = setTimeout(function(){
56			gallery
57				.jp_carousel('slides')
58				.jp_carousel('fitSlide', true);
59			gallery.jp_carousel('updateSlidePositions', true);
60			gallery.jp_carousel('fitMeta', true);
61		}, 200);
62	};
63
64	var prepareGallery = function( /*dataCarouselExtra*/ ){
65		if (!overlay) {
66			overlay = $('<div></div>')
67				.addClass('jp-carousel-overlay')
68				.css({
69					'position' : 'absolute',
70					'top'      : 0,
71					'right'    : 0,
72					'bottom'   : 0,
73					'left'     : 0
74				});
75
76			var buttons  = '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>';
77			if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) {
78			}
79
80			buttons  = $('<div class="jp-carousel-buttons">' + buttons + '</div>');
81
82			caption    = $('<h2></h2>');
83			photo_info = $('<div class="jp-carousel-photo-info"></div>').append(caption);
84
85			imageMeta = $('<div></div>')
86				.addClass('jp-carousel-image-meta')
87				.css({
88					'float'      : 'right',
89					'margin-top' : '20px',
90					'width'      :  '250px'
91				});
92
93			imageMeta
94				.append( buttons )
95				.append( '<ul class=\'jp-carousel-image-exif\' style=\'display:none;\'></ul>' )
96				.append( '<a class=\'jp-carousel-image-download\' style=\'display:none;\'></a>' )
97				.append( '<div class=\'jp-carousel-image-map\' style=\'display:none;\'></div>' );
98
99			titleAndDescription = $('<div></div>')
100				.addClass('jp-carousel-titleanddesc')
101				.css({
102					'width'      : '100%',
103					'margin-top' : imageMeta.css('margin-top')
104				});
105
106			var commentFormMarkup = '<div id="jp-carousel-comment-form-container">';
107
108			if ( jetpackCarouselStrings.local_comments_commenting_as && jetpackCarouselStrings.local_comments_commenting_as.length ) {
109				// Jetpack comments not enabled, fallback to local comments
110
111				if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) && 1 === Number( jetpackCarouselStrings.comment_registration ) ) {
112					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
113				} else {
114					commentFormMarkup += '<form id="jp-carousel-comment-form">';
115					commentFormMarkup += '<textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="' + jetpackCarouselStrings.write_comment + '"></textarea>';
116					commentFormMarkup += '<div id="jp-carousel-comment-form-submit-and-info-wrapper">';
117					commentFormMarkup += '<div id="jp-carousel-comment-form-commenting-as">' + jetpackCarouselStrings.local_comments_commenting_as + '</div>';
118					commentFormMarkup += '<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="'+jetpackCarouselStrings.post_comment+'" />';
119					commentFormMarkup += '<span id="jp-carousel-comment-form-spinner">&nbsp;</span>';
120					commentFormMarkup += '<div id="jp-carousel-comment-post-results"></div>';
121					commentFormMarkup += '</div>';
122					commentFormMarkup += '</form>';
123				}
124			}
125			commentFormMarkup += '</div>';
126
127			commentForm = $(commentFormMarkup)
128				.css({
129					'width'      : '100%',
130					'margin-top' : '20px',
131					'color'      : '#999'
132				});
133
134			comments = $('<div></div>')
135				.addClass('jp-carousel-comments')
136				.css({
137					'width'      : '100%',
138					'bottom'     : '10px',
139					'margin-top' : '20px'
140				});
141
142			var commentsLoading = $('<div id="jp-carousel-comments-loading"><span>'+jetpackCarouselStrings.loading_comments+'</span></div>')
143				.css({
144					'width'      : '100%',
145					'bottom'     : '10px',
146					'margin-top' : '20px'
147				});
148
149			var leftWidth = ( $(window).width() - ( screenPadding * 2 ) ) - (imageMeta.width() + 40);
150			leftWidth += 'px';
151
152			leftColWrapper = $('<div></div>')
153				.addClass('jp-carousel-left-column-wrapper')
154				.css({
155					'width' : Math.floor( leftWidth )
156				})
157				.append(titleAndDescription)
158				.append(commentForm)
159				.append(comments)
160				.append(commentsLoading);
161
162			var fadeaway = $('<div></div>')
163				.addClass('jp-carousel-fadeaway');
164
165			info = $('<div></div>')
166				.addClass('jp-carousel-info')
167				.css({
168					'top'   : Math.floor( ($(window).height() / 100) * proportion ),
169					'left'  : screenPadding,
170					'right' : screenPadding
171				})
172				.append(photo_info)
173				.append(imageMeta);
174
175			if ( window.innerWidth <= 760 ) {
176				photo_info.remove().insertAfter( titleAndDescription );
177				info.prepend( leftColWrapper );
178			}
179			else {
180				info.append( leftColWrapper );
181			}
182
183			var targetBottomPos = ( $(window).height() - parseInt( info.css('top'), 10 ) ) + 'px';
184
185			nextButton = $('<div><span></span></div>')
186				.addClass('jp-carousel-next-button')
187				.css({
188					'right'    : '15px'
189				});
190
191			previousButton = $('<div><span></span></div>')
192				.addClass('jp-carousel-previous-button')
193				.css({
194					'left'     : 0
195				});
196
197			nextButton.add( previousButton ).css( {
198				'position' : 'fixed',
199				'top' : '40px',
200				'bottom' : targetBottomPos,
201				'width' : screenPadding
202			} );
203
204			gallery = $('<div></div>')
205				.addClass('jp-carousel')
206				.css({
207					'position' : 'absolute',
208					'top'      : 0,
209					'bottom'   : targetBottomPos,
210					'left'     : 0,
211					'right'    : 0
212				});
213
214			close_hint = $('<div class="jp-carousel-close-hint"><span>&times;</span></div>')
215				.css({
216					position : 'fixed'
217				});
218
219			container = $('<div></div>')
220				.addClass('jp-carousel-wrap')
221				.addClass( 'jp-carousel-transitions' );
222
223			if ( 'white' === jetpackCarouselStrings.background_color ) {
224				 container.addClass('jp-carousel-light');
225			}
226
227			container.css({
228					'position'   : 'fixed',
229					'top'        : 0,
230					'right'      : 0,
231					'bottom'     : 0,
232					'left'       : 0,
233					'z-index'    : 2147483647,
234					'overflow-x' : 'hidden',
235					'overflow-y' : 'auto',
236					'direction'  : 'ltr'
237				})
238				.hide()
239				.append(overlay)
240				.append(gallery)
241				.append(fadeaway)
242				.append(info)
243				.append(nextButton)
244				.append(previousButton)
245				.append(close_hint)
246				.appendTo($('body'))
247				.click(function(e){
248					var target = $(e.target), wrap = target.parents('div.jp-carousel-wrap'), data = wrap.data('carousel-extra'),
249						slide = wrap.find('div.selected'), attachment_id = slide.data('attachment-id');
250					data = data || [];
251
252					if ( target.is(gallery) || target.parents().add(target).is(close_hint) ) {
253						container.jp_carousel('close');
254					} else if ( target.hasClass('jp-carousel-commentlink') ) {
255						e.preventDefault();
256						e.stopPropagation();
257						$(window).unbind('keydown', keyListener);
258						container.animate({scrollTop: parseInt(info.position()['top'], 10)}, 'fast');
259						$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
260						$('#jp-carousel-comment-form-comment-field').focus();
261					} else if ( target.hasClass('jp-carousel-comment-login') ) {
262						var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id;
263
264						window.location.href = url;
265					} else if ( target.parents('#jp-carousel-comment-form-container').length ) {
266						var textarea = $('#jp-carousel-comment-form-comment-field')
267							.blur(function(){
268								$(window).bind('keydown', keyListener);
269							})
270							.focus(function(){
271								$(window).unbind('keydown', keyListener);
272							});
273
274						var emailField = $('#jp-carousel-comment-form-email-field')
275							.blur(function(){
276								$(window).bind('keydown', keyListener);
277							})
278							.focus(function(){
279								$(window).unbind('keydown', keyListener);
280							});
281
282						var authorField = $('#jp-carousel-comment-form-author-field')
283							.blur(function(){
284								$(window).bind('keydown', keyListener);
285							})
286							.focus(function(){
287								$(window).unbind('keydown', keyListener);
288							});
289
290						var urlField = $('#jp-carousel-comment-form-url-field')
291							.blur(function(){
292								$(window).bind('keydown', keyListener);
293							})
294							.focus(function(){
295								$(window).unbind('keydown', keyListener);
296							});
297
298						if ( textarea && textarea.attr('id') === target.attr('id')) {
299							// For first page load
300							$(window).unbind('keydown', keyListener);
301							$('#jp-carousel-comment-form-submit-and-info-wrapper').slideDown('fast');
302						} else if ( target.is( 'input[type="submit"]' ) ) {
303							e.preventDefault();
304							e.stopPropagation();
305
306							$('#jp-carousel-comment-form-spinner').spin('small', 'white');
307
308							var ajaxData = {
309								action: 'post_attachment_comment',
310								nonce:   jetpackCarouselStrings.nonce,
311								blog_id: data['blog_id'],
312								id:      attachment_id,
313								comment: textarea.val()
314							};
315
316							if ( ! ajaxData['comment'].length ) {
317								gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.no_comment_text});
318								return;
319							}
320
321							if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) {
322								ajaxData['email']  = emailField.val();
323								ajaxData['author'] = authorField.val();
324								ajaxData['url']    = urlField.val();
325
326								if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) {
327									if ( ! ajaxData['email'].length || ! ajaxData['email'].match('@') ) {
328										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-email-field', 'error': jetpackCarouselStrings.no_comment_email});
329										return;
330									} else if ( ! ajaxData['author'].length ) {
331										gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-author-field', 'error': jetpackCarouselStrings.no_comment_author});
332										return;
333									}
334								}
335							}
336
337							$.ajax({
338								type:       'POST',
339								url:        jetpackCarouselStrings.ajaxurl,
340								data:       ajaxData,
341								dataType:   'json',
342								success: function(response/*, status, xhr*/) {
343									if ( 'approved' === response.comment_status ) {
344										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_approved + '</span>').slideDown('fast');
345									} else if ( 'unapproved' === response.comment_status ) {
346										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-success">' + jetpackCarouselStrings.comment_unapproved + '</span>').slideDown('fast');
347									} else {
348										// 'deleted', 'spam', false
349										$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">' + jetpackCarouselStrings.comment_post_error + '</span>').slideDown('fast');
350									}
351									gallery.jp_carousel('clearCommentTextAreaValue');
352									gallery.jp_carousel('getComments', {attachment_id: attachment_id, offset: 0, clear: true});
353									$('#jp-carousel-comment-form-button-submit').val(jetpackCarouselStrings.post_comment);
354									$('#jp-carousel-comment-form-spinner').spin(false);
355								},
356								error: function(/*xhr, status, error*/) {
357									// TODO: Add error handling and display here
358									gallery.jp_carousel('postCommentError', {'field': 'jp-carousel-comment-form-comment-field', 'error': jetpackCarouselStrings.comment_post_error});
359									return;
360								}
361							});
362						}
363					} else if ( ! target.parents( '.jp-carousel-info' ).length ) {
364						container.jp_carousel('next');
365					}
366				})
367				.bind('jp_carousel.afterOpen', function(){
368					$(window).bind('keydown', keyListener);
369					$(window).bind('resize', resizeListener);
370					gallery.opened = true;
371
372					resizeListener();
373				})
374				.bind('jp_carousel.beforeClose', function(){
375					var scroll = $(window).scrollTop();
376
377					$(window).unbind('keydown', keyListener);
378					$(window).unbind('resize', resizeListener);
379					$(window).scrollTop(scroll);
380				})
381				.bind('jp_carousel.afterClose', function(){
382					if ( history.pushState ) {
383						history.pushState('', document.title, window.location.pathname + window.location.search);
384					} else {
385						last_known_location_hash = '';
386						window.location.hash = '';
387					}
388					gallery.opened = false;
389				})
390				.on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function ( e ) {
391					// If the movement transitions take more than twice the allotted time, disable them.
392					// There is some wiggle room in the 2x, since some of that time is taken up in
393					// JavaScript, setting up the transition and calling the events.
394					if ( 'transform' === e.originalEvent.propertyName ) {
395						var transitionMultiplier = ( ( Date.now() - transitionBegin ) / 1000 ) / e.originalEvent.elapsedTime;
396
397						container.off( 'transitionend.jp-carousel' );
398
399						if ( transitionMultiplier >= 2 ) {
400							$( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' );
401						}
402					}
403				} );
404
405				$( '.jp-carousel-wrap' ).touchwipe( {
406					wipeLeft : function ( e ) {
407						e.preventDefault();
408						gallery.jp_carousel( 'next' );
409					},
410					wipeRight : function ( e ) {
411						e.preventDefault();
412						gallery.jp_carousel( 'previous' );
413					},
414					preventDefaultEvents : false
415				} );
416
417			$( '.jetpack-likes-widget-unloaded' ).each( function() {
418				jetpackLikesWidgetQueue.push( this.id );
419				});
420
421			nextButton.add(previousButton).click(function(e){
422				e.preventDefault();
423				e.stopPropagation();
424				if ( nextButton.is(this) ) {
425					gallery.jp_carousel('next');
426				} else {
427					gallery.jp_carousel('previous');
428				}
429			});
430		}
431	};
432
433	var methods = {
434		testForData: function(gallery) {
435			gallery = $( gallery ); // make sure we have it as a jQuery object.
436			return !( ! gallery.length || ! gallery.data( 'carousel-extra' ) );
437		},
438
439		testIfOpened: function() {
440			return !!( 'undefined' !== typeof(gallery) && 'undefined' !== typeof(gallery.opened) && gallery.opened );
441		},
442
443		openOrSelectSlide: function( index ) {
444			// The `open` method triggers an asynchronous effect, so we will get an
445			// error if we try to use `open` then `selectSlideAtIndex` immediately
446			// after it. We can only use `selectSlideAtIndex` if the carousel is
447			// already open.
448			if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) {
449				// The `open` method selects the correct slide during the
450				// initialization.
451				$( this ).jp_carousel( 'open', { start_index: index } );
452			} else {
453				gallery.jp_carousel( 'selectSlideAtIndex', index );
454			}
455		},
456
457		open: function(options) {
458			var settings = {
459				'items_selector' : '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id]',
460				'start_index': 0
461			},
462			data = $(this).data('carousel-extra');
463
464			if ( !data ) {
465				return; // don't run if the default gallery functions weren't used
466			}
467
468			prepareGallery( data );
469
470			if ( gallery.jp_carousel( 'testIfOpened' ) ) {
471				return; // don't open if already opened
472			}
473
474			// make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger
475			// infiniscroll for it when enabled (Reader, theme infiniscroll, etc).
476			originalOverflow = $('body').css('overflow');
477			$('body').css('overflow', 'hidden');
478			// prevent html from overflowing on some of the new themes.
479			originalHOverflow = $('html').css('overflow');
480			$('html').css('overflow', 'hidden');
481
482			// Re-apply inline-block style here and give an initial value for the width
483			// This value will get replaced with a more appropriate value once the slide is loaded
484			// This avoids the likes widget appearing initially full width below the comment button and then shuffling up
485			jQuery( '.slim-likes-widget' ).find( 'iframe' ).css( 'display', 'inline-block' ).css( 'width', '60px' );
486
487			container.data('carousel-extra', data);
488
489			return this.each(function() {
490				// If options exist, lets merge them
491				// with our default settings
492				var $this = $(this);
493
494				if ( options ) {
495					$.extend( settings, options );
496				}
497				if ( -1 === settings.start_index ) {
498					settings.start_index = 0; //-1 returned if can't find index, so start from beginning
499				}
500
501				container.trigger('jp_carousel.beforeOpen').fadeIn('fast',function(){
502					container.trigger('jp_carousel.afterOpen');
503					gallery
504						.jp_carousel('initSlides', $this.find(settings.items_selector), settings.start_index)
505						.jp_carousel('selectSlideAtIndex', settings.start_index);
506				});
507				gallery.html('');
508			});
509		},
510
511		selectSlideAtIndex : function(index){
512			var slides = this.jp_carousel('slides'), selected = slides.eq(index);
513
514			if ( 0 === selected.length ) {
515				selected = slides.eq(0);
516			}
517
518			gallery.jp_carousel('selectSlide', selected, false);
519			return this;
520		},
521
522		close : function(){
523			// make sure to let the page scroll again
524			$('body').css('overflow', originalOverflow);
525			$('html').css('overflow', originalHOverflow);
526			return container
527				.trigger('jp_carousel.beforeClose')
528				.fadeOut('fast', function(){
529					container.trigger('jp_carousel.afterClose');
530				});
531
532		},
533
534		next : function(){
535			var slide = gallery.jp_carousel( 'nextSlide' );
536			container.animate({scrollTop:0}, 'fast');
537
538			if ( slide ) {
539				this.jp_carousel('selectSlide', slide);
540			}
541		},
542
543		previous : function(){
544			var slide = gallery.jp_carousel( 'prevSlide' );
545			container.animate({scrollTop:0}, 'fast');
546
547			if ( slide ) {
548				this.jp_carousel('selectSlide', slide);
549			}
550		},
551
552		resetButtons : function(current) {
553			if ( current.data('liked') ) {
554				$('.jp-carousel-buttons a.jp-carousel-like').addClass('liked').text(jetpackCarouselStrings.unlike);
555			} else {
556				$('.jp-carousel-buttons a.jp-carousel-like').removeClass('liked').text(jetpackCarouselStrings.like);
557			}
558		},
559
560		selectedSlide : function(){
561			return this.find('.selected');
562		},
563
564		setSlidePosition : function(x) {
565			transitionBegin = Date.now();
566
567			return this.css({
568					'-webkit-transform':'translate3d(' + x + 'px,0,0)',
569					'-moz-transform':'translate3d(' + x + 'px,0,0)',
570					'-ms-transform':'translate(' + x + 'px,0)',
571					'-o-transform':'translate(' + x + 'px,0)',
572					'transform':'translate3d(' + x + 'px,0,0)'
573			});
574		},
575
576		updateSlidePositions : function(animate) {
577			var current = this.jp_carousel( 'selectedSlide' ),
578				galleryWidth = gallery.width(),
579				currentWidth = current.width(),
580				previous = gallery.jp_carousel( 'prevSlide' ),
581				next = gallery.jp_carousel( 'nextSlide' ),
582				previousPrevious = previous.prev(),
583				nextNext = next.next(),
584				left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 );
585
586			current.jp_carousel( 'setSlidePosition', left ).show();
587
588			// minimum width
589			gallery.jp_carousel( 'fitInfo', animate );
590
591			// prep the slides
592			var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1;
593
594			// Since we preload the `previousPrevious` and `nextNext` slides, we need
595			// to make sure they technically visible in the DOM, but invisible to the
596			// user. To hide them from the user, we position them outside the edges
597			// of the window.
598			//
599			// This section of code only applies when there are more than three
600			// slides. Otherwise, the `previousPrevious` and `nextNext` slides will
601			// overlap with the `previous` and `next` slides which must be visible
602			// regardless.
603			if ( 1 === direction ) {
604				if ( ! nextNext.is( previous ) ) {
605					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show();
606				}
607
608				if ( ! previousPrevious.is( next ) ) {
609					previousPrevious.jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth ).show();
610				}
611			} else {
612				if ( ! nextNext.is( previous ) ) {
613					nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show();
614				}
615			}
616
617			previous.jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + ( screenPadding * 0.75 ) ) ).show();
618			next.jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - ( screenPadding * 0.75 ) ) ).show();
619		},
620
621		selectSlide : function(slide, animate){
622			lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' );
623
624			var slides = gallery.jp_carousel( 'slides' ).css({ 'position': 'fixed' }),
625				current = $( slide ).addClass( 'selected' ).css({ 'position': 'relative' }),
626				attachmentId = current.data( 'attachment-id' ),
627				previous = gallery.jp_carousel( 'prevSlide' ),
628				next = gallery.jp_carousel( 'nextSlide' ),
629				previousPrevious = previous.prev(),
630				nextNext = next.next(),
631				animated,
632				captionHtml;
633
634			// center the main image
635			gallery.jp_carousel( 'loadFullImage', current );
636
637			caption.hide();
638
639			if ( next.length === 0 && slides.length <= 2 ) {
640				$( '.jp-carousel-next-button' ).hide();
641			} else {
642				$( '.jp-carousel-next-button' ).show();
643			}
644
645			if ( previous.length === 0 && slides.length <= 2 ) {
646				$( '.jp-carousel-previous-button' ).hide();
647			} else {
648				$( '.jp-carousel-previous-button' ).show();
649			}
650
651			animated = current
652				.add( previous )
653				.add( previousPrevious )
654				.add( next )
655				.add( nextNext )
656				.jp_carousel( 'loadSlide' );
657
658			// slide the whole view to the x we want
659			slides.not( animated ).hide();
660
661			gallery.jp_carousel( 'updateSlidePositions', animate );
662
663			gallery.jp_carousel( 'resetButtons', current );
664			container.trigger( 'jp_carousel.selectSlide', [current] );
665
666			gallery.jp_carousel( 'getTitleDesc', {
667				title: current.data( 'title' ),
668				desc: current.data( 'desc' )
669			});
670
671			// Lazy-load the Likes iframe for the current, next, and previous slides.
672			gallery.jp_carousel( 'loadLikes', attachmentId );
673			gallery.jp_carousel( 'updateLikesWidgetVisibility', attachmentId );
674
675			if ( next.length > 0 ) {
676				gallery.jp_carousel( 'loadLikes', next.data( 'attachment-id' ) );
677			}
678
679			if ( previous.length > 0 ) {
680				gallery.jp_carousel( 'loadLikes', previous.data( 'attachment-id' ) );
681			}
682
683			var imageMeta = current.data( 'image-meta' );
684			gallery.jp_carousel( 'updateExif', imageMeta );
685			gallery.jp_carousel( 'updateFullSizeLink', current );
686			gallery.jp_carousel( 'updateMap', imageMeta );
687			gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) );
688			gallery.jp_carousel( 'getComments', {
689				'attachment_id': attachmentId,
690				'offset': 0,
691				'clear': true
692			});
693			$( '#jp-carousel-comment-post-results' ).slideUp();
694
695			// $('<div />').text(sometext).html() is a trick to go to HTML to plain
696			// text (including HTML entities decode, etc)
697			if ( current.data( 'caption' ) ) {
698				captionHtml = $( '<div />' ).text( current.data( 'caption' ) ).html();
699
700				if ( captionHtml === $( '<div />' ).text( current.data( 'title' ) ).html() ) {
701					$( '.jp-carousel-titleanddesc-title' ).fadeOut( 'fast' ).empty();
702				}
703
704				if ( captionHtml === $( '<div />' ).text( current.data( 'desc' ) ).html() ) {
705					$( '.jp-carousel-titleanddesc-desc' ).fadeOut( 'fast' ).empty();
706				}
707
708				caption.html( current.data( 'caption' ) ).fadeIn( 'slow' );
709			} else {
710				caption.fadeOut( 'fast' ).empty();
711			}
712
713
714			// Load the images for the next and previous slides.
715			$( next ).add( previous ).each( function() {
716				gallery.jp_carousel( 'loadFullImage', $( this ) );
717			});
718
719			window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId;
720		},
721
722		slides : function(){
723			return this.find('.jp-carousel-slide');
724		},
725
726		slideDimensions : function(){
727			return {
728				width: $(window).width() - (screenPadding * 2),
729				height: Math.floor( $(window).height() / 100 * proportion - 60 )
730			};
731		},
732
733		loadSlide : function() {
734			return this.each(function(){
735				var slide = $(this);
736				slide.find('img')
737					.one('load', function(){
738						// set the width/height of the image if it's too big
739						slide
740							.jp_carousel('fitSlide',false);
741					});
742			});
743		},
744
745		bestFit : function(){
746			var max        = gallery.jp_carousel('slideDimensions'),
747			    orig       = this.jp_carousel('originalDimensions'),
748			    orig_ratio = orig.width / orig.height,
749			    w_ratio    = 1,
750			    h_ratio    = 1,
751			    width, height;
752
753			if ( orig.width > max.width ) {
754				w_ratio = max.width / orig.width;
755			}
756			if ( orig.height > max.height ) {
757				h_ratio = max.height / orig.height;
758			}
759
760			if ( w_ratio < h_ratio ) {
761				width = max.width;
762				height = Math.floor( width / orig_ratio );
763			} else if ( h_ratio < w_ratio ) {
764				height = max.height;
765				width = Math.floor( height * orig_ratio );
766			} else {
767				width = orig.width;
768				height = orig.height;
769			}
770
771			return {
772				width: width,
773				height: height
774			};
775		},
776
777		fitInfo : function(/*animated*/){
778			var current = this.jp_carousel('selectedSlide'),
779				size = current.jp_carousel('bestFit');
780
781			photo_info.css({
782				'left'  : Math.floor( (info.width() - size.width) * 0.5 ),
783				'width' : Math.floor( size.width )
784			});
785
786			return this;
787		},
788
789		fitMeta : function(animated){
790			var newInfoTop   = { top: Math.floor( $(window).height() / 100 * proportion + 5 ) + 'px' };
791			var newLeftWidth = { width: ( info.width() - (imageMeta.width() + 80) ) + 'px' };
792
793			if (animated) {
794				info.animate(newInfoTop);
795				leftColWrapper.animate(newLeftWidth);
796			} else {
797				info.animate(newInfoTop);
798				leftColWrapper.css(newLeftWidth);
799			}
800		},
801
802		fitSlide : function(/*animated*/){
803			return this.each(function(){
804				var $this      = $(this),
805				    dimensions = $this.jp_carousel('bestFit'),
806				    method     = 'css',
807				    max        = gallery.jp_carousel('slideDimensions');
808
809				dimensions.left = 0;
810				dimensions.top = Math.floor( (max.height - dimensions.height) * 0.5 ) + 40;
811				$this[method](dimensions);
812			});
813		},
814
815		texturize : function(text) {
816				text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace().
817				text = text.replace(/'/g, '&#8217;').replace(/&#039;/g, '&#8217;').replace(/[\u2019]/g, '&#8217;');
818				text = text.replace(/"/g, '&#8221;').replace(/&#034;/g, '&#8221;').replace(/&quot;/g, '&#8221;').replace(/[\u201D]/g, '&#8221;');
819				text = text.replace(/([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"'); // untexturize allowed HTML tags params double-quotes
820				return $.trim(text);
821		},
822
823		initSlides : function(items, start_index){
824			if ( items.length < 2 ) {
825				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide();
826			} else {
827				$( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show();
828			}
829
830			// Calculate the new src.
831			items.each(function(/*i*/){
832				var src_item  = $(this),
833					orig_size = src_item.data('orig-size') || '',
834					max       = gallery.jp_carousel('slideDimensions'),
835					parts     = orig_size.split(','),
836					medium_file     = src_item.data('medium-file') || '',
837					large_file      = src_item.data('large-file') || '',
838					src;
839				orig_size = {width: parseInt(parts[0], 10), height: parseInt(parts[1], 10)};
840
841					src = src_item.data('orig-file');
842
843					src = gallery.jp_carousel('selectBestImageSize', {
844						orig_file   : src,
845						orig_width  : orig_size.width,
846						orig_height : orig_size.height,
847						max_width   : max.width,
848						max_height  : max.height,
849						medium_file : medium_file,
850						large_file  : large_file
851					});
852
853				// Set the final src
854				$(this).data( 'gallery-src', src );
855			});
856
857			// If the start_index is not 0 then preload the clicked image first.
858			if ( 0 !== start_index ) {
859				$('<img/>')[0].src = $(items[start_index]).data('gallery-src');
860			}
861
862			var useInPageThumbnails = items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0;
863
864			// create the 'slide'
865			items.each(function(i){
866				var src_item        = $(this),
867					attachment_id   = src_item.data('attachment-id') || 0,
868					comments_opened = src_item.data('comments-opened') || 0,
869					image_meta      = src_item.data('image-meta') || {},
870					orig_size       = src_item.data('orig-size') || '',
871					thumb_size      = { width : src_item[0].naturalWidth, height : src_item[0].naturalHeight },
872					title           = src_item.data('image-title') || '',
873					description     = src_item.data('image-description') || '',
874					caption         = src_item.parents('dl').find('dd.gallery-caption').html() || '',
875					src		= src_item.data('gallery-src') || '',
876					medium_file     = src_item.data('medium-file') || '',
877					large_file      = src_item.data('large-file') || '',
878					orig_file	= src_item.data('orig-file') || '';
879
880				var tiledCaption = src_item.parents('div.tiled-gallery-item').find('div.tiled-gallery-caption').html();
881				if ( tiledCaption ) {
882					caption = tiledCaption;
883				}
884
885				if ( attachment_id && orig_size.length ) {
886					title       = gallery.jp_carousel('texturize', title);
887					description = gallery.jp_carousel('texturize', description);
888					caption     = gallery.jp_carousel('texturize', caption);
889
890					// Initially, the image is a 1x1 transparent gif.  The preview is shown as a background image on the slide itself.
891					var image = $( '<img/>' )
892						.attr( 'src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' )
893						.css( 'width', '100%' )
894						.css( 'height', '100%' );
895
896					var slide = $('<div class="jp-carousel-slide"></div>')
897							.hide()
898							.css({
899								//'position' : 'fixed',
900								'left'     : i < start_index ? -1000 : gallery.width()
901							})
902							.append( image )
903							.appendTo(gallery)
904							.data('src', src )
905							.data('title', title)
906							.data('desc', description)
907							.data('caption', caption)
908							.data('attachment-id', attachment_id)
909							.data('permalink', src_item.parents('a').attr('href'))
910							.data('orig-size', orig_size)
911							.data('comments-opened', comments_opened)
912							.data('image-meta', image_meta)
913							.data('medium-file', medium_file)
914							.data('large-file', large_file)
915							.data('orig-file', orig_file)
916							.data('thumb-size', thumb_size)
917							;
918
919						if ( useInPageThumbnails ) {
920							// Use the image already loaded in the gallery as a preview.
921							slide
922								.data( 'preview-image', src_item.attr( 'src' ) )
923								.css( {
924									'background-image' : 'url("' + src_item.attr( 'src' ) + '")',
925									'background-size' : '100% 100%',
926									'background-position' : 'center center'
927								} );
928						}
929
930						slide.jp_carousel( 'fitSlide', false );
931				}
932			});
933			return this;
934		},
935
936		selectBestImageSize: function(args) {
937			if ( 'object' !== typeof args ) {
938				args = {};
939			}
940
941			if ( 'undefined' === typeof args.orig_file ) {
942				return '';
943			}
944
945			if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) {
946				return args.orig_file;
947			}
948
949			if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) {
950				return args.orig_file;
951			}
952
953			var medium_size       = args.medium_file.replace(/-([\d]+x[\d]+)\..+$/, '$1'),
954				medium_size_parts = (medium_size !== args.medium_file) ? medium_size.split('x') : [args.orig_width, 0],
955				medium_width      = parseInt( medium_size_parts[0], 10 ),
956				medium_height     = parseInt( medium_size_parts[1], 10 ),
957				large_size        = args.large_file.replace(/-([\d]+x[\d]+)\..+$/, '$1'),
958				large_size_parts  = (large_size !== args.large_file) ? large_size.split('x') : [args.orig_width, 0],
959				large_width       = parseInt( large_size_parts[0], 10 ),
960				large_height      = parseInt( large_size_parts[1], 10 );
961
962			// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
963			if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) {
964				args.max_width  = args.max_width * window.devicePixelRatio;
965				args.max_height = args.max_height * window.devicePixelRatio;
966			}
967
968			if ( large_width >= args.max_width || large_height >= args.max_height ) {
969				return args.large_file;
970			}
971
972			if ( medium_width >= args.max_width || medium_height >= args.max_height ) {
973				return args.medium_file;
974			}
975
976			return args.orig_file;
977		},
978
979		originalDimensions: function() {
980			var splitted = $(this).data('orig-size').split(',');
981			return {width: parseInt(splitted[0], 10), height: parseInt(splitted[1], 10)};
982		},
983
984		format: function( args ) {
985			if ( 'object' !== typeof args ) {
986				args = {};
987			}
988			if ( ! args.text || 'undefined' === typeof args.text ) {
989				return;
990			}
991			if ( ! args.replacements || 'undefined' === typeof args.replacements ) {
992				return args.text;
993			}
994			return args.text.replace(/{(\d+)}/g, function( match, number ) {
995				return typeof args.replacements[number] !== 'undefined' ? args.replacements[number] : match;
996			});
997		},
998
999		shutterSpeed: function(d) {
1000			if (d >= 1) {
1001				return Math.round(d*10)/10 + 's'; // round to one decimal if value >
1001 1s by multiplying it by 10, rounding, then dividing by 10 again
1002			}
1003			var df = 1, top = 1, bot = 1;
1004			var tol = 1e-8;
1005			// iterate while value not reached and difference (positive or negative, hence the Math.abs) between value 
1006			// and approximated value greater than given tolerance
1007			while (df !== d && Math.abs(df-d) > tol) {
1008				if (df < d) {
1009					top += 1;
1010				} else {
1011					bot += 1;
1012					top = parseInt(d * bot, 10);
1013				}
1014				df = top / bot;
1015			}
1016			if (top > 1) {
1017				bot = Math.round(bot / top);
1018				top = 1;
1019			}
1020			if (bot <= 1) {
1021				return '1s';
1022			}
1023			return top + '/' + bot + 's';
1024		},
1025
1026		parseTitleDesc: function( value ) {
1027			if ( !value.match(' ') && value.match('_') ) {
1028				return '';
1029			}
1030			// Prefix list originally based on http://commons.wikimedia.org/wiki/MediaWiki:Filename-prefix-blacklist
1031			$([
1032				'CIMG',                   // Casio
1033				'DSC_',                   // Nikon
1034				'DSCF',                   // Fuji
1035				'DSCN',                   // Nikon
1036				'DUW',                    // some mobile phones
1037				'GEDC',                   // GE
1038				'IMG',                    // generic
1039				'JD',                     // Jenoptik
1040				'MGP',                    // Pentax
1041				'PICT',                   // misc.
1042				'Imagen',                 // misc.
1043				'Foto',                   // misc.
1044				'DSC',                    // misc.
1045				'Scan',                   // Scanners
1046				'SANY',                   // Sanyo
1047				'SAM',                    // Samsung
1048				'Screen Shot [0-9]+'      // Mac screenshots
1049			])
1050			.each(function(key, val){
1051				var regex = new RegExp('^' + val);
1052				if ( regex.test(value) ) {
1053					value = '';
1054					return;
1055				}
1056			});
1057			return value;
1058		},
1059
1060		getTitleDesc: function( data ) {
1061			var title ='', desc = '', markup = '', target;
1062
1063			target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' );
1064			target.hide();
1065
1066			title = gallery.jp_carousel('parseTitleDesc', data.title) || '';
1067			desc  = gallery.jp_carousel('parseTitleDesc', data.desc)  || '';
1068
1069			if ( title.length || desc.length ) {
1070				// $('<div />').text(sometext).html() is a trick to go to HTML to plain text (including HTML entities decode, etc)
1071				if ( $('<div />').text(title).html() === $('<div />').text(desc).html() ) {
1072					title = '';
1073				}
1074
1075				markup  = ( title.length ) ? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>' : '';
1076				markup += ( desc.length )  ? '<div class="jp-carousel-titleanddesc-desc">' + desc + '</div>'   : '';
1077
1078				target.html( markup ).fadeIn('slow');
1079			}
1080
1081			$( 'div#jp-carousel-comment-form-container' ).css('margin-top', '20px');
1082			$( 'div#jp-carousel-comments-loading' ).css('margin-top', '20px');
1083		},
1084
1085		updateLikesWidgetVisibility: function ( attachmentId ) {
1086			// Only do this if likes is enabled
1087			if ( 'undefined' === typeof jetpackLikesWidgetQueue ) {
1088				return;
1089			}
1090
1091			// Hide all likes widgets except for the one for the attachmentId passed in
1092			$( '.jp-carousel-buttons .jetpack-likes-widget-wrapper' ).css( 'display', 'none' ).each( function () {
1093				var widgetWrapper = $( this );
1094				if ( widgetWrapper.attr( 'data-attachment-id' ) == attachmentId ) { // jshint ignore:line
1095					widgetWrapper.css( 'display', 'inline-block' );
1096					return false;
1097				}
1098			});
1099		},
1100
1101		loadLikes : function ( attachmentId ) {
1102			var dataCarouselExtra = $( '.jp-carousel-wrap' ).data( 'carousel-extra' );
1103			var blogId = dataCarouselExtra.likes_blog_id;
1104
1105			if ( $( '#like-post-wrapper-' + blogId + '-' + attachmentId ).length === 0 ) {
1106				// Add the iframe the first time the slide is shown.
1107				var protocol = 'http';
1108				var originDomain = 'http://wordpress.com';
1109
1110				if ( dataCarouselExtra.permalink.length ) {
1111					protocol = dataCarouselExtra.permalink.split( ':' )[0];
1112
1113					if ( ( protocol !== 'http' ) && ( protocol !== 'https' ) ) {
1114						protocol = 'http';
1115					}
1116
1117					var parts = dataCarouselExtra.permalink.split( '/' );
1118
1119					if ( parts.length >= 2 ) {
1120						originDomain = protocol + '://' + parts[2];
1121					}
1122				}
1123
1124				var dataSource = protocol + '://widgets.wp.com/likes/#blog_id=' + encodeURIComponent( blogId ) +
1125					'&post_id=' + encodeURIComponent( attachmentId ) +
1126					'&slim=1&origin=' + encodeURIComponent( originDomain );
1127
1128				if ( 'en' !== jetpackCarouselStrings.lang ) {
1129					dataSource += '&lang=' + encodeURIComponent( jetpackCarouselStrings.lang );
1130				}
1131
1132				var likesWidget = $( '<iframe class=\'post-likes-widget jetpack-likes-widget jetpack-resizeable\'></iframe>' )
1133					.attr( 'name', 'like-post-frame-' + blogId + '-' + attachmentId )
1134					.attr( 'src', dataSource )
1135					.css( 'display', 'inline-block' );
1136
1137				var likesWidgetWrapper = $( '<div/>' )
1138					.addClass( 'jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded slim-likes-widget' )
1139					.attr( 'id', 'like-post-wrapper-' + blogId + '-' + attachmentId )
1140					.attr( 'data-src', dataSource )
1141					.attr( 'data-name', 'like-post-frame-' + blogId + '-' + attachmentId )
1142					.attr( 'data-attachment-id', attachmentId )
1143					.css( 'display', 'none' )
1144					.css( 'vertical-align', 'middle' )
1145					.append( likesWidget )
1146					.append( '<div class=\'post-likes-widget-placeholder\'></div>' );
1147
1148				$( '.jp-carousel-buttons' ).append( likesWidgetWrapper );
1149			}
1150
1151		},
1152
1153		// updateExif updates the contents of the exif UL (.jp-carousel-image-exif)
1154		updateExif: function( meta ) {
1155			if ( !meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) {
1156				return false;
1157			}
1158
1159			var $ul = $( '<ul class=\'jp-carousel-image-exif\'></ul>' );
1160
1161			$.each( meta, function( key, val ) {
1162				if ( 0 === parseFloat(val) || !val.length || -1 === $.inArray( key, [ 'camera', 'aperture', 'shutter_speed', 'focal_length' ] ) ) {
1163					return;
1164				}
1165
1166				switch( key ) {
1167					case 'focal_length':
1168						val = val + 'mm';
1169						break;
1170					case 'shutter_speed':
1171						val = gallery.jp_carousel('shutterSpeed', val);
1172						break;
1173					case 'aperture':
1174						val = 'f/' + val;
1175						break;
1176				}
1177
1178				$ul.append( '<li><h5>' + jetpackCarouselStrings[key] + '</h5>' + val + '</li>' );
1179			});
1180
1181			// Update (replace) the content of the ul
1182			$( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul );
1183		},
1184
1185		// updateFullSizeLink updates the contents of the jp-carousel-image-download link
1186		updateFullSizeLink: function(current) {
1187			if(!current || !current.data) {
1188				return false;
1189			}
1190			var original  = current.data('orig-file').replace(/\?.+$/, ''),
1191				origSize  = current.data('orig-size').split(','),
1192				permalink = $( '<a>'+gallery.jp_carousel('format', {'text': jetpackCarouselStrings.download_original, 'replacements': origSize})+'</a>' )
1193					.addClass( 'jp-carousel-image-download' )
1194					.attr( 'href', original )
1195					.attr( 'target', '_blank' );
1196
1197			// Update (replace) the content of the anchor
1198			$( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink );
1199		},
1200
1201		updateMap: function( meta ) {
1202			if ( !meta.latitude || !meta.longitude || 1 !== Number( jetpackCarouselStrings.display_geo ) ) {
1203				return;
1204			}
1205
1206			var latitude  = meta.latitude,
1207				longitude = meta.longitude,
1208				$metabox  = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ),
1209				$mapbox   = $( '<div></div>' ),
1210				style     = '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91';
1211
1212			$mapbox
1213				.addClass( 'jp-carousel-image-map' )
1214				.html( '<img width="154" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
1215							center=' + latitude + ',' + longitude + '&\
1216							zoom=8&\
1217							size=154x154&\
1218							sensor=false&\
1219							markers=size:medium%7Ccolor:blue%7C' + latitude + ',' + longitude + style +'" class="gmap-main" />\
1220							\
1221						<div class="gmap-topright"><div class="imgclip"><img width="175" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\
1222							center=' + latitude + ',' + longitude + '&\
1223							zoom=3&\
1224							size=175x154&\
1225							sensor=false&\
1226							markers=size:small%7Ccolor:blue%7C' + latitude + ',' + longitude + style + '"c /></div></div>\
1227							\
1228						' )
1229				.prependTo( $metabox );
1230		},
1231
1232		testCommentsOpened: function( opened ) {
1233			if ( 1 === parseInt( opened, 10 ) ) {
1234					$('.jp-carousel-buttons').fadeIn('fast');
1235				commentForm.fadeIn('fast');
1236			} else {
1237					$('.jp-carousel-buttons').fadeOut('fast');
1238				commentForm.fadeOut('fast');
1239			}
1240		},
1241
1242		getComments: function( args ) {
1243			clearInterval( commentInterval );
1244
1245			if ( 'object' !== typeof args ) {
1246				return;
1247			}
1248
1249			if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) {
1250				return;
1251			}
1252
1253			if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) {
1254				args.offset = 0;
1255			}
1256
1257			var comments        = $('.jp-carousel-comments'),
1258				commentsLoading = $('#jp-carousel-comments-loading').show();
1259
1260			if ( args.clear ) {
1261				comments.hide().empty();
1262			}
1263
1264			$.ajax({
1265				type:       'GET',
1266				url:        jetpackCarouselStrings.ajaxurl,
1267				dataType:   'json',
1268				data: {
1269					action: 'get_attachment_comments',
1270					nonce:  jetpackCarouselStrings.nonce,
1271					id:     args.attachment_id,
1272					offset: args.offset
1273				},
1274				success: function(data/*, status, xhr*/) {
1275					if ( args.clear ) {
1276						comments.fadeOut('fast').empty();
1277					}
1278
1279					$( data ).each(function(){
1280						var comment = $('<div></div>')
1281							.addClass('jp-carousel-comment')
1282							.attr('id', 'jp-carousel-comment-' + this['id'])
1283							.html(
1284								  '<div class="comment-gravatar">' +
1285								    this['gravatar_markup'] +
1286								  '</div>' +
1287								  '<div class="comment-author">' +
1288								    this['author_markup'] +
1289								  '</div>' +
1290								  '<div class="comment-date">' +
1291								    this['date_gmt'] +
1292								  '</div>' +
1293								  '<div class="comment-content">' +
1294								    this['content'] +
1295								  '</div>'
1296							);
1297						comments.append(comment);
1298
1299						// Set the interval to check for a new page of comments.
1300						clearInterval( commentInterval );
1301						commentInterval = setInterval( function() {
1302							if ( ( $('.jp-carousel-overlay').height() - 150 ) < $('.jp-carousel-wrap').scrollTop() + $(window).height() ) {
1303								gallery.jp_carousel('getComments',{ attachment_id: args.attachment_id, offset: args.offset + 10, clear: false });
1304								clearInterval( commentInterval );
1305							}
1306						}, 300 );
1307					});
1308
1309					// Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested
1310					// attachment id might no longer match the current attachment id by the time we get the data back or a now
1311					// registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake.
1312					var current = $('.jp-carousel div.selected');
1313					if ( current && current.data && current.data('attachment-id') != args.attachment_id ) { // jshint ignore:line
1314						comments.fadeOut('fast');
1315						comments.empty();
1316						return;
1317					}
1318
1319					// Increase the height of the background, semi-transparent overlay to match the new length of the comments list.
1320					$('.jp-carousel-overlay').height( $(window).height() + titleAndDescription.height() + commentForm.height() + ( (comments.height() > 0) ? comments.height() : imageMeta.height() ) + 200 );
1321
1322					comments.show();
1323					commentsLoading.hide();
1324				},
1325				error: function(xhr, status, error) {
1326					// TODO: proper error handling
1327					console.log( 'Comment get fail...', xhr, status, error );
1328					comments.fadeIn('fast');
1329					commentsLoading.fadeOut('fast');
1330				}
1331			});
1332		},
1333
1334		postCommentError: function(args) {
1335			if ( 'object' !== typeof args ) {
1336				args = {};
1337			}
1338			if ( ! args.field || 'undefined' === typeof args.field ||  ! args.error || 'undefined' === typeof args.error ) {
1339				return;
1340			}
1341			$('#jp-carousel-comment-post-results').slideUp('fast').html('<span class="jp-carousel-comment-post-error">'+args.error+'</span>').slideDown('fast');
1342			$('#jp-carousel-comment-form-spinner').spin(false);
1343		},
1344
1345		setCommentIframeSrc: function(attachment_id) {
1346			var iframe = $('#jp-carousel-comment-iframe');
1347			// Set the proper irame src for the current attachment id
1348			if (iframe && iframe.length) {
1349				iframe.attr('src', iframe.attr('src').replace(/(postid=)\d+/, '$1'+attachment_id) );
1350				iframe.attr('src', iframe.attr('src').replace(/(%23.+)?$/, '%23jp-carousel-'+attachment_id) );
1351			}
1352		},
1353
1354		clearCommentTextAreaValue: function() {
1355			var commentTextArea = $('#jp-carousel-comment-form-comment-field');
1356			if ( commentTextArea ) {
1357				commentTextArea.val('');
1358			}
1359		},
1360
1361		nextSlide : function () {
1362			var slides = this.jp_carousel( 'slides' );
1363			var selected = this.jp_carousel( 'selectedSlide' );
1364
1365			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) {
1366				return slides.first();
1367			}
1368
1369			return selected.next();
1370		},
1371
1372		prevSlide : function () {
1373			var slides = this.jp_carousel( 'slides' );
1374			var selected = this.jp_carousel( 'selectedSlide' );
1375
1376			if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) {
1377				return slides.last();
1378			}
1379
1380			return selected.prev();
1381		},
1382
1383		loadFullImage : function ( slide ) {
1384			var image = slide.find( 'img:first' );
1385
1386			if ( ! image.data( 'loaded' ) ) {
1387				// If the width of the slide is smaller than the width of the "thumbnail" we're already using,
1388				// don't load the full image.
1389
1390				image.on( 'load.jetpack', function () {
1391					image.off( 'load.jetpack' );
1392					$( this ).closest( '.jp-carousel-slide' ).css( 'background-image', '' );
1393				} );
1394
1395				if ( ! slide.data( 'preview-image' ) || ( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width ) ) {
1396					image.attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) );
1397				} else {
1398					image.attr( 'src', slide.data( 'preview-image' ) );
1399				}
1400
1401				image.data( 'loaded', 1 );
1402			}
1403		}
1404	};
1405
1406	$.fn.jp_carousel = function(method){
1407		// ask for the HTML of the gallery
1408		// Method calling logic
1409		if ( methods[method] ) {
1410			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
1411		} else if ( typeof method === 'object' || ! method ) {
1412			return methods.open.apply( this, arguments );
1413		} else {
1414			$.error( 'Method ' +	method + ' does not exist on jQuery.jp_carousel' );
1415		}
1416
1417	};
1418
1419	// register the event listener for starting the gallery
1420	$( document.body ).on( 'click', 'div.gallery,div.tiled-gallery', function(e) {
1421		if ( ! $(this).jp_carousel( 'testForData', e.currentTarget ) ) {
1422			return;
1423		}
1424		if ( $(e.target).parent().hasClass('gallery-caption') ) {
1425			return;
1426		}
1427		e.preventDefault();
1428		$(this).jp_carousel('open', {start_index: $(this).find('.gallery-item, .tiled-gallery-item').index($(e.target).parents('.gallery-item, .tiled-gallery-item'))});
1429	});
1430
1431	// Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger)
1432	$( window ).on( 'hashchange', function () {
1433		var hashRegExp = /jp-carousel-(\d+)/,
1434			matches, attachmentId, galleries, selectedThumbnail;
1435
1436		if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) {
1437			return;
1438		}
1439
1440		if ( window.location.hash === last_known_location_hash ) {
1441			return;
1442		}
1443
1444		last_known_location_hash = window.location.hash;
1445		matches = window.location.hash.match( hashRegExp );
1446		attachmentId = parseInt( matches[1], 10 );
1447		galleries = $( 'div.gallery, div.tiled-gallery' );
1448
1449		// Find the first thumbnail that matches the attachment ID in the location
1450		// hash, then open the gallery that contains it.
1451		galleries.each( function( _, galleryEl ) {
1452			$( galleryEl ).find('img').each( function( imageIndex, imageEl ) {
1453				if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) {
1454					selectedThumbnail = { index: imageIndex, gallery: galleryEl };
1455					return false;
1456				}
1457			});
1458
1459			if ( selectedThumbnail ) {
1460				$( selectedThumbnail.gallery )
1461					.jp_carousel( 'openOrSelectSlide', selectedThumbnail.index );
1462			}
1463		});
1464	});
1465
1466	if ( window.location.hash ) {
1467		$( window ).trigger( 'hashchange' );
1468	}
1469});
1470
1471/**
1472 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
1473 * Common usage: wipe images (left and right to show the previous or next image)
1474 *
1475 * @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
1476 * Version 1.1.1, modified to pass the touchmove event to the callbacks.
1477 */
1478(function($) {
1479$.fn.touchwipe = function(settings) {
1480	var config = {
1481			min_move_x: 20,
1482			min_move_y: 20,
1483			wipeLeft: function(/*e*/) { },
1484			wipeRight: function(/*e*/) { },
1485			wipeUp: function(/*e*/) { },
1486			wipeDown: function(/*e*/) { },
1487			preventDefaultEvents: true
1488	};
1489
1490	if (settings) {
1491		$.extend(config, settings);
1492	}
1493
1494	this.each(function() {
1495		var startX;
1496		var startY;
1497		var isMoving = false;
1498
1499		function cancelTouch() {
1500			this.removeEventListener('touchmove', onTouchMove);
1501			startX = null;
1502			isMoving = false;
1503		}
1504
1505		function onTouchMove(e) {
1506			if(config.preventDefaultEvents) {
1507				e.preventDefault();
1508			}
1509			if(isMoving) {
1510				var x = e.touches[0].pageX;
1511				var y = e.touches[0].pageY;
1512				var dx = startX - x;
1513				var dy = startY - y;
1514				if(Math.abs(dx) >= config.min_move_x) {
1515					cancelTouch();
1516					if(dx > 0) {
1517						config.wipeLeft(e);
1518					} else {
1519						config.wipeRight(e);
1520					}
1521				}
1522				else if(Math.abs(dy) >= config.min_move_y) {
1523						cancelTouch();
1524						if(dy > 0) {
1525							config.wipeDown(e);
1526						} else {
1527							config.wipeUp(e);
1528						}
1529					}
1530			}
1531		}
1532
1533		function onTouchStart(e)
1534		{
1535			if (e.touches.length === 1) {
1536				startX = e.touches[0].pageX;
1537				startY = e.touches[0].pageY;
1538				isMoving = true;
1539				this.addEventListener('touchmove', onTouchMove, false);
1540			}
1541		}
1542		if ('ontouchstart' in document.documentElement) {
1543			this.addEventListener('touchstart', onTouchStart, false);
1544		}
1545	});
1546
1547	return this;
1548};
1549})(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.