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