/* jshint sub: true, onevar: false, multistr: true, devel: true, smarttabs: true */ /* global jetpackCarouselStrings, DocumentTouch */ jQuery( document ).ready( function( $ ) { // gallery faded layer and container elements var overlay, comments, gallery, container, nextButton, previousButton, info, transitionBegin, caption, resizeTimeout, photo_info, close_hint, commentInterval, lastSelectedSlide, screenPadding = 110, originalOverflow = $( 'body' ).css( 'overflow' ), originalHOverflow = $( 'html' ).css( 'overflow' ), proportion = 85, last_known_location_hash = '', imageMeta, titleAndDescription, commentForm, leftColWrapper, scrollPos; if ( window.innerWidth <= 760 ) { screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 ); if ( screenPadding < 40 && ( 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch ) ) ) { screenPadding = 0; } } // Adding a polyfill for browsers that do not have Date.now if ( 'undefined' === typeof Date.now ) { Date.now = function now() { return new Date().getTime(); }; } var keyListener = function( e ) { switch ( e.which ) { case 38: // up e.preventDefault(); container.scrollTop( container.scrollTop() - 100 ); break; case 40: // down e.preventDefault(); container.scrollTop( container.scrollTop() + 100 ); break; case 39: // right e.preventDefault(); gallery.jp_carousel( 'next' ); break; case 37: // left case 8: // backspace e.preventDefault(); gallery.jp_carousel( 'previous' ); break; case 27: // escape e.preventDefault(); container.jp_carousel( 'close' ); break; default: // making jslint happy break; } }; var resizeListener = function(/*e*/) { clearTimeout( resizeTimeout ); resizeTimeout = setTimeout( function() { gallery.jp_carousel( 'slides' ).jp_carousel( 'fitSlide', true ); gallery.jp_carousel( 'updateSlidePositions', true ); gallery.jp_carousel( 'fitMeta', true ); }, 200 ); }; var prepareGallery = function(/*dataCarouselExtra*/) { if ( ! overlay ) { overlay = $( '
' ) .addClass( 'jp-carousel-overlay' ) .css( { position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, } ); var buttons = '' + jetpackCarouselStrings.comment + ''; if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) { } buttons = $( '' ); caption = $( '

' ); photo_info = $( '' ).append( caption ); imageMeta = $( '
' ) .addClass( 'jp-carousel-image-meta' ) .css( { float: 'right', 'margin-top': '20px', width: '250px', } ); imageMeta .append( buttons ) .append( "" ) .append( "" ) .append( "" ); titleAndDescription = $( '
' ) .addClass( 'jp-carousel-titleanddesc' ) .css( { width: '100%', 'margin-top': imageMeta.css( 'margin-top' ), } ); var commentFormMarkup = ''; commentForm = $( commentFormMarkup ).css( { width: '100%', 'margin-top': '20px', color: '#999', } ); comments = $( '
' ) .addClass( 'jp-carousel-comments' ) .css( { width: '100%', bottom: '10px', 'margin-top': '20px', } ); var commentsLoading = $( '' ).css( { width: '100%', bottom: '10px', 'margin-top': '20px', } ); var leftWidth = $( window ).width() - screenPadding * 2 - ( imageMeta.width() + 40 ); leftWidth += 'px'; leftColWrapper = $( '
' ) .addClass( 'jp-carousel-left-column-wrapper' ) .css( { width: Math.floor( leftWidth ), } ) .append( titleAndDescription ) .append( commentForm ) .append( comments ) .append( commentsLoading ); var fadeaway = $( '
' ).addClass( 'jp-carousel-fadeaway' ); info = $( '
' ) .addClass( 'jp-carousel-info' ) .css( { top: Math.floor( ( $( window ).height() / 100 ) * proportion ), left: screenPadding, right: screenPadding, } ) .append( photo_info ) .append( imageMeta ); if ( window.innerWidth <= 760 ) { photo_info.remove().insertAfter( titleAndDescription ); info.prepend( leftColWrapper ); } else { info.append( leftColWrapper ); } var targetBottomPos = $( window ).height() - parseInt( info.css( 'top' ), 10 ) + 'px'; nextButton = $( '
' ) .addClass( 'jp-carousel-next-button' ) .css( { right: '15px', } ) .hide(); previousButton = $( '
' ) .addClass( 'jp-carousel-previous-button' ) .css( { left: 0, } ) .hide(); nextButton.add( previousButton ).css( { position: 'fixed', top: '40px', bottom: targetBottomPos, width: screenPadding, } ); gallery = $( '
' ) .addClass( 'jp-carousel' ) .css( { position: 'absolute', top: 0, bottom: targetBottomPos, left: 0, right: 0, } ); close_hint = $( '' ).css( { position: 'fixed', } ); container = $( '
' ) .addClass( 'jp-carousel-wrap' ) .addClass( 'jp-carousel-transitions' ); if ( 'white' === jetpackCarouselStrings.background_color ) { container.addClass( 'jp-carousel-light' ); } container.attr( 'itemscope', '' ); container.attr( 'itemtype', 'https://schema.org/ImageGallery' ); container .css( { position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, 'z-index': 2147483647, 'overflow-x': 'hidden', 'overflow-y': 'auto', direction: 'ltr', } ) .hide() .append( overlay ) .append( gallery ) .append( fadeaway ) .append( info ) .append( nextButton ) .append( previousButton ) .append( close_hint ) .appendTo( $( 'body' ) ) .click( function( e ) { var target = $( e.target ), wrap = target.parents( 'div.jp-carousel-wrap' ), data = wrap.data( 'carousel-extra' ), slide = wrap.find( 'div.selected' ), attachment_id = slide.data( 'attachment-id' ); data = data || []; if ( target.is( gallery ) || target .parents() .add( target ) .is( close_hint ) ) { container.jp_carousel( 'close' ); } else if ( target.hasClass( 'jp-carousel-commentlink' ) ) { e.preventDefault(); e.stopPropagation(); $( window ).unbind( 'keydown', keyListener ); container.animate( { scrollTop: parseInt( info.position()[ 'top' ], 10 ) }, 'fast' ); $( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' ); $( '#jp-carousel-comment-form-comment-field' ).focus(); } else if ( target.hasClass( 'jp-carousel-comment-login' ) ) { var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id; window.location.href = url; } else if ( target.parents( '#jp-carousel-comment-form-container' ).length ) { var textarea = $( '#jp-carousel-comment-form-comment-field' ) .blur( function() { $( window ).bind( 'keydown', keyListener ); } ) .focus( function() { $( window ).unbind( 'keydown', keyListener ); } ); var emailField = $( '#jp-carousel-comment-form-email-field' ) .blur( function() { $( window ).bind( 'keydown', keyListener ); } ) .focus( function() { $( window ).unbind( 'keydown', keyListener ); } ); var authorField = $( '#jp-carousel-comment-form-author-field' ) .blur( function() { $( window ).bind( 'keydown', keyListener ); } ) .focus( function() { $( window ).unbind( 'keydown', keyListener ); } ); var urlField = $( '#jp-carousel-comment-form-url-field' ) .blur( function() { $( window ).bind( 'keydown', keyListener ); } ) .focus( function() { $( window ).unbind( 'keydown', keyListener ); } ); if ( textarea && textarea.attr( 'id' ) === target.attr( 'id' ) ) { // For first page load $( window ).unbind( 'keydown', keyListener ); $( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' ); } else if ( target.is( 'input[type="submit"]' ) ) { e.preventDefault(); e.stopPropagation(); $( '#jp-carousel-comment-form-spinner' ).spin( 'small', 'white' ); var ajaxData = { action: 'post_attachment_comment', nonce: jetpackCarouselStrings.nonce, blog_id: data[ 'blog_id' ], id: attachment_id, comment: textarea.val(), }; if ( ! ajaxData[ 'comment' ].length ) { gallery.jp_carousel( 'postCommentError', { field: 'jp-carousel-comment-form-comment-field', error: jetpackCarouselStrings.no_comment_text, } ); return; } if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) { ajaxData[ 'email' ] = emailField.val(); ajaxData[ 'author' ] = authorField.val(); ajaxData[ 'url' ] = urlField.val(); if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) { if ( ! ajaxData[ 'email' ].length || ! ajaxData[ 'email' ].match( '@' ) ) { gallery.jp_carousel( 'postCommentError', { field: 'jp-carousel-comment-form-email-field', error: jetpackCarouselStrings.no_comment_email, } ); return; } else if ( ! ajaxData[ 'author' ].length ) { gallery.jp_carousel( 'postCommentError', { field: 'jp-carousel-comment-form-author-field', error: jetpackCarouselStrings.no_comment_author, } ); return; } } } $.ajax( { type: 'POST', url: jetpackCarouselStrings.ajaxurl, data: ajaxData, dataType: 'json', success: function( response /*, status, xhr*/ ) { if ( 'approved' === response.comment_status ) { $( '#jp-carousel-comment-post-results' ) .slideUp( 'fast' ) .html( '' + jetpackCarouselStrings.comment_approved + '' ) .slideDown( 'fast' ); } else if ( 'unapproved' === response.comment_status ) { $( '#jp-carousel-comment-post-results' ) .slideUp( 'fast' ) .html( '' + jetpackCarouselStrings.comment_unapproved + '' ) .slideDown( 'fast' ); } else { // 'deleted', 'spam', false $( '#jp-carousel-comment-post-results' ) .slideUp( 'fast' ) .html( '' + jetpackCarouselStrings.comment_post_error + '' ) .slideDown( 'fast' ); } gallery.jp_carousel( 'clearCommentTextAreaValue' ); gallery.jp_carousel( 'getComments', { attachment_id: attachment_id, offset: 0, clear: true, } ); $( '#jp-carousel-comment-form-button-submit' ).val( jetpackCarouselStrings.post_comment ); $( '#jp-carousel-comment-form-spinner' ).spin( false ); }, error: function(/*xhr, status, error*/) { // TODO: Add error handling and display here gallery.jp_carousel( 'postCommentError', { field: 'jp-carousel-comment-form-comment-field', error: jetpackCarouselStrings.comment_post_error, } ); return; }, } ); } } else if ( ! target.parents( '.jp-carousel-info' ).length ) { container.jp_carousel( 'next' ); } } ) .bind( 'jp_carousel.afterOpen', function() { $( window ).bind( 'keydown', keyListener ); $( window ).bind( 'resize', resizeListener ); gallery.opened = true; resizeListener(); } ) .bind( 'jp_carousel.beforeClose', function() { var scroll = $( window ).scrollTop(); $( window ).unbind( 'keydown', keyListener ); $( window ).unbind( 'resize', resizeListener ); $( window ).scrollTop( scroll ); $( '.jp-carousel-previous-button' ).hide(); $( '.jp-carousel-next-button' ).hide(); // Set height to original value // Fix some themes where closing carousel brings view back to top $( 'html' ).css( 'height', '' ); } ) .bind( 'jp_carousel.afterClose', function() { if ( window.location.hash && history.back ) { history.back(); } last_known_location_hash = ''; gallery.opened = false; } ) .on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function( e ) { // If the movement transitions take more than twice the allotted time, disable them. // There is some wiggle room in the 2x, since some of that time is taken up in // JavaScript, setting up the transition and calling the events. if ( 'transform' === e.originalEvent.propertyName ) { var transitionMultiplier = ( Date.now() - transitionBegin ) / 1000 / e.originalEvent.elapsedTime; container.off( 'transitionend.jp-carousel' ); if ( transitionMultiplier >= 2 ) { $( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' ); } } } ); $( '.jp-carousel-wrap' ).touchwipe( { wipeLeft: function( e ) { e.preventDefault(); gallery.jp_carousel( 'next' ); }, wipeRight: function( e ) { e.preventDefault(); gallery.jp_carousel( 'previous' ); }, preventDefaultEvents: false, } ); nextButton.add( previousButton ).click( function( e ) { e.preventDefault(); e.stopPropagation(); if ( nextButton.is( this ) ) { gallery.jp_carousel( 'next' ); } else { gallery.jp_carousel( 'previous' ); } } ); } }; var processSingleImageGallery = function() { // process links that contain img tag with attribute data-attachment-id $( 'a img[data-attachment-id]' ).each( function() { var container = $( this ).parent(); // skip if image was already added to gallery by shortcode if ( container.parent( '.gallery-icon' ).length ) { return; } // skip if the container is not a link if ( 'undefined' === typeof $( container ).attr( 'href' ) ) { return; } var valid = false; // if link points to 'Media File' (ignoring GET parameters) and flag is set allow it if ( $( container ) .attr( 'href' ) .split( '?' )[ 0 ] === $( this ) .attr( 'data-orig-file' ) .split( '?' )[ 0 ] && 1 === Number( jetpackCarouselStrings.single_image_gallery_media_file ) ) { valid = true; } // if link points to 'Attachment Page' allow it if ( $( container ).attr( 'href' ) === $( this ).attr( 'data-permalink' ) ) { valid = true; } // links to 'Custom URL' or 'Media File' when flag not set are not valid if ( ! valid ) { return; } // make this node a gallery recognizable by event listener above $( container ).addClass( 'single-image-gallery' ); // blog_id is needed to allow posting comments to correct blog $( container ).data( 'carousel-extra', { blog_id: Number( jetpackCarouselStrings.blog_id ), } ); } ); }; var methods = { testForData: function( gallery ) { gallery = $( gallery ); // make sure we have it as a jQuery object. return ! ( ! gallery.length || ! gallery.data( 'carousel-extra' ) ); }, testIfOpened: function() { return !! ( 'undefined' !== typeof gallery && 'undefined' !== typeof gallery.opened && gallery.opened ); }, openOrSelectSlide: function( index ) { // The `open` method triggers an asynchronous effect, so we will get an // error if we try to use `open` then `selectSlideAtIndex` immediately // after it. We can only use `selectSlideAtIndex` if the carousel is // already open. if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) { // The `open` method selects the correct slide during the // initialization. $( this ).jp_carousel( 'open', { start_index: index } ); } else { gallery.jp_carousel( 'selectSlideAtIndex', index ); } }, open: function( options ) { var settings = { items_selector: '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id]', start_index: 0, }, data = $( this ).data( 'carousel-extra' ); if ( ! data ) { return; // don't run if the default gallery functions weren't used } prepareGallery( data ); if ( gallery.jp_carousel( 'testIfOpened' ) ) { return; // don't open if already opened } // make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger // infiniscroll for it when enabled (Reader, theme infiniscroll, etc). originalOverflow = $( 'body' ).css( 'overflow' ); $( 'body' ).css( 'overflow', 'hidden' ); // prevent html from overflowing on some of the new themes. originalHOverflow = $( 'html' ).css( 'overflow' ); $( 'html' ).css( 'overflow', 'hidden' ); scrollPos = $( window ).scrollTop(); container.data( 'carousel-extra', data ); return this.each( function() { // If options exist, lets merge them // with our default settings var $this = $( this ); if ( options ) { $.extend( settings, options ); } if ( -1 === settings.start_index ) { settings.start_index = 0; //-1 returned if can't find index, so start from beginning } container.trigger( 'jp_carousel.beforeOpen' ).fadeIn( 'fast', function() { container.trigger( 'jp_carousel.afterOpen' ); gallery .jp_carousel( 'initSlides', $this.find( settings.items_selector ), settings.start_index ) .jp_carousel( 'selectSlideAtIndex', settings.start_index ); } ); gallery.html( '' ); } ); }, selectSlideAtIndex: function( index ) { var slides = this.jp_carousel( 'slides' ), selected = slides.eq( index ); if ( 0 === selected.length ) { selected = slides.eq( 0 ); } gallery.jp_carousel( 'selectSlide', selected, false ); return this; }, close: function() { // make sure to let the page scroll again $( 'body' ).css( 'overflow', originalOverflow ); $( 'html' ).css( 'overflow', originalHOverflow ); this.jp_carousel( 'clearCommentTextAreaValue' ); return container.trigger( 'jp_carousel.beforeClose' ).fadeOut( 'fast', function() { container.trigger( 'jp_carousel.afterClose' ); $( window ).scrollTop( scrollPos ); } ); }, next: function() { this.jp_carousel( 'previousOrNext', 'nextSlide' ); }, previous: function() { this.jp_carousel( 'previousOrNext', 'prevSlide' ); }, previousOrNext: function( slideSelectionMethodName ) { if ( ! this.jp_carousel( 'hasMultipleImages' ) ) { return false; } var slide = gallery.jp_carousel( slideSelectionMethodName ); if ( slide ) { container.animate( { scrollTop: 0 }, 'fast' ); this.jp_carousel( 'clearCommentTextAreaValue' ); this.jp_carousel( 'selectSlide', slide ); } }, selectedSlide: function() { return this.find( '.selected' ); }, setSlidePosition: function( x ) { transitionBegin = Date.now(); return this.css( { '-webkit-transform': 'translate3d(' + x + 'px,0,0)', '-moz-transform': 'translate3d(' + x + 'px,0,0)', '-ms-transform': 'translate(' + x + 'px,0)', '-o-transform': 'translate(' + x + 'px,0)', transform: 'translate3d(' + x + 'px,0,0)', } ); }, updateSlidePositions: function( animate ) { var current = this.jp_carousel( 'selectedSlide' ), galleryWidth = gallery.width(), currentWidth = current.width(), previous = gallery.jp_carousel( 'prevSlide' ), next = gallery.jp_carousel( 'nextSlide' ), previousPrevious = previous.prev(), nextNext = next.next(), left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 ); current.jp_carousel( 'setSlidePosition', left ).show(); // minimum width gallery.jp_carousel( 'fitInfo', animate ); // prep the slides var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1; // Since we preload the `previousPrevious` and `nextNext` slides, we need // to make sure they technically visible in the DOM, but invisible to the // user. To hide them from the user, we position them outside the edges // of the window. // // This section of code only applies when there are more than three // slides. Otherwise, the `previousPrevious` and `nextNext` slides will // overlap with the `previous` and `next` slides which must be visible // regardless. if ( 1 === direction ) { if ( ! nextNext.is( previous ) ) { nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show(); } if ( ! previousPrevious.is( next ) ) { previousPrevious .jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth ) .show(); } } else { if ( ! nextNext.is( previous ) ) { nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show(); } } previous .jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + screenPadding * 0.75 ) ) .show(); next .jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - screenPadding * 0.75 ) ) .show(); }, selectSlide: function( slide, animate ) { lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' ); var slides = gallery.jp_carousel( 'slides' ).css( { position: 'fixed' } ), current = $( slide ) .addClass( 'selected' ) .css( { position: 'relative' } ), attachmentId = current.data( 'attachment-id' ), previous = gallery.jp_carousel( 'prevSlide' ), next = gallery.jp_carousel( 'nextSlide' ), previousPrevious = previous.prev(), nextNext = next.next(), animated, captionHtml; // center the main image gallery.jp_carousel( 'loadFullImage', current ); caption.hide(); if ( next.length === 0 && slides.length <= 2 ) { $( '.jp-carousel-next-button' ).hide(); } else { $( '.jp-carousel-next-button' ).show(); } if ( previous.length === 0 && slides.length <= 2 ) { $( '.jp-carousel-previous-button' ).hide(); } else { $( '.jp-carousel-previous-button' ).show(); } animated = current .add( previous ) .add( previousPrevious ) .add( next ) .add( nextNext ) .jp_carousel( 'loadSlide' ); // slide the whole view to the x we want slides.not( animated ).hide(); gallery.jp_carousel( 'updateSlidePositions', animate ); container.trigger( 'jp_carousel.selectSlide', [ current ] ); gallery.jp_carousel( 'getTitleDesc', { title: current.data( 'title' ), desc: current.data( 'desc' ), } ); var imageMeta = current.data( 'image-meta' ); gallery.jp_carousel( 'updateExif', imageMeta ); gallery.jp_carousel( 'updateFullSizeLink', current ); gallery.jp_carousel( 'updateMap', imageMeta ); gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) ); gallery.jp_carousel( 'getComments', { attachment_id: attachmentId, offset: 0, clear: true, } ); $( '#jp-carousel-comment-post-results' ).slideUp(); // $('
').text(sometext).html() is a trick to go to HTML to plain // text (including HTML entities decode, etc) if ( current.data( 'caption' ) ) { captionHtml = $( '
' ) .text( current.data( 'caption' ) ) .html(); if ( captionHtml === $( '
' ) .text( current.data( 'title' ) ) .html() ) { $( '.jp-carousel-titleanddesc-title' ) .fadeOut( 'fast' ) .empty(); } if ( captionHtml === $( '
' ) .text( current.data( 'desc' ) ) .html() ) { $( '.jp-carousel-titleanddesc-desc' ) .fadeOut( 'fast' ) .empty(); } caption.html( current.data( 'caption' ) ).fadeIn( 'slow' ); } else { caption.fadeOut( 'fast' ).empty(); } // Record pageview in WP Stats, for each new image loaded full-screen. if ( jetpackCarouselStrings.stats ) { new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?' + jetpackCarouselStrings.stats + '&post=' + encodeURIComponent( attachmentId ) + '&rand=' + Math.random(); } // Load the images for the next and previous slides. $( next ) .add( previous ) .each( function() { gallery.jp_carousel( 'loadFullImage', $( this ) ); } ); window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId; }, slides: function() { return this.find( '.jp-carousel-slide' ); }, slideDimensions: function() { return { width: $( window ).width() - screenPadding * 2, height: Math.floor( ( $( window ).height() / 100 ) * proportion - 60 ), }; }, loadSlide: function() { return this.each( function() { var slide = $( this ); slide.find( 'img' ).one( 'load', function() { // set the width/height of the image if it's too big slide.jp_carousel( 'fitSlide', false ); } ); } ); }, bestFit: function() { var max = gallery.jp_carousel( 'slideDimensions' ), orig = this.jp_carousel( 'originalDimensions' ), orig_ratio = orig.width / orig.height, w_ratio = 1, h_ratio = 1, width, height; if ( orig.width > max.width ) { w_ratio = max.width / orig.width; } if ( orig.height > max.height ) { h_ratio = max.height / orig.height; } if ( w_ratio < h_ratio ) { width = max.width; height = Math.floor( width / orig_ratio ); } else if ( h_ratio < w_ratio ) { height = max.height; width = Math.floor( height * orig_ratio ); } else { width = orig.width; height = orig.height; } return { width: width, height: height, }; }, fitInfo: function(/*animated*/) { var current = this.jp_carousel( 'selectedSlide' ), size = current.jp_carousel( 'bestFit' ); photo_info.css( { left: Math.floor( ( info.width() - size.width ) * 0.5 ), width: Math.floor( size.width ), } ); return this; }, fitMeta: function( animated ) { var newInfoTop = { top: Math.floor( ( $( window ).height() / 100 ) * proportion + 5 ) + 'px', }; var newLeftWidth = { width: info.width() - ( imageMeta.width() + 80 ) + 'px' }; if ( animated ) { info.animate( newInfoTop ); leftColWrapper.animate( newLeftWidth ); } else { info.animate( newInfoTop ); leftColWrapper.css( newLeftWidth ); } }, fitSlide: function(/*animated*/) { return this.each( function() { var $this = $( this ), dimensions = $this.jp_carousel( 'bestFit' ), method = 'css', max = gallery.jp_carousel( 'slideDimensions' ); dimensions.left = 0; dimensions.top = Math.floor( ( max.height - dimensions.height ) * 0.5 ) + 40; $this[ method ]( dimensions ); } ); }, texturize: function( text ) { text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace(). text = text .replace( /'/g, '’' ) .replace( /'/g, '’' ) .replace( /[\u2019]/g, '’' ); text = text .replace( /"/g, '”' ) .replace( /"/g, '”' ) .replace( /"/g, '”' ) .replace( /[\u201D]/g, '”' ); text = text.replace( /([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"' ); // untexturize allowed HTML tags params double-quotes return $.trim( text ); }, initSlides: function( items, start_index ) { if ( items.length < 2 ) { $( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide(); } else { $( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show(); } // Calculate the new src. items.each( function(/*i*/) { var src_item = $( this ), orig_size = src_item.data( 'orig-size' ) || '', max = gallery.jp_carousel( 'slideDimensions' ), parts = orig_size.split( ',' ), medium_file = src_item.data( 'medium-file' ) || '', large_file = src_item.data( 'large-file' ) || '', src; orig_size = { width: parseInt( parts[ 0 ], 10 ), height: parseInt( parts[ 1 ], 10 ) }; src = src_item.data( 'orig-file' ); src = gallery.jp_carousel( 'selectBestImageSize', { orig_file: src, orig_width: orig_size.width, orig_height: orig_size.height, max_width: max.width, max_height: max.height, medium_file: medium_file, large_file: large_file, } ); // Set the final src $( this ).data( 'gallery-src', src ); } ); // If the start_index is not 0 then preload the clicked image first. if ( 0 !== start_index ) { $( '' )[ 0 ].src = $( items[ start_index ] ).data( 'gallery-src' ); } var useInPageThumbnails = items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0; // create the 'slide' items.each( function( i ) { var src_item = $( this ), attachment_id = src_item.data( 'attachment-id' ) || 0, comments_opened = src_item.data( 'comments-opened' ) || 0, image_meta = src_item.data( 'image-meta' ) || {}, orig_size = src_item.data( 'orig-size' ) || '', thumb_size = { width: src_item[ 0 ].naturalWidth, height: src_item[ 0 ].naturalHeight }, title = src_item.data( 'image-title' ) || '', description = src_item.data( 'image-description' ) || '', caption = src_item .parents( '.gallery-item' ) .find( '.gallery-caption' ) .html() || '', src = src_item.data( 'gallery-src' ) || '', medium_file = src_item.data( 'medium-file' ) || '', large_file = src_item.data( 'large-file' ) || '', orig_file = src_item.data( 'orig-file' ) || ''; var tiledCaption = src_item .parents( 'div.tiled-gallery-item' ) .find( 'div.tiled-gallery-caption' ) .html(); if ( tiledCaption ) { caption = tiledCaption; } if ( attachment_id && orig_size.length ) { title = gallery.jp_carousel( 'texturize', title ); description = gallery.jp_carousel( 'texturize', description ); caption = gallery.jp_carousel( 'texturize', caption ); // Initially, the image is a 1x1 transparent gif. The preview is shown as a background image on the slide itself. var image = $( '' ) .attr( 'src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ) .css( 'width', '100%' ) .css( 'height', '100%' ); var slide = $( '' ) .hide() .css( { //'position' : 'fixed', left: i < start_index ? -1000 : gallery.width(), } ) .append( image ) .appendTo( gallery ) .data( 'src', src ) .data( 'title', title ) .data( 'desc', description ) .data( 'caption', caption ) .data( 'attachment-id', attachment_id ) .data( 'permalink', src_item.parents( 'a' ).attr( 'href' ) ) .data( 'orig-size', orig_size ) .data( 'comments-opened', comments_opened ) .data( 'image-meta', image_meta ) .data( 'medium-file', medium_file ) .data( 'large-file', large_file ) .data( 'orig-file', orig_file ) .data( 'thumb-size', thumb_size ); if ( useInPageThumbnails ) { // Use the image already loaded in the gallery as a preview. slide.data( 'preview-image', src_item.attr( 'src' ) ).css( { 'background-image': 'url("' + src_item.attr( 'src' ) + '")', 'background-size': '100% 100%', 'background-position': 'center center', } ); } slide.jp_carousel( 'fitSlide', false ); } } ); return this; }, selectBestImageSize: function( args ) { if ( 'object' !== typeof args ) { args = {}; } if ( 'undefined' === typeof args.orig_file ) { return ''; } if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) { return args.orig_file; } if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) { return args.orig_file; } // Check if the image is being served by Photon (using a regular expression on the hostname). var imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = args.large_file; var isPhotonUrl = /^i[0-2].wp.com$/i.test( imageLinkParser.hostname ); var medium_size_parts = gallery.jp_carousel( 'getImageSizeParts', args.medium_file, args.orig_width, isPhotonUrl ); var large_size_parts = gallery.jp_carousel( 'getImageSizeParts', args.large_file, args.orig_width, isPhotonUrl ); var large_width = parseInt( large_size_parts[ 0 ], 10 ), large_height = parseInt( large_size_parts[ 1 ], 10 ), medium_width = parseInt( medium_size_parts[ 0 ], 10 ), medium_height = parseInt( medium_size_parts[ 1 ], 10 ); // Assign max width and height. args.orig_max_width = args.max_width; args.orig_max_height = args.max_height; // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc) if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) { args.max_width = args.max_width * window.devicePixelRatio; args.max_height = args.max_height * window.devicePixelRatio; } if ( large_width >= args.max_width || large_height >= args.max_height ) { return args.large_file; } if ( medium_width >= args.max_width || medium_height >= args.max_height ) { return args.medium_file; } if ( isPhotonUrl ) { // args.orig_file doesn't point to a Photon url, so in this case we use args.large_file // to return the photon url of the original image. var largeFileIndex = args.large_file.lastIndexOf( '?' ); var origPhotonUrl = args.large_file; if ( -1 !== largeFileIndex ) { origPhotonUrl = args.large_file.substring( 0, largeFileIndex ); // If we have a really large image load a smaller version // that is closer to the viewable size if ( args.orig_width > args.max_width || args.orig_height > args.max_height ) { origPhotonUrl += '?fit=' + args.orig_max_width + '%2C' + args.orig_max_height; } } return origPhotonUrl; } return args.orig_file; }, getImageSizeParts: function( file, orig_width, isPhotonUrl ) { var size = isPhotonUrl ? file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' ) : file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' ); var size_parts = size !== file ? isPhotonUrl ? size.split( '%2C' ) : size.split( 'x' ) : [ orig_width, 0 ]; // If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url. // In that case, we set the value to 0. if ( '9999' === size_parts[ 0 ] ) { size_parts[ 0 ] = '0'; } if ( '9999' === size_parts[ 1 ] ) { size_parts[ 1 ] = '0'; } return size_parts; }, originalDimensions: function() { var splitted = $( this ) .data( 'orig-size' ) .split( ',' ); return { width: parseInt( splitted[ 0 ], 10 ), height: parseInt( splitted[ 1 ], 10 ) }; }, format: function( args ) { if ( 'object' !== typeof args ) { args = {}; } if ( ! args.text || 'undefined' === typeof args.text ) { return; } if ( ! args.replacements || 'undefined' === typeof args.replacements ) { return args.text; } return args.text.replace( /{(\d+)}/g, function( match, number ) { return typeof args.replacements[ number ] !== 'undefined' ? args.replacements[ number ] : match; } ); }, /** * Returns a number in a fraction format that represents the shutter speed. * @param Number speed * @return String */ shutterSpeed: function( speed ) { var denominator; // round to one decimal if value > 1s by multiplying it by 10, rounding, then dividing by 10 again if ( speed >= 1 ) { return Math.round( speed * 10 ) / 10 + 's'; } // If the speed is less than one, we find the denominator by inverting // the number. Since cameras usually use rational numbers as shutter // speeds, we should get a nice round number. Or close to one in cases // like 1/30. So we round it. denominator = Math.round( 1 / speed ); return '1/' + denominator + 's'; }, parseTitleDesc: function( value ) { if ( ! value.match( ' ' ) && value.match( '_' ) ) { return ''; } return value; }, getTitleDesc: function( data ) { var title = '', desc = '', markup = '', target; target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' ); target.hide(); title = gallery.jp_carousel( 'parseTitleDesc', data.title ) || ''; desc = gallery.jp_carousel( 'parseTitleDesc', data.desc ) || ''; if ( title.length || desc.length ) { // Convert from HTML to plain text (including HTML entities decode, etc) if ( $( '
' ) .html( title ) .text() === $( '
' ) .html( desc ) .text() ) { title = ''; } markup = title.length ? '' : ''; markup += desc.length ? '' : ''; target.html( markup ).fadeIn( 'slow' ); } $( 'div#jp-carousel-comment-form-container' ).css( 'margin-top', '20px' ); $( 'div#jp-carousel-comments-loading' ).css( 'margin-top', '20px' ); }, // updateExif updates the contents of the exif UL (.jp-carousel-image-exif) updateExif: function( meta ) { if ( ! meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) { return false; } var $ul = $( "" ); $.each( meta, function( key, val ) { if ( 0 === parseFloat( val ) || ! val.length || -1 === $.inArray( key, $.makeArray( jetpackCarouselStrings.meta_data ) ) ) { return; } switch ( key ) { case 'focal_length': val = val + 'mm'; break; case 'shutter_speed': val = gallery.jp_carousel( 'shutterSpeed', val ); break; case 'aperture': val = 'f/' + val; break; } $ul.append( '
  • ' + jetpackCarouselStrings[ key ] + '
    ' + val + '
  • ' ); } ); // Update (replace) the content of the ul $( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul ); }, // updateFullSizeLink updates the contents of the jp-carousel-image-download link updateFullSizeLink: function( current ) { if ( ! current || ! current.data ) { return false; } var original, origSize = current.data( 'orig-size' ).split( ',' ), imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = current.data( 'src' ).replace( /\?.+$/, '' ); // Is this a Photon URL? if ( imageLinkParser.hostname.match( /^i[\d]{1}.wp.com$/i ) !== null ) { original = imageLinkParser.href; } else { original = current.data( 'orig-file' ).replace( /\?.+$/, '' ); } var permalink = $( '' + gallery.jp_carousel( 'format', { text: jetpackCarouselStrings.download_original, replacements: origSize, } ) + '' ) .addClass( 'jp-carousel-image-download' ) .attr( 'href', original ) .attr( 'target', '_blank' ); // Update (replace) the content of the anchor $( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink ); }, updateMap: function( meta ) { if ( ! meta.latitude || ! meta.longitude || 1 !== Number( jetpackCarouselStrings.display_geo ) ) { return; } var latitude = meta.latitude, longitude = meta.longitude, $metabox = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ), $mapbox = $( '
    ' ), style = '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91'; $mapbox .addClass( 'jp-carousel-image-map' ) .html( '\ \
    \ \ ' ) .prependTo( $metabox ); }, testCommentsOpened: function( opened ) { if ( 1 === parseInt( opened, 10 ) ) { $( '.jp-carousel-buttons' ).fadeIn( 'fast' ); commentForm.fadeIn( 'fast' ); } else { $( '.jp-carousel-buttons' ).fadeOut( 'fast' ); commentForm.fadeOut( 'fast' ); } }, getComments: function( args ) { clearInterval( commentInterval ); if ( 'object' !== typeof args ) { return; } if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) { return; } if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) { args.offset = 0; } var comments = $( '.jp-carousel-comments' ), commentsLoading = $( '#jp-carousel-comments-loading' ).show(); if ( args.clear ) { comments.hide().empty(); } $.ajax( { type: 'GET', url: jetpackCarouselStrings.ajaxurl, dataType: 'json', data: { action: 'get_attachment_comments', nonce: jetpackCarouselStrings.nonce, id: args.attachment_id, offset: args.offset, }, success: function( data /*, status, xhr*/ ) { if ( args.clear ) { comments.fadeOut( 'fast' ).empty(); } $( data ).each( function() { var comment = $( '
    ' ) .addClass( 'jp-carousel-comment' ) .attr( 'id', 'jp-carousel-comment-' + this[ 'id' ] ) .html( '
    ' + this[ 'gravatar_markup' ] + '
    ' + '
    ' + this[ 'author_markup' ] + '
    ' + '
    ' + this[ 'date_gmt' ] + '
    ' + '
    ' + this[ 'content' ] + '
    ' ); comments.append( comment ); // Set the interval to check for a new page of comments. clearInterval( commentInterval ); commentInterval = setInterval( function() { if ( $( '.jp-carousel-overlay' ).height() - 150 < $( '.jp-carousel-wrap' ).scrollTop() + $( window ).height() ) { gallery.jp_carousel( 'getComments', { attachment_id: args.attachment_id, offset: args.offset + 10, clear: false, } ); clearInterval( commentInterval ); } }, 300 ); } ); // Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested // attachment id might no longer match the current attachment id by the time we get the data back or a now // registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake. var current = $( '.jp-carousel div.selected' ); if ( current && current.data && current.data( 'attachment-id' ) != args.attachment_id // jshint ignore:line ) { comments.fadeOut( 'fast' ); comments.empty(); return; } // Increase the height of the background, semi-transparent overlay to match the new length of the comments list. $( '.jp-carousel-overlay' ).height( $( window ).height() + titleAndDescription.height() + commentForm.height() + ( comments.height() > 0 ? comments.height() : imageMeta.height() ) + 200 ); comments.show(); commentsLoading.hide(); }, error: function( xhr, status, error ) { // TODO: proper error handling console.log( 'Comment get fail...', xhr, status, error ); comments.fadeIn( 'fast' ); commentsLoading.fadeOut( 'fast' ); }, } ); }, postCommentError: function( args ) { if ( 'object' !== typeof args ) { args = {}; } if ( ! args.field || 'undefined' === typeof args.field || ! args.error || 'undefined' === typeof args.error ) { return; } $( '#jp-carousel-comment-post-results' ) .slideUp( 'fast' ) .html( '' ) .slideDown( 'fast' ); $( '#jp-carousel-comment-form-spinner' ).spin( false ); }, setCommentIframeSrc: function( attachment_id ) { var iframe = $( '#jp-carousel-comment-iframe' ); // Set the proper irame src for the current attachment id if ( iframe && iframe.length ) { iframe.attr( 'src', iframe.attr( 'src' ).replace( /(postid=)\d+/, '$1' + attachment_id ) ); iframe.attr( 'src', iframe.attr( 'src' ).replace( /(%23.+)?$/, '%23jp-carousel-' + attachment_id ) ); } }, clearCommentTextAreaValue: function() { var commentTextArea = $( '#jp-carousel-comment-form-comment-field' ); if ( commentTextArea ) { commentTextArea.val( '' ); } }, nextSlide: function() { var slides = this.jp_carousel( 'slides' ); var selected = this.jp_carousel( 'selectedSlide' ); if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) { return slides.first(); } return selected.next(); }, prevSlide: function() { var slides = this.jp_carousel( 'slides' ); var selected = this.jp_carousel( 'selectedSlide' ); if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) { return slides.last(); } return selected.prev(); }, loadFullImage: function( slide ) { var image = slide.find( 'img:first' ); if ( ! image.data( 'loaded' ) ) { // If the width of the slide is smaller than the width of the "thumbnail" we're already using, // don't load the full image. image.on( 'load.jetpack', function() { image.off( 'load.jetpack' ); $( this ) .closest( '.jp-carousel-slide' ) .css( 'background-image', '' ); } ); if ( ! slide.data( 'preview-image' ) || ( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width ) ) { image .attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) ) .attr( 'itemprop', 'image' ); } else { image.attr( 'src', slide.data( 'preview-image' ) ).attr( 'itemprop', 'image' ); } image.data( 'loaded', 1 ); } }, hasMultipleImages: function() { return gallery.jp_carousel( 'slides' ).length > 1; }, }; $.fn.jp_carousel = function( method ) { // ask for the HTML of the gallery // Method calling logic if ( methods[ method ] ) { return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); } else if ( typeof method === 'object' || ! method ) { return methods.open.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist on jQuery.jp_carousel' ); } }; // register the event listener for starting the gallery $( document.body ).on( 'click.jp-carousel', 'div.gallery, div.tiled-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery', function( e ) { if ( ! $( this ).jp_carousel( 'testForData', e.currentTarget ) ) { return; } // Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link. if ( $( e.target ) .parent() .hasClass( 'gallery-caption' ) ) { return; } // Do not open the modal if we are looking at a caption of a gallery block, which may contain a link. if ( $( e.target ) .parent() .is( 'figcaption' ) ) { return; } // Set height to auto // Fix some themes where closing carousel brings view back to top $( 'html' ).css( 'height', 'auto' ); e.preventDefault(); // Stopping propagation in case there are parent elements // with .gallery or .tiled-gallery class e.stopPropagation(); $( this ).jp_carousel( 'open', { start_index: $( this ) .find( '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item' ) .index( $( e.target ).parents( '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item' ) ), } ); } ); // handle lightbox (single image gallery) for images linking to 'Attachment Page' if ( 1 === Number( jetpackCarouselStrings.single_image_gallery ) ) { processSingleImageGallery(); $( document.body ).on( 'post-load', function() { processSingleImageGallery(); } ); } // Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger) $( window ).on( 'hashchange.jp-carousel', function() { var hashRegExp = /jp-carousel-(\d+)/, matches, attachmentId, galleries, selectedThumbnail; if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) { if ( gallery && gallery.opened ) { container.jp_carousel( 'close' ); } return; } if ( window.location.hash === last_known_location_hash && gallery.opened ) { return; } if ( window.location.hash && gallery && ! gallery.opened && history.back ) { history.back(); return; } last_known_location_hash = window.location.hash; matches = window.location.hash.match( hashRegExp ); attachmentId = parseInt( matches[ 1 ], 10 ); galleries = $( 'div.gallery, div.tiled-gallery, a.single-image-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery' ); // Find the first thumbnail that matches the attachment ID in the location // hash, then open the gallery that contains it. galleries.each( function( _, galleryEl ) { $( galleryEl ) .find( 'img' ) .each( function( imageIndex, imageEl ) { if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) { selectedThumbnail = { index: imageIndex, gallery: galleryEl }; return false; } } ); if ( selectedThumbnail ) { $( selectedThumbnail.gallery ).jp_carousel( 'openOrSelectSlide', selectedThumbnail.index ); return false; } } ); } ); if ( window.location.hash ) { $( window ).trigger( 'hashchange' ); } } ); /** * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!) * Common usage: wipe images (left and right to show the previous or next image) * * @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de) * Version 1.1.1, modified to pass the touchmove event to the callbacks. */ ( function( $ ) { $.fn.touchwipe = function( settings ) { var config = { min_move_x: 20, min_move_y: 20, wipeLeft: function(/*e*/) {}, wipeRight: function(/*e*/) {}, wipeUp: function(/*e*/) {}, wipeDown: function(/*e*/) {}, preventDefaultEvents: true, }; if ( settings ) { $.extend( config, settings ); } this.each( function() { var startX; var startY; var isMoving = false; function cancelTouch() { this.removeEventListener( 'touchmove', onTouchMove ); startX = null; isMoving = false; } function onTouchMove( e ) { if ( config.preventDefaultEvents ) { e.preventDefault(); } if ( isMoving ) { var x = e.touches[ 0 ].pageX; var y = e.touches[ 0 ].pageY; var dx = startX - x; var dy = startY - y; if ( Math.abs( dx ) >= config.min_move_x ) { cancelTouch(); if ( dx > 0 ) { config.wipeLeft( e ); } else { config.wipeRight( e ); } } else if ( Math.abs( dy ) >= config.min_move_y ) { cancelTouch(); if ( dy > 0 ) { config.wipeDown( e ); } else { config.wipeUp( e ); } } } } function onTouchStart( e ) { if ( e.touches.length === 1 ) { startX = e.touches[ 0 ].pageX; startY = e.touches[ 0 ].pageY; isMoving = true; this.addEventListener( 'touchmove', onTouchMove, false ); } } if ( 'ontouchstart' in document.documentElement ) { this.addEventListener( 'touchstart', onTouchStart, false ); } } ); return this; }; } )( jQuery );