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