1/* Detect-zoom 2 * ----------- 3 * Cross Browser Zoom and Pixel Ratio Detector 4 * Version 1.0.4 | Apr 1 2013 5 * dual-licensed under the WTFPL and MIT license 6 * Maintained by https://github/tombigel 7 * Original developer https://github.com/yonran 8 */ 9 10//AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js 11(function (root, ns, factory) { 12 "use strict"; 13 14 if (typeof (module) !== 'undefined' && module.exports) { // CommonJS 15 module.exports = factory(ns, root); 16 } else if (typeof (define) === 'function' && define.amd) { // AMD 17 define("factory", function () { 18 return factory(ns, root); 19 }); 20 } else { 21 root[ns] = factory(ns, root); 22 } 23 24}(window, 'detectZoom', function () { 25 26 /** 27 * Use devicePixelRatio if supported by the browser 28 * @return {Number} 29 * @private 30 */ 31 var devicePixelRatio = function () { 32 return window.devicePixelRatio || 1; 33 }; 34 35 /** 36 * Fallback function to set default values 37 * @return {Object} 38 * @private 39 */ 40 var fallback = function () { 41 return { 42 zoom: 1, 43 devicePxPerCssPx: 1 44 }; 45 }; 46 /** 47 * IE 8 and 9: no trick needed! 48 * TODO: Test on IE10 and Windows 8 RT 49 * @return {Object} 50 * @private 51 **/ 52 var ie8 = function () { 53 var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100; 54 return { 55 zoom: zoom, 56 devicePxPerCssPx: zoom * devicePixelRatio() 57 }; 58 }; 59 60 /** 61 * For IE10 we need to change our technique again... 62 * thanks https://github.com/stefanvanburen 63 * @return {Object} 64 * @private 65 */ 66 var ie10 = function () { 67 var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100; 68 return { 69 zoom: zoom, 70 devicePxPerCssPx: zoom * devicePixelRatio() 71 }; 72 }; 73 74 /** 75 * Mobile WebKit 76 * the trick: window.innerWIdth is in CSS pixels, while 77 * screen.width and screen.height are in system pixels. 78 * And there are no scrollbars to mess up the measurement. 79 * @return {Object} 80 * @private 81 */ 82 var webkitMobile = function () { 83 var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width; 84 var zoom = deviceWidth / window.innerWidth; 85 return { 86 zoom: zoom, 87 devicePxPerCssPx: zoom * devicePixelRatio() 88 }; 89 }; 90 91 /** 92 * Desktop Webkit 93 * the trick: an element's clientHeight is in CSS pixels, while you can 94 * set its line-height in system pixels using font-size and 95 * -webkit-text-size-adjust:none. 96 * device-pixel-ratio: https://www.webkit.org/blog/55/high-dpi-web-sites/ 97 * 98 * Previous trick (used before https://trac.webkit.org/changeset/100847): 99 * documentElement.scrollWidth is in CSS pixels, while 100 * document.width was in system pixels. Note that this is the 101 * layout width of the document, which is slightly different from viewport 102 * because document width does not include scrollbars and might be wider 103 * due to big elements. 104 * @return {Object} 105 * @private 106 */ 107 var webkit = function () { 108 var important = function (str) { 109 return str.replace(/;/g, " !important;"); 110 }; 111 112 var div = document.createElement('div'); 113 div.innerHTML = "1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0"; 114 div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;')); 115 116 // The container exists so that the div will be laid out in its own flow 117 // while not impacting the layout, viewport size, or display of the 118 // webpage as a whole. 119 // Add !important and relevant CSS rule resets 120 // so that other rules cannot affect the results. 121 var container = document.createElement('div'); 122 container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;')); 123 container.appendChild(div); 124 125 document.body.appendChild(container); 126 var zoom = 1000 / div.clientHeight; 127 zoom = Math.round(zoom * 100) / 100; 128 document.body.removeChild(container); 129 130 return{ 131 zoom: zoom, 132 devicePxPerCssPx: zoom * devicePixelRatio() 133 }; 134 }; 135 136 /** 137 * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi. 138 * (Note that this is a different interpretation than Webkit's device 139 * pixel ratio, which is the ratio device dpi / system dpi). 140 * 141 * Also, for Mozilla, there is no difference between the zoom factor and the device ratio. 142 * 143 * @return {Object} 144 * @private 145 */ 146 var firefox4 = function () { 147 var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001); 148 zoom = Math.round(zoom * 100) / 100; 149 return { 150 zoom: zoom, 151 devicePxPerCssPx: zoom 152 }; 153 }; 154 155 /** 156 * Firefox 18.x
157 * Mozilla added support for devicePixelRatio to Firefox 18, 158 * but it is affected by the zoom level, so, like in older 159 * Firefox we can't tell if we are in zoom mode or in a device 160 * with a different pixel ratio 161 * @return {Object} 162 * @private 163 */ 164 var firefox18 = function () { 165 return { 166 zoom: firefox4().zoom, 167 devicePxPerCssPx: devicePixelRatio() 168 }; 169 }; 170 171 /** 172 * works starting Opera 11.11 173 * the trick: outerWidth is the viewport width including scrollbars in 174 * system px, while innerWidth is the viewport width including scrollbars 175 * in CSS px 176 * @return {Object} 177 * @private 178 */ 179 var opera11 = function () { 180 var zoom = window.top.outerWidth / window.top.innerWidth; 181 zoom = Math.round(zoom * 100) / 100; 182 return { 183 zoom: zoom, 184 devicePxPerCssPx: zoom * devicePixelRatio() 185 }; 186 }; 187 188 /** 189 * Use a binary search through media queries to find zoom level in Firefox 190 * @param property 191 * @param unit 192 * @param a 193 * @param b 194 * @param maxIter 195 * @param epsilon 196 * @return {Number} 197 */ 198 var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) { 199 var matchMedia; 200 var head, style, div; 201 if (window.matchMedia) { 202 matchMedia = window.matchMedia; 203 } else { 204 head = document.getElementsByTagName('head')[0]; 205 style = document.createElement('style'); 206 head.appendChild(style); 207 208 div = document.createElement('div'); 209 div.className = 'mediaQueryBinarySearch'; 210 div.style.display = 'none'; 211 document.body.appendChild(div); 212 213 matchMedia = function (query) { 214 style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0); 215 var matched = getComputedStyle(div, null).textDecoration == 'underline'; 216 style.sheet.deleteRule(0); 217 return {matches: matched}; 218 }; 219 } 220 var ratio = binarySearch(a, b, maxIter); 221 if (div) { 222 head.removeChild(style); 223 document.body.removeChild(div); 224 } 225 return ratio; 226 227 function binarySearch(a, b, maxIter) { 228 var mid = (a + b) / 2; 229 if (maxIter <= 0 || b - a < epsilon) { 230 return mid; 231 } 232 var query = "(" + property + ":" + mid + unit + ")"; 233 if (matchMedia(query).matches) { 234 return binarySearch(mid, b, maxIter - 1); 235 } else { 236 return binarySearch(a, mid, maxIter - 1); 237 } 238 } 239 }; 240 241 /** 242 * Generate detection function 243 * @private 244 */ 245 var detectFunction = (function () { 246 var func = fallback; 247 //IE8+ 248 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { 249 func = ie8; 250 } 251 // IE10+ / Touch 252 else if (window.navigator.msMaxTouchPoints) { 253 func = ie10; 254 } 255 //Mobile Webkit 256 else if ('orientation' in window && typeof document.body.style.webkitMarquee === 'string') { 257 func = webkitMobile; 258 } 259 //WebKit 260 else if (typeof document.body.style.webkitMarquee === 'string') { 261 func = webkit; 262 } 263 //Opera 264 else if (navigator.userAgent.indexOf('Opera') >= 0) { 265 func = opera11; 266 } 267 //Last one is Firefox 268 //FF 18.x 269 else if (window.devicePixelRatio) { 270 func = firefox18; 271 } 272 //FF 4.0 - 17.x 273 else if (firefox4().zoom > 0.001) { 274 func = firefox4; 275 } 276 277 return func; 278 }()); 279 280 281 return ({ 282 283 /** 284 * Ratios.zoom shorthand 285 * @return {Number} Zoom level 286 */ 287 zoom: function () { 288 return detectFunction().zoom; 289 }, 290 291 /** 292 * Ratios.devicePxPerCssPx shorthand 293 * @return {Number} devicePxPerCssPx level 294 */ 295 device: function () { 296 return detectFunction().devicePxPerCssPx; 297 } 298 }); 299})); 300 301var wpcom_img_zoomer = { 302 clientHintSupport: { 303 gravatar: false, 304 files: false, 305 photon: false, 306 mshots: false, 307 staticAssets: false, 308 latex: false, 309 imgpress: false, 310 }, 311 useHints: false, 312 zoomed: false, 313 timer: null, 314 interval: 1000, // zoom polling interval in millisecond 315 316 // Should we apply width/height attributes to control the image size? 317 imgNeedsSizeAtts: function( img ) {
318 // Do not overwrite existing width/height attributes. 319 if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null ) 320 return false; 321 // Do not apply the attributes if the image is already constrained by a parent element. 322 if ( img.width < img.naturalWidth || img.height < img.naturalHeight ) 323 return false; 324 return true; 325 }, 326 327 hintsFor: function( service ) { 328 if ( this.useHints === false ) { 329 return false; 330 } 331 if ( this.hints() === false ) { 332 return false; 333 } 334 if ( typeof this.clientHintSupport[service] === "undefined" ) { 335 return false; 336 } 337 if ( this.clientHintSupport[service] === true ) { 338 return true; 339 } 340 return false; 341 }, 342 343 hints: function() { 344 try { 345 var chrome = window.navigator.userAgent.match(/\sChrome\/([0-9]+)\.[.0-9]+\s/) 346 if (chrome !== null) { 347 var version = parseInt(chrome[1], 10) 348 if (isNaN(version) === false && version >= 46) { 349 return true 350 } 351 } 352 } catch (e) { 353 return false 354 } 355 return false 356 }, 357 358 init: function() { 359 var t = this; 360 try{ 361 t.zoomImages(); 362 t.timer = setInterval( function() { t.zoomImages(); }, t.interval ); 363 } 364 catch(e){ 365 } 366 }, 367 368 stop: function() { 369 if ( this.timer ) 370 clearInterval( this.timer ); 371 }, 372 373 getScale: function() { 374 var scale = detectZoom.device(); 375 // Round up to 1.5 or the next integer below the cap. 376 if ( scale <= 1.0 ) scale = 1.0; 377 else if ( scale <= 1.5 ) scale = 1.5; 378 else if ( scale <= 2.0 ) scale = 2.0; 379 else if ( scale <= 3.0 ) scale = 3.0; 380 else if ( scale <= 4.0 ) scale = 4.0; 381 else scale = 5.0; 382 return scale; 383 }, 384 385 shouldZoom: function( scale ) { 386 var t = this; 387 // Do not operate on hidden frames. 388 if ( "innerWidth" in window && !window.innerWidth ) 389 return false; 390 // Don't do anything until scale > 1 391 if ( scale == 1.0 && t.zoomed == false ) 392 return false; 393 return true; 394 }, 395 396 zoomImages: function() { 397 var t = this; 398 var scale = t.getScale(); 399 if ( ! t.shouldZoom( scale ) ){ 400 return; 401 } 402 t.zoomed = true; 403 // Loop through all the <img> elements on the page. 404 var imgs = document.getElementsByTagName("img"); 405 406 for ( var i = 0; i < imgs.length; i++ ) { 407 // Wait for original images to load 408 if ( "complete" in imgs[i] && ! imgs[i].complete ) 409 continue; 410 411 // Skip images that have srcset attributes. 412 if ( imgs[i].hasAttribute('srcset') ) { 413 continue; 414 } 415 416 // Skip images that don't need processing. 417 var imgScale = imgs[i].getAttribute("scale"); 418 if ( imgScale == scale || imgScale == "0" ) 419 continue; 420 421 // Skip images that have already failed at this scale 422 var scaleFail = imgs[i].getAttribute("scale-fail"); 423 if ( scaleFail && scaleFail <= scale ) 424 continue; 425 426 // Skip images that have no dimensions yet. 427 if ( ! ( imgs[i].width && imgs[i].height ) ) 428 continue; 429 430 // Skip images from Lazy Load plugins 431 if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src"))) 432 continue; 433 434 if ( t.scaleImage( imgs[i], scale ) ) { 435 // Mark the img as having been processed at this scale. 436 imgs[i].setAttribute("scale", scale); 437 } 438 else { 439 // Set the flag to skip this image. 440 imgs[i].setAttribute("scale", "0"); 441 } 442 } 443 }, 444 445 scaleImage: function( img, scale ) { 446 var t = this; 447 var newSrc = img.src; 448 449 var isFiles = false; 450 var isLatex = false; 451 var isPhoton = false; 452 453 // Skip slideshow images 454 if ( img.parentNode.className.match(/slideshow-slide/) ) 455 return false; 456 457 // Scale gravatars that have ?s= or ?size= 458 if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) { 459 if ( this.hintsFor( "gravatar" ) === true ) { 460 return false; 461 } 462 newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) { 463 // Stash the original size 464 var originalAtt = "originals", 465 originalSize = img.getAttribute(originalAtt); 466 if ( originalSize === null ) { 467 originalSize = $3; 468 img.setAttribute(originalAtt, originalSize); 469 if ( t.imgNeedsSizeAtts( img ) ) { 470 // Fix width and height attributes to rendered dimensions. 471 img.width = img.width;
472 img.height = img.height; 473 } 474 } 475 // Get the width/height of the image in CSS pixels 476 var size = img.clientWidth; 477 // Convert CSS pixels to device pixels 478 var targetSize = Math.ceil(img.clientWidth * scale); 479 // Don't go smaller than the original size 480 targetSize = Math.max( targetSize, originalSize ); 481 // Don't go larger than the service supports 482 targetSize = Math.min( targetSize, 512 ); 483 return $1 + targetSize; 484 }); 485 } 486 487 // Scale mshots that have width 488 else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) { 489 if ( this.hintsFor( "mshots" ) === true ) { 490 return false; 491 } 492 newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) { 493 // Stash the original size 494 var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt); 495 if ( originalSize === null ) { 496 originalSize = $2; 497 img.setAttribute(originalAtt, originalSize); 498 if ( t.imgNeedsSizeAtts( img ) ) { 499 // Fix width and height attributes to rendered dimensions. 500 img.width = img.width; 501 img.height = img.height; 502 } 503 } 504 // Get the width of the image in CSS pixels 505 var size = img.clientWidth; 506 // Convert CSS pixels to device pixels 507 var targetSize = Math.ceil(size * scale); 508 // Don't go smaller than the original size 509 targetSize = Math.max( targetSize, originalSize ); 510 // Don't go bigger unless the current one is actually lacking 511 if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth ) 512 targetSize = $2; 513 if ( $2 != targetSize ) 514 return $1 + targetSize; 515 return $0; 516 }); 517 518 // Update height attribute to match width 519 newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) { 520 if ( newSrc == img.src ) { 521 return $0; 522 } 523 // Stash the original size 524 var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt); 525 if ( originalSize === null ) { 526 originalSize = $2; 527 img.setAttribute(originalAtt, originalSize); 528 } 529 // Get the height of the image in CSS pixels 530 var size = img.clientHeight; 531 // Convert CSS pixels to device pixels 532 var targetSize = Math.ceil(size * scale); 533 // Don't go smaller than the original size 534 targetSize = Math.max( targetSize, originalSize ); 535 // Don't go bigger unless the current one is actually lacking 536 if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight ) 537 targetSize = $2; 538 if ( $2 != targetSize ) 539 return $1 + targetSize; 540 return $0; 541 }); 542 } 543 544 // Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit 545 else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) { 546 if ( this.hintsFor( "imgpress" ) === true ) { 547 return false; 548 } 549 var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"]; 550 // Search the query string for unsupported functions. 551 var qs = RegExp.$3.split('&'); 552 for ( var q in qs ) { 553 q = qs[q].split('=')[0]; 554 if ( imgpressSafeFunctions.indexOf(q) == -1 ) { 555 return false; 556 } 557 } 558 // Fix width and height attributes to rendered dimensions. 559 img.width = img.width; 560 img.height = img.height; 561 // Compute new src 562 if ( scale == 1 ) 563 newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); 564 else 565 newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&'); 566 } 567 568 // Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com) 569 else if ( 570 ( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) || 571 ( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) || 572 ( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) ) 573 ) { 574 if ( false !== isFiles && this.hintsFor( "files" ) === true ) { 575 return false 576 } 577 if ( false !== isLatex && this.hintsFor( "latex" ) === true ) { 578 return false 579 } 580 if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) { 581 return false 582 } 583 // Fix width and height attributes to rendered dimensions. 584 img.width = img.width;
585 img.height = img.height; 586 // Compute new src 587 if ( scale == 1 ) { 588 newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); 589 } else { 590 newSrc = img.src; 591 592 var url_var = newSrc.match( /([?&]w=)(\d+)/ ); 593 if ( url_var !== null && url_var[2] ) { 594 newSrc = newSrc.replace( url_var[0], url_var[1] + img.width ); 595 } 596 597 url_var = newSrc.match( /([?&]h=)(\d+)/ ); 598 if ( url_var !== null && url_var[2] ) { 599 newSrc = newSrc.replace( url_var[0], url_var[1] + img.height ); 600 } 601 602 var zoom_arg = '&zoom=2'; 603 if ( !newSrc.match( /\?/ ) ) { 604 zoom_arg = '?zoom=2'; 605 } 606 img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' ); 607 } 608 } 609 610 // Scale static assets that have a name matching *-1x.png or *@1x.png 611 else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) { 612 if ( this.hintsFor( "staticAssets" ) === true ) { 613 return false; 614 } 615 // Fix width and height attributes to rendered dimensions. 616 img.width = img.width; 617 img.height = img.height; 618 var currentSize = RegExp.$1, newSize = currentSize; 619 if ( scale <= 1 ) 620 newSize = 1; 621 else 622 newSize = 2; 623 if ( currentSize != newSize ) 624 newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3'); 625 } 626 627 else { 628 return false; 629 } 630 631 // Don't set img.src unless it has changed. This avoids unnecessary reloads. 632 if ( newSrc != img.src ) { 633 // Store the original img.src 634 var prevSrc, origSrc = img.getAttribute("src-orig"); 635 if ( !origSrc ) { 636 origSrc = img.src; 637 img.setAttribute("src-orig", origSrc); 638 } 639 // In case of error, revert img.src 640 prevSrc = img.src; 641 img.onerror = function(){ 642 img.src = prevSrc; 643 if ( img.getAttribute("scale-fail") < scale ) 644 img.setAttribute("scale-fail", scale); 645 img.onerror = null; 646 }; 647 // Finally load the new image 648 img.src = newSrc; 649 } 650 651 return true; 652 } 653}; 654 655wpcom_img_zoomer.init(); 656; 657/* global pm, wpcom_reblog */ 658 659var jetpackLikesWidgetQueue = []; 660var jetpackLikesWidgetBatch = []; 661var jetpackLikesMasterReady = false; 662 663function JetpackLikespostMessage( message, target ) { 664 if ( 'string' === typeof message ){ 665 try { 666 message = JSON.parse( message ); 667 } catch(e) { 668 return; 669 } 670 } 671 672 pm( { 673 target: target, 674 type: 'likesMessage', 675 data: message, 676 origin: '*' 677 } ); 678} 679 680function JetpackLikesBatchHandler() { 681 var requests = []; 682 jQuery( 'div.jetpack-likes-widget-unloaded' ).each( function() { 683 if ( jetpackLikesWidgetBatch.indexOf( this.id ) > -1 ) { 684 return; 685 } 686 jetpackLikesWidgetBatch.push( this.id ); 687 var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/, 688 match = regex.exec( this.id ), 689 info; 690 691 if ( ! match || match.length !== 5 ) { 692 return; 693 } 694 695 info = { 696 blog_id: match[2], 697 width: this.width 698 }; 699 700 if ( 'post' === match[1] ) { 701 info.post_id = match[3]; 702 } else if ( 'comment' === match[1] ) { 703 info.comment_id = match[3]; 704 } 705 706 info.obj_id = match[4]; 707 708 requests.push( info ); 709 }); 710 711 if ( requests.length > 0 ) { 712 JetpackLikespostMessage( { event: 'initialBatch', requests: requests }, window.frames['likes-master'] ); 713 } 714} 715 716function JetpackLikesMessageListener( event, message ) { 717 var allowedOrigin, $container, $list, offset, rowLength, height, scrollbarWidth; 718 719 if ( 'undefined' === typeof event.event ) { 720 return; 721 } 722 723 // We only allow messages from one origin 724 allowedOrigin = window.location.protocol + 'https://widgets.wp.com'; 725 if ( allowedOrigin !== message.origin ) { 726 return; 727 } 728 729 if ( 'masterReady' === event.event ) { 730 jQuery( document ).ready( function() { 731 jetpackLikesMasterReady = true; 732 733 var stylesData = { 734 event: 'injectStyles' 735 }, 736 $sdTextColor = jQuery( '.sd-text-color' ), 737 $sdLinkColor = jQuery( '.sd-link-color' ); 738 739 if ( jQuery( 'iframe.admin-bar-likes-widget' ).length > 0 ) { 740 JetpackLikespostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] ); 741 742 stylesData.adminBarStyles = { 743 background: jQuery( '#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a' ).css( 'background' ),
744 isRtl: ( 'rtl' === jQuery( '#wpadminbar' ).css( 'direction' ) ) 745 }; 746 } 747 748 // enable reblogs if we're on a single post page 749 if ( jQuery( 'body' ).hasClass( 'single' ) ) { 750 JetpackLikespostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] ); 751 } 752 753 if ( ! window.addEventListener ) { 754 jQuery( '#wp-admin-bar-admin-bar-likes-widget' ).hide(); 755 } 756 757 stylesData.textStyles = { 758 color: $sdTextColor.css( 'color' ), 759 fontFamily: $sdTextColor.css( 'font-family' ), 760 fontSize: $sdTextColor.css( 'font-size' ), 761 direction: $sdTextColor.css( 'direction' ), 762 fontWeight: $sdTextColor.css( 'font-weight' ), 763 fontStyle: $sdTextColor.css( 'font-style' ), 764 textDecoration: $sdTextColor.css('text-decoration') 765 }; 766 767 stylesData.linkStyles = { 768 color: $sdLinkColor.css('color'), 769 fontFamily: $sdLinkColor.css('font-family'), 770 fontSize: $sdLinkColor.css('font-size'), 771 textDecoration: $sdLinkColor.css('text-decoration'), 772 fontWeight: $sdLinkColor.css( 'font-weight' ), 773 fontStyle: $sdLinkColor.css( 'font-style' ) 774 }; 775 776 JetpackLikespostMessage( stylesData, window.frames[ 'likes-master' ] ); 777 778 JetpackLikesBatchHandler(); 779 780 jQuery( document ).on( 'inview', 'div.jetpack-likes-widget-unloaded', function() { 781 jetpackLikesWidgetQueue.push( this.id ); 782 }); 783 }); 784 } 785 786 if ( 'showLikeWidget' === event.event ) { 787 jQuery( '#' + event.id + ' .post-likes-widget-placeholder' ).fadeOut( 'fast', function() { 788 jQuery( '#' + event.id + ' .post-likes-widget' ).fadeIn( 'fast', function() { 789 JetpackLikespostMessage( { event: 'likeWidgetDisplayed', blog_id: event.blog_id, post_id: event.post_id, obj_id: event.obj_id }, window.frames['likes-master'] ); 790 }); 791 }); 792 } 793 794 if ( 'clickReblogFlair' === event.event ) { 795 wpcom_reblog.toggle_reblog_box_flair( event.obj_id ); 796 } 797 798 if ( 'showOtherGravatars' === event.event ) { 799 $container = jQuery( '#likes-other-gravatars' ); 800 $list = $container.find( 'ul' ); 801 802 $container.hide(); 803 $list.html( '' ); 804 805 $container.find( '.likes-text span' ).text( event.total ); 806 807 jQuery.each( event.likers, function( i, liker ) { 808 var element = jQuery( '<li><a><img /></a></li>' ); 809 element.addClass( liker.css_class ); 810 811 element.find( 'a' ). 812 attr({ 813 href: liker.profile_URL, 814 rel: 'nofollow', 815 target: '_parent' 816 }). 817 addClass( 'wpl-liker' ); 818 819 element.find( 'img' ). 820 attr({ 821 src: liker.avatar_URL, 822 alt: liker.name 823 }). 824 css({ 825 width: '30px', 826 height: '30px', 827 paddingRight: '3px' 828 }); 829 830 $list.append( element ); 831 } ); 832 833 offset = jQuery( '[name=\'' + event.parent + '\']' ).offset(); 834 835 $container.css( 'left', offset.left + event.position.left - 10 + 'px' ); 836 $container.css( 'top', offset.top + event.position.top - 33 + 'px' ); 837 838 rowLength = Math.floor( event.width / 37 ); 839 height = ( Math.ceil( event.likers.length / rowLength ) * 37 ) + 13; 840 if ( height > 204 ) { 841 height = 204; 842 } 843 844 $container.css( 'height', height + 'px' ); 845 $container.css( 'width', rowLength * 37 - 7 + 'px' ); 846 847 $list.css( 'width', rowLength * 37 + 'px' ); 848 849 $container.fadeIn( 'slow' ); 850 851 scrollbarWidth = $list[0].offsetWidth - $list[0].clientWidth; 852 if ( scrollbarWidth > 0 ) { 853 $container.width( $container.width() + scrollbarWidth ); 854 $list.width( $list.width() + scrollbarWidth ); 855 } 856 } 857} 858 859pm.bind( 'likesMessage', JetpackLikesMessageListener ); 860 861jQuery( document ).click( function( e ) { 862 var $container = jQuery( '#likes-other-gravatars' ); 863 864 if ( $container.has( e.target ).length === 0 ) { 865 $container.fadeOut( 'slow' ); 866 } 867}); 868 869function JetpackLikesWidgetQueueHandler() { 870 var $wrapper, wrapperID, found; 871 if ( ! jetpackLikesMasterReady ) { 872 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 873 return; 874 } 875 876 if ( jetpackLikesWidgetQueue.length > 0 ) { 877 // We may have a widget that needs creating now 878 found = false; 879 while( jetpackLikesWidgetQueue.length > 0 ) { 880 // Grab the first member of the queue that isn't already loading. 881 wrapperID = jetpackLikesWidgetQueue.splice( 0, 1 )[0]; 882 if ( jQuery( '#' + wrapperID ).hasClass( 'jetpack-likes-widget-unloaded' ) ) { 883 found = true; 884 break; 885 } 886 } 887 if ( ! found ) { 888 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 889 return; 890 } 891 } else if ( jQuery( 'div.jetpack-likes-widget-unloaded' ).length > 0 ) { 892 // Grab any unloaded widgets for a batch request 893 JetpackLikesBatchHandler(); 894 895 // Get the next unloaded widget 896 wrapperID = jQuery( 'div.jetpack-likes-widget-unloaded' ).first()[0].id; 897 if ( ! wrapperID ) { 898 // Everything is currently loaded 899 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 900 return; 901 } 902 } 903 904 if ( 'undefined' === typeof wrapperID ) { 905 setTimeout( JetpackLikesWidgetQueueHandler, 500 ); 906 return; 907 } 908 909 $wrapper = jQuery( '#' + wrapperID );
910 $wrapper.find( 'iframe' ).remove(); 911 912 if ( $wrapper.hasClass( 'slim-likes-widget' ) ) { 913 $wrapper.find( '.post-likes-widget-placeholder' ).after( '<iframe class="post-likes-widget jetpack-likes-widget" name="' + $wrapper.data( 'name' ) + '" height="22px" width="68px" frameBorder="0" scrolling="no" src="' + $wrapper.data( 'src' ) + '"></iframe>' ); 914 } else { 915 $wrapper.find( '.post-likes-widget-placeholder' ).after( '<iframe class="post-likes-widget jetpack-likes-widget" name="' + $wrapper.data( 'name' ) + '" height="55px" width="100%" frameBorder="0" src="' + $wrapper.data( 'src' ) + '"></iframe>' ); 916 } 917 918 $wrapper.removeClass( 'jetpack-likes-widget-unloaded' ).addClass( 'jetpack-likes-widget-loading' ); 919 920 $wrapper.find( 'iframe' ).load( function( e ) { 921 var $iframe = jQuery( e.target ); 922 $wrapper.removeClass( 'jetpack-likes-widget-loading' ).addClass( 'jetpack-likes-widget-loaded' ); 923 924 JetpackLikespostMessage( { event: 'loadLikeWidget', name: $iframe.attr( 'name' ), width: $iframe.width(), domain: window.location.hostname }, window.frames[ 'likes-master' ] ); 925 926 if ( $wrapper.hasClass( 'slim-likes-widget' ) ) { 927 $wrapper.find( 'iframe' ).Jetpack( 'resizeable' ); 928 } 929 }); 930 setTimeout( JetpackLikesWidgetQueueHandler, 250 ); 931} 932JetpackLikesWidgetQueueHandler(); 933; 934/* 935 * Swipe 2.0 936 * 937 * Brad Birdsall 938 * Copyright 2013, MIT License 939 * 940*/ 941 942function Swipe(container, options) { 943 944 "use strict"; 945 946 // utilities 947 var noop = function() {}; // simple no operation function 948 var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution 949 950 // check browser capabilities 951 var browser = { 952 addEventListener: !!window.addEventListener, 953 touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, 954 transitions: (function(temp) { 955 var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; 956 for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true; 957 return false; 958 })(document.createElement('swipe')) 959 }; 960 961 // quit if no root element 962 if (!container) return; 963 var element = container.children[0]; 964 var slides, slidePos, width, length; 965 options = options || {}; 966 var index = parseInt(options.startSlide, 10) || 0; 967 var speed = options.speed || 300; 968 options.continuous = options.continuous !== undefined ? options.continuous : true; 969 970 function setup() { 971 972 // cache slides 973 slides = element.children; 974 length = slides.length; 975 976 // set continuous to false if only one slide 977 if (slides.length < 2) options.continuous = false; 978 979 //special case if two slides 980 if (browser.transitions && options.continuous && slides.length < 3) { 981 element.appendChild(slides[0].cloneNode(true)); 982 element.appendChild(element.children[1].cloneNode(true)); 983 slides = element.children; 984 } 985 986 // create an array to store current positions of each slide 987 slidePos = new Array(slides.length); 988 989 // determine width of each slide 990 width = container.getBoundingClientRect().width || container.offsetWidth; 991 992 element.style.width = (slides.length * width) + 'px'; 993 994 // stack elements 995 var pos = slides.length; 996 while(pos--) { 997 998 var slide = slides[pos]; 999 1000 slide.style.width = width + 'px'; 1001 slide.setAttribute('data-index', pos); 1002 1003 if (browser.transitions) { 1004 slide.style.left = (pos * -width) + 'px'; 1005 move(pos, index > pos ? -width : (index < pos ? width : 0), 0); 1006 } 1007 1008 } 1009 1010 // reposition elements before and after index 1011 if (options.continuous && browser.transitions) { 1012 move(circle(index-1), -width, 0); 1013 move(circle(index+1), width, 0); 1014 } 1015 1016 if (!browser.transitions) element.style.left = (index * -width) + 'px'; 1017 1018 container.style.visibility = 'visible'; 1019 1020 } 1021 1022 function prev() { 1023 1024 if (options.continuous) slide(index-1); 1025 else if (index) slide(index-1); 1026 1027 } 1028 1029 function next() { 1030 1031 if (options.continuous) slide(index+1); 1032 else if (index < slides.length - 1) slide(index+1); 1033 1034 } 1035 1036 function circle(index) { 1037 1038 // a simple positive modulo using slides.length 1039 return (slides.length + (index % slides.length)) % slides.length; 1040 1041 } 1042 1043 function slide(to, slideSpeed) { 1044 1045 // do nothing if already on requested slide 1046 if (index == to) return; 1047 1048 if (browser.transitions) { 1049 1050 var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward 1051 1052 // get the actual position of the slide 1053 if (options.continuous) { 1054 var natural_direction = direction; 1055 direction = -slidePos[circle(to)] / width; 1056 1057 // if going forward but to < index, use to = slides.length + to 1058 // if going backward but to > index, use to = -slides.length + to 1059 if (direction !== natural_direction) to = -direction * slides.length + to; 1060 1061 } 1062 1063 var diff = Math.abs(index-to) - 1; 1064 1065 // move all the slides between index and to in the right direction 1066 while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0); 1067 1068 to = circle(to); 1069 1070 move(index, width * direction, slideSpeed || speed); 1071 move(to, 0, slideSpeed || speed); 1072 1073 if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place 1074 1075 } else { 1076 1077 to = circle(to); 1078 animate(index * -width, to * -width, slideSpeed || speed); 1079 //no fallback for a circular continuous if the browser does not accept transitions 1080 } 1081 1082 index = to; 1083 offloadFn(options.callback && options.callback(index, slides[index])); 1084 } 1085 1086 function move(index, dist, speed) { 1087 1088 translate(index, dist, speed); 1089 slidePos[index] = dist; 1090 1091 } 1092 1093 function translate(index, dist, speed) { 1094 1095 var slide = slides[index]; 1096 var style = slide && slide.style; 1097 1098 if (!style) return; 1099 1100 style.webkitTransitionDuration = 1101 style.MozTransitionDuration = 1102 style.msTransitionDuration = 1103 style.OTransitionDuration = 1104 style.transitionDuration = speed + 'ms'; 1105 1106 style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)'; 1107 style.msTransform = 1108 style.MozTransform = 1109 style.OTransform = 'translateX(' + dist + 'px)'; 1110 1111 } 1112 1113 function animate(from, to, speed) { 1114 1115 // if not an animation, just reposition 1116 if (!speed) { 1117 1118 element.style.left = to + 'px'; 1119 return; 1120 1121 } 1122 1123 var start = +new Date; 1124 1125 var timer = setInterval(function() { 1126 1127 var timeElap = +new Date - start; 1128 1129 if (timeElap > speed) { 1130 1131 element.style.left = to + 'px'; 1132 1133 if (delay) begin(); 1134 1135 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 1136 1137 clearInterval(timer); 1138 return; 1139 1140 } 1141 1142 element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; 1143 1144 }, 4); 1145 1146 } 1147 1148 // setup auto slideshow
1149 var delay = options.auto || 0; 1150 var interval; 1151 1152 function begin() { 1153 1154 interval = setTimeout(next, delay); 1155 1156 } 1157 1158 function stop() { 1159 1160 delay = 0; 1161 clearTimeout(interval); 1162 1163 } 1164 1165 1166 // setup initial vars 1167 var start = {}; 1168 var delta = {}; 1169 var isScrolling; 1170 1171 // setup event capturing 1172 var events = { 1173 1174 handleEvent: function(event) { 1175 1176 switch (event.type) { 1177 case 'touchstart': this.start(event); break; 1178 case 'touchmove': this.move(event); break; 1179 case 'touchend': offloadFn(this.end(event)); break; 1180 case 'webkitTransitionEnd': 1181 case 'msTransitionEnd': 1182 case 'oTransitionEnd': 1183 case 'otransitionend': 1184 case 'transitionend': offloadFn(this.transitionEnd(event)); break; 1185 case 'resize': offloadFn(setup.call()); break; 1186 } 1187 1188 if (options.stopPropagation) event.stopPropagation(); 1189 1190 }, 1191 start: function(event) { 1192 1193 var touches = event.touches[0]; 1194 1195 // measure start values 1196 start = { 1197 1198 // get initial touch coords 1199 x: touches.pageX, 1200 y: touches.pageY, 1201 1202 // store time to determine touch duration 1203 time: +new Date 1204 1205 }; 1206 1207 // used for testing first move event 1208 isScrolling = undefined; 1209 1210 // reset delta and end measurements 1211 delta = {}; 1212 1213 // attach touchmove and touchend listeners 1214 element.addEventListener('touchmove', this, false); 1215 element.addEventListener('touchend', this, false); 1216 1217 }, 1218 move: function(event) { 1219 1220 // ensure swiping with one touch and not pinching 1221 if ( event.touches.length > 1 || event.scale && event.scale !== 1) return 1222 1223 if (options.disableScroll) event.preventDefault(); 1224 1225 var touches = event.touches[0]; 1226 1227 // measure change in x and y 1228 delta = { 1229 x: touches.pageX - start.x, 1230 y: touches.pageY - start.y 1231 } 1232 1233 // determine if scrolling test has run - one time test 1234 if ( typeof isScrolling == 'undefined') { 1235 isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); 1236 } 1237 1238 // if user is not trying to scroll vertically 1239 if (!isScrolling) { 1240 1241 // prevent native scrolling 1242 event.preventDefault(); 1243 1244 // stop slideshow 1245 stop(); 1246 1247 // increase resistance if first or last slide 1248 if (options.continuous) { // we don't add resistance at the end 1249 1250 translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0); 1251 translate(index, delta.x + slidePos[index], 0); 1252 translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0); 1253 1254 } else { 1255 1256 delta.x = 1257 delta.x / 1258 ( (!index && delta.x > 0 // if first slide and sliding left 1259 || index == slides.length - 1 // or if last slide and sliding right 1260 && delta.x < 0 // and if sliding at all 1261 ) ? 1262 ( Math.abs(delta.x) / width + 1 ) // determine resistance level 1263 : 1 ); // no resistance if false 1264 1265 // translate 1:1 1266 translate(index-1, delta.x + slidePos[index-1], 0); 1267 translate(index, delta.x + slidePos[index], 0); 1268 translate(index+1, delta.x + slidePos[index+1], 0); 1269 } 1270 1271 } 1272 1273 }, 1274 end: function(event) { 1275 1276 // measure duration 1277 var duration = +new Date - start.time; 1278 1279 // determine if slide attempt triggers next/prev slide 1280 var isValidSlide = 1281 Number(duration) < 250 // if slide duration is less than 250ms 1282 && Math.abs(delta.x) > 20 // and if slide amt is greater than 20px 1283 || Math.abs(delta.x) > width/2; // or if slide amt is greater than half the width 1284 1285 // determine if slide attempt is past start and end 1286 var isPastBounds = 1287 !index && delta.x > 0 // if first slide and slide amt is greater than 0 1288 || index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0 1289 1290 if (options.continuous) isPastBounds = false;
1291 1292 // determine direction of swipe (true:right, false:left) 1293 var direction = delta.x < 0; 1294 1295 // if not scrolling vertically 1296 if (!isScrolling) { 1297 1298 if (isValidSlide && !isPastBounds) { 1299 1300 if (direction) { 1301 1302 if (options.continuous) { // we need to get the next in this direction in place 1303 1304 move(circle(index-1), -width, 0); 1305 move(circle(index+2), width, 0); 1306 1307 } else { 1308 move(index-1, -width, 0); 1309 } 1310 1311 move(index, slidePos[index]-width, speed); 1312 move(circle(index+1), slidePos[circle(index+1)]-width, speed); 1313 index = circle(index+1); 1314 1315 } else { 1316 if (options.continuous) { // we need to get the next in this direction in place 1317 1318 move(circle(index+1), width, 0); 1319 move(circle(index-2), -width, 0); 1320 1321 } else { 1322 move(index+1, width, 0); 1323 } 1324 1325 move(index, slidePos[index]+width, speed); 1326 move(circle(index-1), slidePos[circle(index-1)]+width, speed); 1327 index = circle(index-1); 1328 1329 } 1330 1331 options.callback && options.callback(index, slides[index]); 1332 1333 } else { 1334 1335 if (options.continuous) { 1336 1337 move(circle(index-1), -width, speed); 1338 move(index, 0, speed); 1339 move(circle(index+1), width, speed); 1340 1341 } else { 1342 1343 move(index-1, -width, speed); 1344 move(index, 0, speed); 1345 move(index+1, width, speed); 1346 } 1347 1348 } 1349 1350 } 1351 1352 // kill touchmove and touchend event listeners until touchstart called again 1353 element.removeEventListener('touchmove', events, false) 1354 element.removeEventListener('touchend', events, false) 1355 1356 }, 1357 transitionEnd: function(event) { 1358 1359 if (parseInt(event.target.getAttribute('data-index'), 10) == index) { 1360 1361 if (delay) begin(); 1362 1363 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 1364 1365 } 1366 1367 } 1368 1369 } 1370 1371 // trigger setup 1372 setup(); 1373 1374 // start auto slideshow if applicable 1375 if (delay) begin(); 1376 1377 1378 // add event listeners 1379 if (browser.addEventListener) { 1380 1381 // set touchstart event on element 1382 if (browser.touch) element.addEventListener('touchstart', events, false); 1383 1384 if (browser.transitions) { 1385 element.addEventListener('webkitTransitionEnd', events, false); 1386 element.addEventListener('msTransitionEnd', events, false); 1387 element.addEventListener('oTransitionEnd', events, false); 1388 element.addEventListener('otransitionend', events, false); 1389 element.addEventListener('transitionend', events, false); 1390 } 1391 1392 // set resize event on window 1393 window.addEventListener('resize', events, false); 1394 1395 } else { 1396 1397 window.onresize = function () { setup() }; // to play nice with old IE 1398 1399 } 1400 1401 // expose the Swipe API 1402 return { 1403 setup: function() { 1404 1405 setup(); 1406 1407 }, 1408 slide: function(to, speed) { 1409 1410 // cancel slideshow 1411 stop(); 1412 1413 slide(to, speed); 1414 1415 }, 1416 prev: function() { 1417 1418 // cancel slideshow 1419 stop(); 1420 1421 prev(); 1422 1423 }, 1424 next: function() { 1425 1426 // cancel slideshow 1427 stop(); 1428 1429 next(); 1430 1431 }, 1432 getPos: function() { 1433 1434 // return current index position 1435 return index; 1436 1437 }, 1438 getNumSlides: function() { 1439 1440 // return total number of slides 1441 return length; 1442 }, 1443 kill: function() { 1444 1445 // cancel slideshow 1446 stop(); 1447 1448 // reset element 1449 element.style.width = 'auto'; 1450 element.style.left = 0; 1451 1452 // reset slides 1453 var pos = slides.length; 1454 while(pos--) { 1455 1456 var slide = slides[pos]; 1457 slide.style.width = '100%'; 1458 slide.style.left = 0; 1459 1460 if (browser.transitions) translate(pos, 0, 0); 1461 1462 } 1463 1464 // removed event listeners 1465 if (browser.addEventListener) { 1466 1467 // remove current event listeners 1468 element.removeEventListener('touchstart', events, false); 1469 element.removeEventListener('webkitTransitionEnd', events, false); 1470 element.removeEventListener('msTransitionEnd', events, false); 1471 element.removeEventListener('oTransitionEnd', events, false); 1472 element.removeEventListener('otransitionend', events, false); 1473 element.removeEventListener('transitionend', events, false); 1474 window.removeEventListener('resize', events, false); 1475 1476 } 1477 else { 1478 1479 window.onresize = null; 1480 1481 } 1482 1483 } 1484 } 1485 1486} 1487 1488 1489if ( window.jQuery || window.Zepto ) { 1490 (function($) { 1491 $.fn.Swipe = function(params) { 1492 return this.each(function() { 1493 $(this).data('Swipe', new Swipe($(this)[0], params)); 1494 }); 1495 } 1496 })( window.jQuery || window.Zepto ) 1497} 1498; 1499/** 1500 * Comment Likes - JavaScript 1501 * 1502 * This handles liking and unliking comments, as well as viewing who has 1503 * liked a particular comment. 1504 * 1505 * @dependency jQuery 1506 * @dependency Swipe 1507 * 1508 * @package Comment_Likes 1509 * @subpackage JavaScript 1510 */ 1511jQuery( function() { 1512 var $ = jQuery; 1513 var extWin; 1514 var extWinCheck; 1515 var commentLikeEvent; 1516 1517 // The O2 theme re-injects this script into the DOM when somebody 1518 // creates a new thread and the page is already open, but we don't 1519 // want to run this script a second time. 1520 if ( window.comment_likes_loaded ) return; 1521 window.comment_likes_loaded = true; 1522 1523 // Client-side cache of who liked a particular comment to avoid 1524 // having to hit the server multiple times for the same data. 1525 var comment_like_cache = {}; 1526 1527 /** 1528 * Parse the comment ID from a comment like link. 1529 */ 1530 var get_comment_id = function( $link ) { 1531 var comment_id = $link.attr( 'href' ).split( 'like_comment=' ); 1532 return comment_id[1].split( '&_wpnonce=' )[0]; 1533 }; 1534 1535 /** 1536 * Handle an ajax action on the comment like link. 1537 */ 1538 var handle_link_action = function( $link, action, comment_id, callback ) { 1539 var nonce = $link.attr( 'href' ).split( '_wpnonce=' )[1]; 1540 1541 $.post( '/wp-admin/admin-ajax.php', { 1542 'action': action, 1543 '_wpnonce': nonce, 1544 'like_comment': comment_id, 1545 'blog_id': Number( $link.data( 'blog' ) ) 1546 }, callback, 'json' ); 1547 }; 1548 1549 // Overlay used for displaying comment like info.
1550 var overlay = { 1551 1552 // Overlay element. 1553 $el: $( '<div/>' ) 1554 .appendTo( 'body' ) 1555 .addClass( 'comment-likes-overlay' ) 1556 .hide() 1557 .mouseenter( function() { 1558 // Don't hide the overlay if the user is mousing over it. 1559 overlay.cancel_hide(); 1560 } ).mouseleave( function() { 1561 overlay.request_hide(); 1562 } ), 1563 1564 // Inner contents of overlay. 1565 $inner: null, 1566 1567 // Instance of the Swipe library. 1568 swipe: null, 1569 1570 // Initialise the overlay for use, removing any old content. 1571 clear: function() { 1572 // Unload any previous instance of Swipe (to avoid leaking a global 1573 // event handler). This is done before clearing the contents of the 1574 // overlay because Swipe expects the slides to still be present. 1575 if ( this.swipe ) { 1576 this.swipe.kill(); 1577 this.swipe = null; 1578 } 1579 this.$el.html( '' ); 1580 this.$inner = $( '<div/>' ).addClass( 'inner' ).appendTo( this.$el ); 1581 }, 1582 1583 /** 1584 * Construct a list (<ul>) of user (gravatar, name) details. 1585 * 1586 * @param data liker data returned from the server 1587 * @param klass CSS class to apply to the <ul> element 1588 * @param start index of user to start at 1589 * @param length number of users to include in the list 1590 * 1591 * @return HTML for the list 1592 */ 1593 get_user_bits: function( data, klass, start, length ) { 1594 start = start || 0; 1595 var last = start + ( length || data.length ); 1596 last = ( last > data.length ) ? data.length : last; 1597 var html = '<div class="liker-list"><ul class="' + ( klass || '' ) + '">'; 1598 for ( var i = start; i < last; ++i ) { 1599 var user = data[ i ]; 1600 html += '<li>'; 1601 html += '<a rel="nofollow" title="' + user.display_name_esc + '" href="' + user.profile_url_esc + '"><img src="' + user.avatar_url_esc + '" alt="' + user.display_name_esc + '" /> <span class="user-name">' + user.display_name_esc + '</span></a>'; 1602 html += '</li>'; 1603 } 1604 html += '</ul></div>'; 1605 return html; 1606 }, 1607 1608 /** 1609 * Render the display of who has liked this comment. The type of 1610 * display depends on how many people have liked the comment. 1611 * If more than 10 people have liked the comment, this function 1612 * renders navigation controls and sets up the Swipe library for 1613 * changing between pages. 1614 * 1615 * @param link the element over which the user is hovering 1616 * @param data the results retrieved from the server 1617 */ 1618 show_likes: function( $link, data ) { 1619 this.clear(); 1620 1621 $link.data( 'likeCount', data.length ); 1622 if ( 0 === data.length ) { 1623 // No likers after all. 1624 return this.$el.hide(); 1625 } 1626 1627 this.$inner.css( 'padding', 12 ); 1628 1629 if ( data.length < 6 ) { 1630 // Only one column needed. 1631 this.$inner.css( 'max-width', 200 ); 1632 this.$inner.html( this.get_user_bits( data, 'single' ) ); 1633 1634 } else if ( data.length < 11 ) { 1635 // Two columns, but only one page. 1636 this.$inner.html( this.get_user_bits( data, 'double' ) ); 1637 1638 } else { 1639 // Multiple pages. 1640 this.render_likes_with_pagination( data ); 1641 } 1642 1643 // Move the overlay into the correct position and then show it. 1644 this.set_position( $link ); 1645 }, 1646 1647 /** 1648 * Render multiple pages of likes with pagination controls. 1649 * This function is intended to be called by `show_likes` above. 1650 * 1651 * @param data the results retrieved from the server 1652 */ 1653 render_likes_with_pagination: function( data ) { 1654 var page_count = Math.ceil( data.length / 10 ); 1655 // Swipe requires two nested containers. 1656 var $swipe = $( '<div/>' ).addClass( 'swipe' ).appendTo( this.$inner ); 1657 var $div = $( '<div/>' ).addClass( 'swipe-wrap' ).appendTo( $swipe ); 1658 for ( var i = 0; i < page_count; ++i ) { 1659 $( this.get_user_bits( data, 'double', i * 10, 10 ) ).appendTo( $div ); 1660 } 1661 1662 /** Navigation controls. 1663 * This is based on the Newdash controls found in 1664 * reader/recommendations-templates.php 1665 */ 1666 var nav_html = '<nav class="slider-nav"><a href="#" class="prev"><span class="noticon noticon-previous" title="Previous" alt="<"></span></a><span class="position">'; 1667 for ( var i = 0; i < page_count; ++i ) { 1668 nav_html += '<em data-page="' + i + '" class="' + ( ( i === 0 ) ? 'on' : '' ) + '">•</em>'; 1669 } 1670 nav_html += '</span><a href="#" class="next"><span class="noticon noticon-next" title="Next" alt=">"></span></a></nav>';
1671 var $nav = $( nav_html ).appendTo( this.$inner ); 1672 1673 /** Set up Swipe. **/ 1674 1675 // Swipe cannot be set up successfully unless its container 1676 // is visible, so we show it now. 1677 this.$el.show(); 1678 1679 var swipe = this.swipe = new Swipe( $swipe[0], { 1680 callback: function( pos ) { 1681 // Update the pagination indicators. 1682 // 1683 // If there are exactly two pages, Swipe has a weird 1684 // special case where it duplicates both pages and 1685 // can return index 2 and 3 even though those aren't 1686 // real pages (see swipe.js, line 47). To deal with 1687 // this, we use the expression `pos % page_count`. 1688 pos = pos % page_count; 1689 $nav.find( 'em' ).each( function() { 1690 var page = Number( $( this ).data( 'page' ) ); 1691 $( this ).attr( 'class', ( pos === page ) ? 'on' : '' ); 1692 } ); 1693 } 1694 } ); 1695 $nav.find( 'em' ).on( 'click', function( $e ) { 1696 // Go to the page corresponding to the indicator clicked. 1697 swipe.slide( Number( $( this ).data( 'page' ) ) ); 1698 $e.preventDefault(); 1699 } ); 1700 // Previous and next buttons. 1701 $nav.find( '.prev' ).on( 'click', function( $e ) { 1702 swipe.prev(); 1703 $e.preventDefault(); 1704 } ); 1705 $nav.find( '.next' ).on( 'click', function( $e ) { 1706 swipe.next(); 1707 $e.preventDefault(); 1708 } ); 1709 }, 1710 1711 /** 1712 * Open the overlay and show a loading message. 1713 */ 1714 show_loading_message: function( $link ) { 1715 this.clear(); 1716 this.$inner.text( comment_like_text.loading ); 1717 this.set_position( $link ); 1718 }, 1719 1720 /** 1721 * Position the overlay near the current comment. 1722 * 1723 * @param $link element near which to position the overlay 1724 */ 1725 set_position: function( $link ) { 1726 // Prepare a down arrow icon for the bottom of the overlay. 1727 var $icon = $( '<span/>' ) 1728 .appendTo( this.$el ) 1729 .addClass( 'icon noticon noticon-downarrow' ) 1730 .css( 'text-shadow', '0px 1px 1px rgb(223, 223, 223)' ); 1731 1732 var offset = $link.offset(); 1733 var left = offset.left - ( this.$el.width() - $link.width() ) / 2; 1734 left = left < 5 ? 5 : left; 1735 var top = offset.top - this.$el.height() + 5; 1736 1737 // Check if the overlay would appear off the screen. 1738 if ( top < ( $( window ).scrollTop() + ( $( '#wpadminbar' ).height() || 0 ) ) ) { 1739 // We'll display the overlay beneath the link instead. 1740 top = offset.top + $link.height(); 1741 // Instead of using the down arrow icon, use an up arrow. 1742 $icon.remove().prependTo( this.$el ) 1743 .removeClass( 'noticon-downarrow') 1744 .addClass( 'noticon-uparrow' ) 1745 .css( { 1746 'text-shadow': '0px -1px 1px rgb(223, 223, 223)', 1747 'vertical-align': 'bottom' 1748 } ); 1749 } 1750 1751 this.$el.css( { 'left': left, 'top': top } ).show(); 1752 1753 $icon.css( { 1754 // The height of the arrow icon differs slightly between browsers, 1755 // so we compute the margin here to make sure it isn't disjointed 1756 // from the overlay. 1757 'margin-top': $icon[0].scrollHeight - 26, 1758 'margin-bottom': 20 - $icon[0].scrollHeight, 1759 1760 // Position the arrow to be horizontally centred on the link. 1761 'padding-left': offset.left - left + ( $link.width() - $icon[0].scrollWidth ) / 2 1762 } ); 1763 }, 1764 1765 /** 1766 * Return whether the overlay is visible. 1767 */ 1768 is_visible: function() { 1769 return ( 'none' !== this.$el.css( 'display' ) ); 1770 }, 1771 1772 // Timeout used for hiding the overlay. 1773 hide_timeout: null, 1774 1775 /** 1776 * Request that the overlay be hidden after a short delay. 1777 */ 1778 request_hide: function() { 1779 if ( null !== this.hide_timeout ) { 1780 return; 1781 } 1782 var self = this; 1783 this.hide_timeout = setTimeout( function() { 1784 self.$el.hide(); 1785 self.clear(); 1786 }, 300 ); 1787 }, 1788 1789 /** 1790 * Cancel a request to hide the overlay. 1791 */ 1792 cancel_hide: function() { 1793 if ( null !== this.hide_timeout ) { 1794 clearTimeout( this.hide_timeout ); 1795 this.hide_timeout = null; 1796 } 1797 } 1798 }; 1799 1800 // The most recent comment for which the user has requested to see 1801 // who liked it. 1802 var relevant_comment; 1803 1804 // Precache after this timeout. 1805 var precache_timeout = null; 1806 1807 /** 1808 * Fetch the like data for a particular comment. 1809 */ 1810 var fetch_like_data = function( $link, comment_id ) { 1811 comment_like_cache[ comment_id ] = null; 1812 1813 var $star = $link.parent().parent().find( 'a.comment-like-link' ); 1814 handle_link_action( $star, 'view_comment_likes', comment_id, function( data ) { 1815 // Populate the cache. 1816 comment_like_cache[ comment_id ] = data; 1817 1818 // Only show the overlay if the user is interested. 1819 if ( overlay.is_visible() && ( relevant_comment === comment_id ) ) { 1820 overlay.show_likes( $link, data ); 1821 } 1822 } ); 1823 }; 1824 1825 function readCookie( c ) { 1826 var nameEQ = c + '=', 1827 cookieStrings = document.cookie.split( ';' ), 1828 i, cookieString, num, chunk, pairs, pair, cookie_data; 1829 for ( i = 0; i < cookieStrings.length; i++ ) { 1830 cookieString = cookieStrings[ i ]; 1831 while ( cookieString.charAt( 0 ) === ' ' ) { 1832 cookieString = cookieString.substring( 1, cookieString.length ); 1833 } 1834 if ( cookieString.indexOf( nameEQ ) === 0 ) { 1835 chunk = cookieString.substring( nameEQ.length, cookieString.length ); 1836 pairs = chunk.split( '&' ); 1837 cookie_data = {}; 1838 for ( num = pairs.length - 1; num >= 0; num-- ) { 1839 pair = pairs[ num ].split( '=' ); 1840 cookie_data[ pair[0] ] = decodeURIComponent( pair[1] ); 1841 } 1842 return cookie_data; 1843 } 1844 } 1845 return null; 1846 } 1847 1848 function getServiceData() { 1849 var data = readCookie( 'wpc_wpc' ); 1850 if ( null === data || 'undefined' === typeof data.access_token || ! data.access_token ) { 1851 return false;
1852 } 1853 return data; 1854 } 1855 1856 function readMessage( event ) { 1857 if ( 'undefined' == typeof event.event ) { 1858 return; 1859 } 1860 1861 if ( 'login' == event.event && event.success ) { 1862 extWinCheck = setInterval( function() { 1863 if ( ! extWin || extWin.closed ) { 1864 clearInterval( extWinCheck ); 1865 if ( getServiceData() ) { 1866 1867 // Load page in an iframe to get the current comment nonce 1868 var nonceIframe = document.createElement( 'iframe' ); 1869 nonceIframe.id = 'wp-login-comment-nonce-iframe'; 1870 nonceIframe.style.display = 'none'; 1871 nonceIframe.src = commentLikeEvent + ''; 1872 document.body.appendChild( nonceIframe ); 1873 1874 var commentLikeId = ( commentLikeEvent + '' ).split( 'like_comment=' )[1].split( '&_wpnonce=' )[0]; 1875 var c = false; 1876 1877 // Set a 5 second timeout to redirect to the comment page without doing the Like as a fallback 1878 var commentLikeTimeout = setTimeout( function() { 1879 window.location = commentLikeEvent; 1880 }, 5000 ); 1881 1882 // Check for a new nonced redirect and use that if available before timing out 1883 var commentLikeCheck = setInterval( function() { 1884 c = $( '#wp-login-comment-nonce-iframe' ).contents().find( '#comment-like-' + commentLikeId + ' .comment-like-link' ); 1885 if ( 'undefined' !== typeof c && 'undefined' !== typeof c[0] && 'undefined' !== typeof c[0].href ) { 1886 clearTimeout( commentLikeTimeout ); 1887 clearInterval( commentLikeCheck ); 1888 window.location = c[0].href; 1889 } 1890 }, 100 ); 1891 } 1892 } 1893 }, 100 ); 1894 1895 if ( extWin ) { 1896 if ( ! extWin.closed ) { 1897 extWin.close(); 1898 } 1899 extWin = false; 1900 } 1901 1902 $( '#wp-login-polling-iframe' ).remove(); 1903 } 1904 } 1905 1906 if ( 'undefined' != typeof window.pm ) { 1907 pm.bind( 'loginMessage', function( e ) { readMessage( e ); } ); 1908 } 1909 1910 $( 'body' ).on( 'click', 'a.comment-like-link', function( $e ) { 1911 if ( $( $e.target ).hasClass( 'needs-login' ) ) { 1912 $e.preventDefault(); 1913 commentLikeEvent = $e.target; 1914 if ( extWin ) { 1915 if ( ! extWin.closed ) { 1916 extWin.close(); 1917 } 1918 extWin = false; 1919 } 1920 1921 $( '#wp-login-polling-iframe' ).remove(); 1922 1923 var url = 'https://wordpress.com/public.api/connect/?action=request&service=wordpress'; 1924 extWin = window.open( url, 'likeconn', 'status=0,toolbar=0,location=1,menubar=0,directories=0,resizable=1,scrollbars=1,height=560,width=500' ); 1925 1926 // Append cookie polling login iframe to this window to wait for user to finish logging in (or cancel) 1927 var loginIframe = $( "<iframe id='wp-login-polling-iframe'></iframe>" ); 1928 loginIframe.attr( "src", "https://wordpress.com/public.api/connect/?iframe=true" ); 1929 loginIframe.css( "display", "none" ); 1930 $( document.body ).append( loginIframe ); 1931 1932 return false; 1933 } 1934 1935 // Record that the user likes or does not like this comment. 1936 var $star = $( this ); 1937 var comment_id = get_comment_id( $star ); 1938 $star.addClass( 'loading' ); 1939 // Determine whether to like or unlike based on whether the comment is 1940 // currently liked. 1941 var action = ( $( 'p#comment-like-' + comment_id ).data( 'liked' ) === 'comment-liked' ) ? 'unlike_comment' : 'like_comment'; 1942 handle_link_action( $star, action, comment_id, function( data ) { 1943 // Invalidate the like cache for this comment. 1944 delete comment_like_cache[ comment_id ]; 1945 1946 $( '#comment-like-count-' + data.context ).html( data.display ); 1947 1948 if ( 'like_comment' === action ) { 1949 $( 'p#comment-like-' + data.context ).removeClass( 'comment-not-liked' ) 1950 .addClass( 'comment-liked' ) 1951 .data( 'liked', 'comment-liked' ); 1952 } else { 1953 $( 'p#comment-like-' + data.context ).removeClass( 'comment-liked' ) 1954 .addClass( 'comment-not-liked' ) 1955 .data( 'liked', 'comment-not-liked' ); 1956 } 1957 1958 // Prefetch new data for this comment (if there are likers left). 1959 var $link = $star.parent().find( 'a.view-likers' ); 1960 if ( 0 !== $link.length ) { 1961 fetch_like_data( $link, comment_id ); 1962 } 1963 1964 $star.removeClass( 'loading' ); 1965 } ); 1966 $e.preventDefault(); 1967 $e.stopPropagation(); 1968 1969 } ).on( 'click', 'p.comment-not-liked', function() { 1970 // When a comment hasn't been liked, make the text clickable, too 1971 $( this ).find( 'a.comment-like-link' ).click(); 1972 1973 } ).on( 'mouseenter', 'p.comment-likes a.view-likers', function() { 1974 // Show the user a list of who has liked this comment. 1975 1976 var $link = $( this ); 1977 if ( 0 === Number( $link.data( 'likeCount' ) || 0 ) ) { 1978 // No one has liked this comment. 1979 return; 1980 } 1981 1982 // Don't hide the overlay. 1983 overlay.cancel_hide(); 1984 1985 // Get the comment ID. 1986 var $star = $link.parent().parent().find( 'a.comment-like-link' ); 1987 var comment_id = relevant_comment = get_comment_id( $star ); 1988 1989 // Check if the list of likes for this comment is already in 1990 // the cache. 1991 if ( comment_id in comment_like_cache ) { 1992 var entry = comment_like_cache[ comment_id ];
1993 // Only display the likes if the ajax request is 1994 // actually done. 1995 if ( null !== entry ) { 1996 overlay.show_likes( $link, entry ); 1997 } else { 1998 // Make sure the overlay is visible (in case 1999 // the user moved the mouse away while loading 2000 // but then came back before it finished 2001 // loading). 2002 overlay.show_loading_message( $link ); 2003 } 2004 return; 2005 } 2006 2007 // Position the "Loading..." overlay. 2008 overlay.show_loading_message( $link ); 2009 2010 // Fetch the data. 2011 fetch_like_data( $link, comment_id ); 2012 2013 } ).on( 'mouseleave', 'p.comment-likes a.view-likers', function() { 2014 // User has moved cursor away - hide the overlay. 2015 overlay.request_hide(); 2016 2017 } ).on( 'click', 'p.comment-likes a.view-likers', function( $e ) { 2018 // Don't do anything when clicking on the text. 2019 $e.preventDefault(); 2020 2021 } ).on( 'mouseenter', '.comment:has(a.comment-like-link)', function() { 2022 // User is moving over a comment - precache the comment like data. 2023 if ( null !== precache_timeout ) { 2024 clearTimeout( precache_timeout ); 2025 precache_timeout = null; 2026 } 2027 2028 var $star = $( this ).find( 'a.comment-like-link' ); 2029 var $link = $star.parent().find( 'a.view-likers' ); 2030 if ( 0 === Number( $link.data( 'likeCount' ) || 0 ) ) { 2031 // No likes. 2032 return; 2033 } 2034 var comment_id = get_comment_id( $star ); 2035 if ( comment_id in comment_like_cache ) { 2036 // Already in cache. 2037 return; 2038 } 2039 2040 precache_timeout = setTimeout( function() { 2041 precache_timeout = null; 2042 if ( comment_id in comment_like_cache ) { 2043 // Was cached in the interim. 2044 return; 2045 } 2046 fetch_like_data( $link, comment_id ); 2047 }, 1000 ); 2048 } ); 2049} ); 2050; 2051/** 2052 * File skip-link-focus-fix.js. 2053 * 2054 * Helps with accessibility for keyboard only users. 2055 * 2056 * Learn more: https://git.io/vWdr2 2057 */ 2058( function() { 2059 var isWebkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1, 2060 isOpera = navigator.userAgent.toLowerCase().indexOf( 'opera' ) > -1, 2061 isIe = navigator.userAgent.toLowerCase().indexOf( 'msie' ) > -1; 2062 2063 if ( ( isWebkit || isOpera || isIe ) && document.getElementById && window.addEventListener ) { 2064 window.addEventListener( 'hashchange', function() { 2065 var id = location.hash.substring( 1 ), 2066 element; 2067 2068 if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) { 2069 return; 2070 } 2071 2072 element = document.getElementById( id ); 2073 2074 if ( element ) { 2075 if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) { 2076 element.tabIndex = -1; 2077 } 2078 2079 element.focus(); 2080 } 2081 }, false ); 2082 } 2083})();; 2084/* 2085 * jQuery FlexSlider v2.4.0 2086 * Copyright 2012 WooThemes 2087 * Contributing Author: Tyler Smith 2088 */ 2089; 2090(function ($) { 2091 2092 //FlexSlider: Object Instance 2093 $.flexslider = function(el, options) { 2094 var slider = $(el); 2095 2096 // making variables public 2097 slider.vars = $.extend({}, $.flexslider.defaults, options); 2098 2099 var namespace = slider.vars.namespace, 2100 msGesture = window.navigator && window.navigator.msPointerEnabled && window.MSGesture, 2101 touch = (( "ontouchstart" in window ) || msGesture || window.DocumentTouch && document instanceof DocumentTouch) && slider.vars.touch, 2102 // depricating this idea, as devices are being released with both of these events 2103 //eventType = (touch) ? "touchend" : "click", 2104 eventType = "click touchend MSPointerUp keyup", 2105 watchedEvent = "", 2106 watchedEventClearTimer, 2107 vertical = slider.vars.direction === "vertical", 2108 reverse = slider.vars.reverse, 2109 carousel = (slider.vars.itemWidth > 0), 2110 fade = slider.vars.animation === "fade", 2111 asNav = slider.vars.asNavFor !== "", 2112 methods = {}, 2113 focused = true; 2114 2115 // Store a reference to the slider object 2116 $.data(el, "flexslider", slider); 2117 2118 // Private slider methods 2119 methods = { 2120 init: function() { 2121 slider.animating = false; 2122 // Get current slide and make sure it is a number 2123 slider.currentSlide = parseInt( ( slider.vars.startAt ? slider.vars.startAt : 0), 10 ); 2124 if ( isNaN( slider.currentSlide ) ) { slider.currentSlide = 0; } 2125 slider.animatingTo = slider.currentSlide; 2126 slider.atEnd = (slider.currentSlide === 0 || slider.currentSlide === slider.last); 2127 slider.containerSelector = slider.vars.selector.substr(0,slider.vars.selector.search(' ')); 2128 slider.slides = $(slider.vars.selector, slider); 2129 slider.container = $(slider.containerSelector, slider); 2130 slider.count = slider.slides.length; 2131 // SYNC: 2132 slider.syncExists = $(slider.vars.sync).length > 0; 2133 // SLIDE: 2134 if (slider.vars.animation === "slide") { slider.vars.animation = "swing"; } 2135 slider.prop = (vertical) ? "top" : "marginLeft"; 2136 slider.args = {}; 2137 // SLIDESHOW: 2138 slider.manualPause = false;
vendor: 44,973 bytes, lines 2139-3184
2139 slider.stopped = false; 2140 //PAUSE WHEN INVISIBLE 2141 slider.started = false; 2142 slider.startTimeout = null; 2143 // TOUCH/USECSS: 2144 slider.transitions = !slider.vars.video && !fade && slider.vars.useCSS && (function() { 2145 var obj = document.createElement('div'), 2146 props = ['perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']; 2147 for (var i in props) { 2148 if ( obj.style[ props[i] ] !== undefined ) { 2149 slider.pfx = props[i].replace('Perspective','').toLowerCase(); 2150 slider.prop = "-" + slider.pfx + "-transform"; 2151 return true; 2152 } 2153 } 2154 return false; 2155 }()); 2156 slider.ensureAnimationEnd = ''; 2157 // CONTROLSCONTAINER: 2158 if (slider.vars.controlsContainer !== "") slider.controlsContainer = $(slider.vars.controlsContainer).length > 0 && $(slider.vars.controlsContainer); 2159 // MANUAL: 2160 if (slider.vars.manualControls !== "") slider.manualControls = $(slider.vars.manualControls).length > 0 && $(slider.vars.manualControls); 2161 2162 // RANDOMIZE: 2163 if (slider.vars.randomize) { 2164 slider.slides.sort(function() { return (Math.round(Math.random())-0.5); }); 2165 slider.container.empty().append(slider.slides); 2166 } 2167 2168 slider.doMath(); 2169 2170 // INIT 2171 slider.setup("init"); 2172 2173 // CONTROLNAV: 2174 if (slider.vars.controlNav) { methods.controlNav.setup(); } 2175 2176 // DIRECTIONNAV: 2177 if (slider.vars.directionNav) { methods.directionNav.setup(); } 2178 2179 // KEYBOARD: 2180 if (slider.vars.keyboard && ($(slider.containerSelector).length === 1 || slider.vars.multipleKeyboard)) { 2181 $(document).bind('keyup', function(event) { 2182 var keycode = event.keyCode; 2183 if (!slider.animating && (keycode === 39 || keycode === 37)) { 2184 var target = (keycode === 39) ? slider.getTarget('next') : 2185 (keycode === 37) ? slider.getTarget('prev') : false; 2186 slider.flexAnimate(target, slider.vars.pauseOnAction); 2187 } 2188 }); 2189 } 2190 // MOUSEWHEEL: 2191 if (slider.vars.mousewheel) { 2192 slider.bind('mousewheel', function(event, delta, deltaX, deltaY) { 2193 event.preventDefault(); 2194 var target = (delta < 0) ? slider.getTarget('next') : slider.getTarget('prev'); 2195 slider.flexAnimate(target, slider.vars.pauseOnAction); 2196 }); 2197 } 2198 2199 // PAUSEPLAY 2200 if (slider.vars.pausePlay) { methods.pausePlay.setup(); } 2201 2202 //PAUSE WHEN INVISIBLE 2203 if (slider.vars.slideshow && slider.vars.pauseInvisible) { methods.pauseInvisible.init(); } 2204 2205 // SLIDSESHOW 2206 if (slider.vars.slideshow) { 2207 if (slider.vars.pauseOnHover) { 2208 slider.hover(function() { 2209 if (!slider.manualPlay && !slider.manualPause) { slider.pause(); } 2210 }, function() { 2211 if (!slider.manualPause && !slider.manualPlay && !slider.stopped) { slider.play(); } 2212 }); 2213 } 2214 // initialize animation 2215 //If we're visible, or we don't use PageVisibility API 2216 if(!slider.vars.pauseInvisible || !methods.pauseInvisible.isHidden()) { 2217 (slider.vars.initDelay > 0) ? slider.startTimeout = setTimeout(slider.play, slider.vars.initDelay) : slider.play(); 2218 } 2219 } 2220 2221 // ASNAV: 2222 if (asNav) { methods.asNav.setup(); } 2223 2224 // TOUCH 2225 if (touch && slider.vars.touch) { methods.touch(); } 2226 2227 // FADE&&SMOOTHHEIGHT || SLIDE: 2228 if (!fade || (fade && slider.vars.smoothHeight)) { $(window).bind("resize orientationchange focus", methods.resize); } 2229 2230 slider.find("img").attr("draggable", "false"); 2231 2232 // API: start() Callback 2233 setTimeout(function(){ 2234 slider.vars.start(slider); 2235 }, 200); 2236 }, 2237 asNav: { 2238 setup: function() { 2239 slider.asNav = true; 2240 slider.animatingTo = Math.floor(slider.currentSlide/slider.move); 2241 slider.currentItem = slider.currentSlide; 2242 slider.slides.removeClass(namespace + "active-slide").eq(slider.currentItem).addClass(namespace + "active-slide"); 2243 if(!msGesture){ 2244 slider.slides.on(eventType, function(e){ 2245 e.preventDefault(); 2246 var $slide = $(this), 2247 target = $slide.index(); 2248 var posFromLeft = $slide.offset().left - $(slider).scrollLeft(); // Find position of slide relative to left of slider container 2249 if( posFromLeft <= 0 && $slide.hasClass( namespace + 'active-slide' ) ) { 2250 slider.flexAnimate(slider.getTarget("prev"), true); 2251 } else if (!$(slider.vars.asNavFor).data('flexslider').animating && !$slide.hasClass(namespace + "active-slide")) { 2252 slider.direction = (slider.currentItem < target) ? "next" : "prev"; 2253 slider.flexAnimate(target, slider.vars.pauseOnAction, false, true, true); 2254 } 2255 }); 2256 }else{ 2257 el._slider = slider; 2258 slider.slides.each(function (){ 2259 var that = this; 2260 that._gesture = new MSGesture(); 2261 that._gesture.target = that; 2262 that.addEventListener("MSPointerDown", function (e){ 2263 e.preventDefault(); 2264 if(e.currentTarget._gesture) { 2265 e.currentTarget._gesture.addPointer(e.pointerId); 2266 } 2267 }, false); 2268 that.addEventListener("MSGestureTap", function (e){ 2269 e.preventDefault(); 2270 var $slide = $(this), 2271 target = $slide.index(); 2272 if (!$(slider.vars.asNavFor).data('flexslider').animating && !$slide.hasClass('active')) { 2273 slider.direction = (slider.currentItem < target) ? "next" : "prev"; 2274 slider.flexAnimate(target, slider.vars.pauseOnAction, false, true, true); 2275 } 2276 }); 2277 }); 2278 } 2279 } 2280 }, 2281 controlNav: { 2282 setup: function() { 2283 if (!slider.manualControls) { 2284 methods.controlNav.setupPaging(); 2285 } else { // MANUALCONTROLS: 2286 methods.controlNav.setupManual(); 2287 } 2288 }, 2289 setupPaging: function() { 2290 var type = (slider.vars.controlNav === "thumbnails") ? 'control-thumbs' : 'control-paging', 2291 j = 1, 2292 item, 2293 slide; 2294 2295 slider.controlNavScaffold = $('<ol class="'+ namespace + 'control-nav ' + namespace + type + '"></ol>'); 2296 2297 if (slider.pagingCount > 1) { 2298 for (var i = 0; i < slider.pagingCount; i++) { 2299 slide = slider.slides.eq(i); 2300 2301 if ( undefined === slide.attr( 'data-thumb-alt' ) ) { 2302 slide.attr( 'data-thumb-alt', '' ); 2303 } 2304 2305 item = $( '<a></a>' ).attr( 'href', '#' ).text( j ); 2306 if ( slider.vars.controlNav === "thumbnails" ) { 2307 item = $( '<img/>' ).attr( 'src', slide.attr( 'data-thumb' ) ); 2308 } 2309 2310 if ( '' !== slide.attr( 'data-thumb-alt' ) ) { 2311 item.attr( 'alt', slide.attr( 'data-thumb-alt' ) ); 2312 } 2313 2314 if ( 'thumbnails' === slider.vars.controlNav && true === slider.vars.thumbCaptions ) { 2315 var captn = slide.attr( 'data-thumbcaption' ); 2316 if ( '' !== captn && undefined !== captn ) { 2317 var caption = $('<span></span>' ).addClass( namespace + 'caption' ).text( captn ); 2318 item.append( caption ); 2319 } 2320 } 2321 2322 var liElement = $( '<li>' ); 2323 item.appendTo( liElement ); 2324 liElement.append( '</li>' ); 2325 2326 slider.controlNavScaffold.append(liElement); 2327 j++; 2328 } 2329 } 2330 2331 // CONTROLSCONTAINER: 2332 (slider.controlsContainer) ? $(slider.controlsContainer).append(slider.controlNavScaffold) : slider.append(slider.controlNavScaffold); 2333 methods.controlNav.set(); 2334 2335 methods.controlNav.active(); 2336 2337 slider.controlNavScaffold.delegate('a, img', eventType, function(event) { 2338 event.preventDefault(); 2339 2340 if (watchedEvent === "" || watchedEvent === event.type) { 2341 var $this = $(this), 2342 target = slider.controlNav.index($this); 2343 2344 if (!$this.hasClass(namespace + 'active')) { 2345 slider.direction = (target > slider.currentSlide) ? "next" : "prev"; 2346 slider.flexAnimate(target, slider.vars.pauseOnAction); 2347 } 2348 } 2349 2350 // setup flags to prevent event duplication 2351 if (watchedEvent === "") { 2352 watchedEvent = event.type; 2353 } 2354 methods.setToClearWatchedEvent(); 2355 2356 }); 2357 }, 2358 setupManual: function() { 2359 slider.controlNav = slider.manualControls; 2360 methods.controlNav.active(); 2361 2362 slider.controlNav.bind(eventType, function(event) { 2363 event.preventDefault(); 2364 2365 if (watchedEvent === "" || watchedEvent === event.type) { 2366 var $this = $(this), 2367 target = slider.controlNav.index($this); 2368 2369 if (!$this.hasClass(namespace + 'active')) { 2370 (target > slider.currentSlide) ? slider.direction = "next" : slider.direction = "prev"; 2371 slider.flexAnimate(target, slider.vars.pauseOnAction); 2372 } 2373 } 2374 2375 // setup flags to prevent event duplication 2376 if (watchedEvent === "") { 2377 watchedEvent = event.type; 2378 } 2379 methods.setToClearWatchedEvent(); 2380 }); 2381 }, 2382 set: function() { 2383 var selector = (slider.vars.controlNav === "thumbnails") ? 'img' : 'a'; 2384 slider.controlNav = $('.' + namespace + 'control-nav li ' + selector, (slider.controlsContainer) ? slider.controlsContainer : slider); 2385 }, 2386 active: function() { 2387 slider.controlNav.removeClass(namespace + "active").eq(slider.animatingTo).addClass(namespace + "active"); 2388 }, 2389 update: function(action, pos) { 2390 if (slider.pagingCount > 1 && action === "add") { 2391 slider.controlNavScaffold.append($('<li><a>' + slider.count + '</a></li>')); 2392 } else if (slider.pagingCount === 1) { 2393 slider.controlNavScaffold.find('li').remove(); 2394 } else { 2395 slider.controlNav.eq(pos).closest('li').remove(); 2396 } 2397 methods.controlNav.set(); 2398 (slider.pagingCount > 1 && slider.pagingCount !== slider.controlNav.length) ? slider.update(pos, action) : methods.controlNav.active(); 2399 } 2400 }, 2401 directionNav: { 2402 setup: function() { 2403 var directionNavScaffold = $('<ul class="' + namespace + 'direction-nav"><li class="' + namespace + 'nav-prev"><a class="' + namespace + 'prev" href="#">' + slider.vars.prevText + '</a></li><li class="' + namespace + 'nav-next"><a class="' + namespace + 'next" href="#">' + slider.vars.nextText + '</a></li></ul>'); 2404 2405 // CONTROLSCONTAINER: 2406 if (slider.controlsContainer) { 2407 $(slider.controlsContainer).append(directionNavScaffold); 2408 slider.directionNav = $('.' + namespace + 'direction-nav li a', slider.controlsContainer); 2409 } else { 2410 slider.append(directionNavScaffold); 2411 slider.directionNav = $('.' + namespace + 'direction-nav li a', slider); 2412 } 2413 2414 methods.directionNav.update(); 2415 2416 slider.directionNav.bind(eventType, function(event) { 2417 event.preventDefault(); 2418 var target; 2419 2420 if (watchedEvent === "" || watchedEvent === event.type) { 2421 target = ($(this).hasClass(namespace + 'next')) ? slider.getTarget('next') : slider.getTarget('prev'); 2422 slider.flexAnimate(target, slider.vars.pauseOnAction); 2423 } 2424 2425 // setup flags to prevent event duplication 2426 if (watchedEvent === "") { 2427 watchedEvent = event.type; 2428 } 2429 methods.setToClearWatchedEvent(); 2430 }); 2431 }, 2432 update: function() { 2433 var disabledClass = namespace + 'disabled'; 2434 if (slider.pagingCount === 1) { 2435 slider.directionNav.addClass(disabledClass).attr('tabindex', '-1'); 2436 } else if (!slider.vars.animationLoop) { 2437 if (slider.animatingTo === 0) { 2438 slider.directionNav.removeClass(disabledClass).filter('.' + namespace + "prev").addClass(disabledClass).attr('tabindex', '-1'); 2439 } else if (slider.animatingTo === slider.last) { 2440 slider.directionNav.removeClass(disabledClass).filter('.' + namespace + "next").addClass(disabledClass).attr('tabindex', '-1'); 2441 } else { 2442 slider.directionNav.removeClass(disabledClass).removeAttr('tabindex'); 2443 } 2444 } else { 2445 slider.directionNav.removeClass(disabledClass).removeAttr('tabindex'); 2446 } 2447 } 2448 }, 2449 pausePlay: { 2450 setup: function() { 2451 var pausePlayScaffold = $('<div class="' + namespace + 'pauseplay"><a></a></div>'); 2452 2453 // CONTROLSCONTAINER: 2454 if (slider.controlsContainer) { 2455 slider.controlsContainer.append(pausePlayScaffold); 2456 slider.pausePlay = $('.' + namespace + 'pauseplay a', slider.controlsContainer); 2457 } else { 2458 slider.append(pausePlayScaffold); 2459 slider.pausePlay = $('.' + namespace + 'pauseplay a', slider); 2460 } 2461 2462 methods.pausePlay.update((slider.vars.slideshow) ? namespace + 'pause' : namespace + 'play'); 2463 2464 slider.pausePlay.bind(eventType, function(event) { 2465 event.preventDefault(); 2466 2467 if (watchedEvent === "" || watchedEvent === event.type) { 2468 if ($(this).hasClass(namespace + 'pause')) { 2469 slider.manualPause = true; 2470 slider.manualPlay = false; 2471 slider.pause(); 2472 } else { 2473 slider.manualPause = false; 2474 slider.manualPlay = true; 2475 slider.play(); 2476 } 2477 } 2478 2479 // setup flags to prevent event duplication 2480 if (watchedEvent === "") { 2481 watchedEvent = event.type; 2482 } 2483 methods.setToClearWatchedEvent(); 2484 }); 2485 }, 2486 update: function(state) { 2487 (state === "play") ? slider.pausePlay.removeClass(namespace + 'pause').addClass(namespace + 'play').html(slider.vars.playText) : slider.pausePlay.removeClass(namespace + 'play').addClass(namespace + 'pause').html(slider.vars.pauseText); 2488 } 2489 }, 2490 touch: function() { 2491 var startX, 2492 startY, 2493 offset, 2494 cwidth, 2495 dx, 2496 startT, 2497 scrolling = false, 2498 localX = 0, 2499 localY = 0, 2500 accDx = 0; 2501 2502 if(!msGesture){ 2503 el.addEventListener('touchstart', onTouchStart, false); 2504 2505 function onTouchStart(e) { 2506 if (slider.animating) { 2507 e.preventDefault(); 2508 } else if ( ( window.navigator.msPointerEnabled ) || e.touches.length === 1 ) { 2509 slider.pause(); 2510 // CAROUSEL: 2511 cwidth = (vertical) ? slider.h : slider. w; 2512 startT = Number(new Date()); 2513 // CAROUSEL: 2514 2515 // Local vars for X and Y points. 2516 localX = e.touches[0].pageX; 2517 localY = e.touches[0].pageY; 2518 2519 offset = (carousel && reverse && slider.animatingTo === slider.last) ? 0 : 2520 (carousel && reverse) ? slider.limit - (((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.animatingTo) : 2521 (carousel && slider.currentSlide === slider.last) ? slider.limit : 2522 (carousel) ? ((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.currentSlide : 2523 (reverse) ? (slider.last - slider.currentSlide + slider.cloneOffset) * cwidth : (slider.currentSlide + slider.cloneOffset) * cwidth; 2524 startX = (vertical) ? localY : localX; 2525 startY = (vertical) ? localX : localY; 2526 2527 el.addEventListener('touchmove', onTouchMove, false); 2528 el.addEventListener('touchend', onTouchEnd, false); 2529 } 2530 } 2531 2532 function onTouchMove(e) { 2533 // Local vars for X and Y points. 2534 2535 localX = e.touches[0].pageX; 2536 localY = e.touches[0].pageY; 2537 2538 dx = (vertical) ? startX - localY : startX - localX; 2539 scrolling = (vertical) ? (Math.abs(dx) < Math.abs(localX - startY)) : (Math.abs(dx) < Math.abs(localY - startY)); 2540 2541 var fxms = 500; 2542 2543 if ( ! scrolling || Number( new Date() ) - startT > fxms ) { 2544 e.preventDefault(); 2545 if (!fade && slider.transitions) { 2546 if (!slider.vars.animationLoop) { 2547 dx = dx/((slider.currentSlide === 0 && dx < 0 || slider.currentSlide === slider.last && dx > 0) ? (Math.abs(dx)/cwidth+2) : 1); 2548 } 2549 slider.setProps(offset + dx, "setTouch"); 2550 } 2551 } 2552 } 2553 2554 function onTouchEnd(e) { 2555 // finish the touch by undoing the touch session 2556 el.removeEventListener('touchmove', onTouchMove, false); 2557 2558 if (slider.animatingTo === slider.currentSlide && !scrolling && !(dx === null)) { 2559 var updateDx = (reverse) ? -dx : dx, 2560 target = (updateDx > 0) ? slider.getTarget('next') : slider.getTarget('prev'); 2561 2562 if (slider.canAdvance(target) && (Number(new Date()) - startT < 550 && Math.abs(updateDx) > 50 || Math.abs(updateDx) > cwidth/2)) { 2563 slider.flexAnimate(target, slider.vars.pauseOnAction); 2564 } else { 2565 if (!fade) { slider.flexAnimate(slider.currentSlide, slider.vars.pauseOnAction, true); } 2566 } 2567 } 2568 el.removeEventListener('touchend', onTouchEnd, false); 2569 2570 startX = null; 2571 startY = null; 2572 dx = null; 2573 offset = null; 2574 } 2575 }else{ 2576 el.style.msTouchAction = "none"; 2577 el._gesture = new MSGesture(); 2578 el._gesture.target = el; 2579 el.addEventListener("MSPointerDown", onMSPointerDown, false); 2580 el._slider = slider; 2581 el.addEventListener("MSGestureChange", onMSGestureChange, false); 2582 el.addEventListener("MSGestureEnd", onMSGestureEnd, false); 2583 2584 function onMSPointerDown(e){ 2585 e.stopPropagation(); 2586 if (slider.animating) { 2587 e.preventDefault(); 2588 }else{ 2589 slider.pause(); 2590 el._gesture.addPointer(e.pointerId); 2591 accDx = 0; 2592 cwidth = (vertical) ? slider.h : slider. w; 2593 startT = Number(new Date()); 2594 // CAROUSEL: 2595 2596 offset = (carousel && reverse && slider.animatingTo === slider.last) ? 0 : 2597 (carousel && reverse) ? slider.limit - (((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.animatingTo) : 2598 (carousel && slider.currentSlide === slider.last) ? slider.limit : 2599 (carousel) ? ((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.currentSlide : 2600 (reverse) ? (slider.last - slider.currentSlide + slider.cloneOffset) * cwidth : (slider.currentSlide + slider.cloneOffset) * cwidth; 2601 } 2602 } 2603 2604 function onMSGestureChange(e) { 2605 e.stopPropagation(); 2606 var slider = e.target._slider; 2607 if(!slider){ 2608 return; 2609 } 2610 var transX = -e.translationX, 2611 transY = -e.translationY; 2612 2613 //Accumulate translations. 2614 accDx = accDx + ((vertical) ? transY : transX); 2615 dx = accDx; 2616 scrolling = (vertical) ? (Math.abs(accDx) < Math.abs(-transX)) : (Math.abs(accDx) < Math.abs(-transY)); 2617 2618 if(e.detail === e.MSGESTURE_FLAG_INERTIA){ 2619 setImmediate(function (){ 2620 el._gesture.stop(); 2621 }); 2622 2623 return; 2624 } 2625 2626 if (!scrolling || Number(new Date()) - startT > 500) { 2627 e.preventDefault(); 2628 if (!fade && slider.transitions) { 2629 if (!slider.vars.animationLoop) { 2630 dx = accDx / ((slider.currentSlide === 0 && accDx < 0 || slider.currentSlide === slider.last && accDx > 0) ? (Math.abs(accDx) / cwidth + 2) : 1); 2631 } 2632 slider.setProps(offset + dx, "setTouch"); 2633 } 2634 } 2635 } 2636 2637 function onMSGestureEnd(e) { 2638 e.stopPropagation(); 2639 var slider = e.target._slider; 2640 if(!slider){ 2641 return; 2642 } 2643 if (slider.animatingTo === slider.currentSlide && !scrolling && !(dx === null)) { 2644 var updateDx = (reverse) ? -dx : dx, 2645 target = (updateDx > 0) ? slider.getTarget('next') : slider.getTarget('prev'); 2646 2647 if (slider.canAdvance(target) && (Number(new Date()) - startT < 550 && Math.abs(updateDx) > 50 || Math.abs(updateDx) > cwidth/2)) { 2648 slider.flexAnimate(target, slider.vars.pauseOnAction); 2649 } else { 2650 if (!fade) { slider.flexAnimate(slider.currentSlide, slider.vars.pauseOnAction, true); } 2651 } 2652 } 2653 2654 startX = null; 2655 startY = null; 2656 dx = null; 2657 offset = null; 2658 accDx = 0; 2659 } 2660 } 2661 }, 2662 resize: function() { 2663 if (!slider.animating && slider.is(':visible')) { 2664 if (!carousel) { slider.doMath(); } 2665 2666 if (fade) { 2667 // SMOOTH HEIGHT: 2668 methods.smoothHeight(); 2669 } else if (carousel) { //CAROUSEL: 2670 slider.slides.width(slider.computedW); 2671 slider.update(slider.pagingCount); 2672 slider.setProps(); 2673 } 2674 else if (vertical) { //VERTICAL: 2675 slider.viewport.height(slider.h); 2676 slider.setProps(slider.h, "setTotal"); 2677 } else { 2678 // SMOOTH HEIGHT: 2679 if (slider.vars.smoothHeight) { methods.smoothHeight(); } 2680 slider.newSlides.width(slider.computedW); 2681 slider.setProps(slider.computedW, "setTotal"); 2682 } 2683 } 2684 }, 2685 smoothHeight: function(dur) { 2686 if (!vertical || fade) { 2687 var $obj = (fade) ? slider : slider.viewport; 2688 (dur) ? $obj.animate({"height": slider.slides.eq(slider.animatingTo).height()}, dur) : $obj.height(slider.slides.eq(slider.animatingTo).height()); 2689 } 2690 }, 2691 sync: function(action) { 2692 var $obj = $(slider.vars.sync).data("flexslider"), 2693 target = slider.animatingTo; 2694 2695 switch (action) { 2696 case "animate": $obj.flexAnimate(target, slider.vars.pauseOnAction, false, true); break; 2697 case "play": if (!$obj.playing && !$obj.asNav) { $obj.play(); } break; 2698 case "pause": $obj.pause(); break; 2699 } 2700 }, 2701 uniqueID: function($clone) { 2702 // Append _clone to current level and children elements with id attributes 2703 $clone.filter( '[id]' ).add($clone.find( '[id]' )).each(function() { 2704 var $this = $(this); 2705 $this.attr( 'id', $this.attr( 'id' ) + '_clone' ); 2706 }); 2707 return $clone; 2708 }, 2709 pauseInvisible: { 2710 visProp: null, 2711 init: function() { 2712 var visProp = methods.pauseInvisible.getHiddenProp(); 2713 if (visProp) { 2714 var evtname = visProp.replace(/[H|h]idden/,'') + 'visibilitychange'; 2715 document.addEventListener(evtname, function() { 2716 if (methods.pauseInvisible.isHidden()) { 2717 if(slider.startTimeout) { 2718 clearTimeout(slider.startTimeout); //If clock is ticking, stop timer and prevent from starting while invisible 2719 } else { 2720 slider.pause(); //Or just pause 2721 } 2722 } 2723 else { 2724 if(slider.started) { 2725 slider.play(); //Initiated before, just play 2726 } else { 2727 if (slider.vars.initDelay > 0) { 2728 setTimeout(slider.play, slider.vars.initDelay); 2729 } else { 2730 slider.play(); //Didn't init before: simply init or wait for it 2731 } 2732 } 2733 } 2734 }); 2735 } 2736 }, 2737 isHidden: function() { 2738 var prop = methods.pauseInvisible.getHiddenProp(); 2739 if (!prop) { 2740 return false; 2741 } 2742 return document[prop]; 2743 }, 2744 getHiddenProp: function() { 2745 var prefixes = ['webkit','moz','ms','o']; 2746 // if 'hidden' is natively supported just return it 2747 if ('hidden' in document) { 2748 return 'hidden'; 2749 } 2750 // otherwise loop over all the known prefixes until we find one 2751 for ( var i = 0; i < prefixes.length; i++ ) { 2752 if ((prefixes[i] + 'Hidden') in document) { 2753 return prefixes[i] + 'Hidden'; 2754 } 2755 } 2756 // otherwise it's not supported 2757 return null; 2758 } 2759 }, 2760 setToClearWatchedEvent: function() { 2761 clearTimeout(watchedEventClearTimer); 2762 watchedEventClearTimer = setTimeout(function() { 2763 watchedEvent = ""; 2764 }, 3000); 2765 } 2766 }; 2767 2768 // public methods 2769 slider.flexAnimate = function(target, pause, override, withSync, fromNav) { 2770 if (!slider.vars.animationLoop && target !== slider.currentSlide) { 2771 slider.direction = (target > slider.currentSlide) ? "next" : "prev"; 2772 } 2773 2774 if (asNav && slider.pagingCount === 1) slider.direction = (slider.currentItem < target) ? "next" : "prev"; 2775 2776 if (!slider.animating && (slider.canAdvance(target, fromNav) || override) && slider.is(":visible")) { 2777 if (asNav && withSync) { 2778 var master = $(slider.vars.asNavFor).data('flexslider'); 2779 slider.atEnd = target === 0 || target === slider.count - 1; 2780 master.flexAnimate(target, true, false, true, fromNav); 2781 slider.direction = (slider.currentItem < target) ? "next" : "prev"; 2782 master.direction = slider.direction; 2783 2784 if (Math.ceil((target + 1)/slider.visible) - 1 !== slider.currentSlide && target !== 0) { 2785 slider.currentItem = target; 2786 slider.slides.removeClass(namespace + "active-slide").eq(target).addClass(namespace + "active-slide"); 2787 target = Math.floor(target/slider.visible); 2788 } else { 2789 slider.currentItem = target; 2790 slider.slides.removeClass(namespace + "active-slide").eq(target).addClass(namespace + "active-slide"); 2791 return false; 2792 } 2793 } 2794 2795 slider.animating = true; 2796 slider.animatingTo = target; 2797 2798 // SLIDESHOW: 2799 if (pause) { slider.pause(); } 2800 2801 // API: before() animation Callback 2802 slider.vars.before(slider); 2803 2804 // SYNC: 2805 if (slider.syncExists && !fromNav) { methods.sync("animate"); } 2806 2807 // CONTROLNAV 2808 if (slider.vars.controlNav) { methods.controlNav.active(); } 2809 2810 // !CAROUSEL: 2811 // CANDIDATE: slide active class (for add/remove slide) 2812 if (!carousel) { slider.slides.removeClass(namespace + 'active-slide').eq(target).addClass(namespace + 'active-slide'); } 2813 2814 // INFINITE LOOP: 2815 // CANDIDATE: atEnd 2816 slider.atEnd = target === 0 || target === slider.last; 2817 2818 // DIRECTIONNAV: 2819 if (slider.vars.directionNav) { methods.directionNav.update(); } 2820 2821 if (target === slider.last) { 2822 // API: end() of cycle Callback 2823 slider.vars.end(slider); 2824 // SLIDESHOW && !INFINITE LOOP: 2825 if (!slider.vars.animationLoop) { slider.pause(); } 2826 } 2827 2828 // SLIDE: 2829 if (!fade) { 2830 var dimension = (vertical) ? slider.slides.filter(':first').height() : slider.computedW, 2831 margin, slideString, calcNext; 2832 2833 // INFINITE LOOP / REVERSE: 2834 if (carousel) { 2835 //margin = (slider.vars.itemWidth > slider.w) ? slider.vars.itemMargin * 2 : slider.vars.itemMargin; 2836 margin = slider.vars.itemMargin; 2837 calcNext = ((slider.itemW + margin) * slider.move) * slider.animatingTo; 2838 slideString = (calcNext > slider.limit && slider.visible !== 1) ? slider.limit : calcNext; 2839 } else if (slider.currentSlide === 0 && target === slider.count - 1 && slider.vars.animationLoop && slider.direction !== "next") { 2840 slideString = (reverse) ? (slider.count + slider.cloneOffset) * dimension : 0; 2841 } else if (slider.currentSlide === slider.last && target === 0 && slider.vars.animationLoop && slider.direction !== "prev") { 2842 slideString = (reverse) ? 0 : (slider.count + 1) * dimension; 2843 } else { 2844 slideString = (reverse) ? ((slider.count - 1) - target + slider.cloneOffset) * dimension : (target + slider.cloneOffset) * dimension; 2845 } 2846 slider.setProps(slideString, "", slider.vars.animationSpeed); 2847 if (slider.transitions) { 2848 if (!slider.vars.animationLoop || !slider.atEnd) { 2849 slider.animating = false; 2850 slider.currentSlide = slider.animatingTo; 2851 } 2852 2853 // Unbind previous transitionEnd events and re-bind new transitionEnd event 2854 slider.container.unbind("webkitTransitionEnd transitionend"); 2855 slider.container.bind("webkitTransitionEnd transitionend", function() { 2856 clearTimeout(slider.ensureAnimationEnd); 2857 slider.wrapup(dimension); 2858 }); 2859 2860 // Insurance for the ever-so-fickle transitionEnd event 2861 clearTimeout(slider.ensureAnimationEnd); 2862 slider.ensureAnimationEnd = setTimeout(function() { 2863 slider.wrapup(dimension); 2864 }, slider.vars.animationSpeed + 100); 2865 2866 } else { 2867 slider.container.animate(slider.args, slider.vars.animationSpeed, slider.vars.easing, function(){ 2868 slider.wrapup(dimension); 2869 }); 2870 } 2871 } else { // FADE: 2872 if (!touch) { 2873 //slider.slides.eq(slider.currentSlide).fadeOut(slider.vars.animationSpeed, slider.vars.easing); 2874 //slider.slides.eq(target).fadeIn(slider.vars.animationSpeed, slider.vars.easing, slider.wrapup); 2875 2876 slider.slides.eq(slider.currentSlide).css({"zIndex": 1}).animate({"opacity": 0}, slider.vars.animationSpeed, slider.vars.easing); 2877 slider.slides.eq(target).css({"zIndex": 2}).animate({"opacity": 1}, slider.vars.animationSpeed, slider.vars.easing, slider.wrapup); 2878 2879 } else { 2880 slider.slides.eq(slider.currentSlide).css({ "opacity": 0, "zIndex": 1 }); 2881 slider.slides.eq(target).css({ "opacity": 1, "zIndex": 2 }); 2882 slider.wrapup(dimension); 2883 } 2884 } 2885 // SMOOTH HEIGHT: 2886 if (slider.vars.smoothHeight) { methods.smoothHeight(slider.vars.animationSpeed); } 2887 } 2888 }; 2889 slider.wrapup = function(dimension) { 2890 // SLIDE: 2891 if (!fade && !carousel) { 2892 if (slider.currentSlide === 0 && slider.animatingTo === slider.last && slider.vars.animationLoop) { 2893 slider.setProps(dimension, "jumpEnd"); 2894 } else if (slider.currentSlide === slider.last && slider.animatingTo === 0 && slider.vars.animationLoop) { 2895 slider.setProps(dimension, "jumpStart"); 2896 } 2897 } 2898 slider.animating = false; 2899 slider.currentSlide = slider.animatingTo; 2900 // API: after() animation Callback 2901 slider.vars.after(slider); 2902 }; 2903 2904 // SLIDESHOW: 2905 slider.animateSlides = function() { 2906 if (!slider.animating && focused ) { slider.flexAnimate(slider.getTarget("next")); } 2907 }; 2908 // SLIDESHOW: 2909 slider.pause = function() { 2910 clearInterval(slider.animatedSlides); 2911 slider.animatedSlides = null; 2912 slider.playing = false; 2913 // PAUSEPLAY: 2914 if (slider.vars.pausePlay) { methods.pausePlay.update("play"); } 2915 // SYNC: 2916 if (slider.syncExists) { methods.sync("pause"); } 2917 }; 2918 // SLIDESHOW: 2919 slider.play = function() { 2920 if (slider.playing) { clearInterval(slider.animatedSlides); } 2921 slider.animatedSlides = slider.animatedSlides || setInterval(slider.animateSlides, slider.vars.slideshowSpeed); 2922 slider.started = slider.playing = true; 2923 // PAUSEPLAY: 2924 if (slider.vars.pausePlay) { methods.pausePlay.update("pause"); } 2925 // SYNC: 2926 if (slider.syncExists) { methods.sync("play"); } 2927 }; 2928 // STOP: 2929 slider.stop = function () { 2930 slider.pause(); 2931 slider.stopped = true; 2932 }; 2933 slider.canAdvance = function(target, fromNav) { 2934 // ASNAV: 2935 var last = (asNav) ? slider.pagingCount - 1 : slider.last; 2936 return (fromNav) ? true : 2937 (asNav && slider.currentItem === slider.count - 1 && target === 0 && slider.direction === "prev") ? true : 2938 (asNav && slider.currentItem === 0 && target === slider.pagingCount - 1 && slider.direction !== "next") ? false : 2939 (target === slider.currentSlide && !asNav) ? false : 2940 (slider.vars.animationLoop) ? true : 2941 (slider.atEnd && slider.currentSlide === 0 && target === last && slider.direction !== "next") ? false : 2942 (slider.atEnd && slider.currentSlide === last && target === 0 && slider.direction === "next") ? false : 2943 true; 2944 }; 2945 slider.getTarget = function(dir) { 2946 slider.direction = dir; 2947 if (dir === "next") { 2948 return (slider.currentSlide === slider.last) ? 0 : slider.currentSlide + 1; 2949 } else { 2950 return (slider.currentSlide === 0) ? slider.last : slider.currentSlide - 1; 2951 } 2952 }; 2953 2954 // SLIDE: 2955 slider.setProps = function(pos, special, dur) { 2956 var target = (function() { 2957 var posCheck = (pos) ? pos : ((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.animatingTo, 2958 posCalc = (function() { 2959 if (carousel) { 2960 return (special === "setTouch") ? pos : 2961 (reverse && slider.animatingTo === slider.last) ? 0 : 2962 (reverse) ? slider.limit - (((slider.itemW + slider.vars.itemMargin) * slider.move) * slider.animatingTo) : 2963 (slider.animatingTo === slider.last) ? slider.limit : posCheck; 2964 } else { 2965 switch (special) { 2966 case "setTotal": return (reverse) ? ((slider.count - 1) - slider.currentSlide + slider.cloneOffset) * pos : (slider.currentSlide + slider.cloneOffset) * pos; 2967 case "setTouch": return (reverse) ? pos : pos; 2968 case "jumpEnd": return (reverse) ? pos : slider.count * pos; 2969 case "jumpStart": return (reverse) ? slider.count * pos : pos; 2970 default: return pos; 2971 } 2972 } 2973 }()); 2974 2975 return (posCalc * -1) + "px"; 2976 }()); 2977 2978 if (slider.transitions) { 2979 target = (vertical) ? "translate3d(0," + target + ",0)" : "translate3d(" + target + ",0,0)"; 2980 dur = (dur !== undefined) ? (dur/1000) + "s" : "0s"; 2981 slider.container.css("-" + slider.pfx + "-transition-duration", dur); 2982 slider.container.css("transition-duration", dur); 2983 } 2984 2985 slider.args[slider.prop] = target; 2986 if (slider.transitions || dur === undefined) { slider.container.css(slider.args); } 2987 2988 slider.container.css('transform',target); 2989 }; 2990 2991 slider.setup = function(type) { 2992 // SLIDE: 2993 if (!fade) { 2994 var sliderOffset, arr; 2995 2996 if (type === "init") { 2997 slider.viewport = $('<div class="' + namespace + 'viewport"></div>').css({"overflow": "hidden", "position": "relative"}).appendTo(slider).append(slider.container); 2998 // INFINITE LOOP: 2999 slider.cloneCount = 0; 3000 slider.cloneOffset = 0; 3001 // REVERSE: 3002 if (reverse) { 3003 arr = $.makeArray(slider.slides).reverse(); 3004 slider.slides = $(arr); 3005 slider.container.empty().append(slider.slides); 3006 } 3007 } 3008 // INFINITE LOOP && !CAROUSEL: 3009 if (slider.vars.animationLoop && !carousel) { 3010 slider.cloneCount = 2; 3011 slider.cloneOffset = 1; 3012 // clear out old clones 3013 if (type !== "init") { slider.container.find('.clone').remove(); } 3014 slider.container.append(methods.uniqueID(slider.slides.first().clone().addClass('clone')).attr('aria-hidden', 'true')) 3015 .prepend(methods.uniqueID(slider.slides.last().clone().addClass('clone')).attr('aria-hidden', 'true')); 3016 } 3017 slider.newSlides = $(slider.vars.selector, slider); 3018 3019 sliderOffset = (reverse) ? slider.count - 1 - slider.currentSlide + slider.cloneOffset : slider.currentSlide + slider.cloneOffset; 3020 // VERTICAL: 3021 if (vertical && !carousel) { 3022 slider.container.height((slider.count + slider.cloneCount) * 200 + "%").css("position", "absolute").width("100%"); 3023 setTimeout(function(){ 3024 slider.newSlides.css({"display": "block"}); 3025 slider.doMath(); 3026 slider.viewport.height(slider.h); 3027 slider.setProps(sliderOffset * slider.h, "init"); 3028 }, (type === "init") ? 100 : 0); 3029 } else { 3030 slider.container.width((slider.count + slider.cloneCount) * 200 + "%"); 3031 slider.setProps(sliderOffset * slider.computedW, "init"); 3032 setTimeout(function(){ 3033 slider.doMath(); 3034 slider.newSlides.css({"width": slider.computedW, "float": "left", "display": "block"}); 3035 // SMOOTH HEIGHT: 3036 if (slider.vars.smoothHeight) { methods.smoothHeight(); } 3037 }, (type === "init") ? 100 : 0); 3038 } 3039 } else { // FADE: 3040 slider.slides.css({"width": "100%", "float": "left", "marginRight": "-100%", "position": "relative"}); 3041 if (type === "init") { 3042 if (!touch) { 3043 //slider.slides.eq(slider.currentSlide).fadeIn(slider.vars.animationSpeed, slider.vars.easing); 3044 if (slider.vars.fadeFirstSlide == false) { 3045 slider.slides.css({ "opacity": 0, "display": "block", "zIndex": 1 }).eq(slider.currentSlide).css({"zIndex": 2}).css({"opacity": 1}); 3046 } else { 3047 slider.slides.css({ "opacity": 0, "display": "block", "zIndex": 1 }).eq(slider.currentSlide).css({"zIndex": 2}).animate({"opacity": 1},slider.vars.animationSpeed,slider.vars.easing); 3048 } 3049 } else { 3050 slider.slides.css({ "opacity": 0, "display": "block", "webkitTransition": "opacity " + slider.vars.animationSpeed / 1000 + "s ease", "zIndex": 1 }).eq(slider.currentSlide).css({ "opacity": 1, "zIndex": 2}); 3051 } 3052 } 3053 // SMOOTH HEIGHT: 3054 if (slider.vars.smoothHeight) { methods.smoothHeight(); } 3055 } 3056 // !CAROUSEL: 3057 // CANDIDATE: active slide 3058 if (!carousel) { slider.slides.removeClass(namespace + "active-slide").eq(slider.currentSlide).addClass(namespace + "active-slide"); } 3059 3060 //FlexSlider: init() Callback 3061 slider.vars.init(slider); 3062 }; 3063 3064 slider.doMath = function() { 3065 var slide = slider.slides.first(), 3066 slideMargin = slider.vars.itemMargin, 3067 minItems = slider.vars.minItems, 3068 maxItems = slider.vars.maxItems; 3069 3070 slider.w = (slider.viewport===undefined) ? slider.width() : slider.viewport.width(); 3071 slider.h = slide.height(); 3072 slider.boxPadding = slide.outerWidth() - slide.width(); 3073 3074 // CAROUSEL: 3075 if (carousel) { 3076 slider.itemT = slider.vars.itemWidth + slideMargin; 3077 slider.minW = (minItems) ? minItems * slider.itemT : slider.w; 3078 slider.maxW = (maxItems) ? (maxItems * slider.itemT) - slideMargin : slider.w; 3079 slider.itemW = (slider.minW > slider.w) ? (slider.w - (slideMargin * (minItems - 1)))/minItems : 3080 (slider.maxW < slider.w) ? (slider.w - (slideMargin * (maxItems - 1)))/maxItems : 3081 (slider.vars.itemWidth > slider.w) ? slider.w : slider.vars.itemWidth; 3082 3083 slider.visible = Math.floor(slider.w/(slider.itemW)); 3084 slider.move = (slider.vars.move > 0 && slider.vars.move < slider.visible ) ? slider.vars.move : slider.visible; 3085 slider.pagingCount = Math.ceil(((slider.count - slider.visible)/slider.move) + 1); 3086 slider.last = slider.pagingCount - 1; 3087 slider.limit = (slider.pagingCount === 1) ? 0 : 3088 (slider.vars.itemWidth > slider.w) ? (slider.itemW * (slider.count - 1)) + (slideMargin * (slider.count - 1)) : ((slider.itemW + slideMargin) * slider.count) - slider.w - slideMargin; 3089 } else { 3090 slider.itemW = slider.w; 3091 slider.pagingCount = slider.count; 3092 slider.last = slider.count - 1; 3093 } 3094 slider.computedW = slider.itemW - slider.boxPadding; 3095 }; 3096 3097 slider.update = function(pos, action) { 3098 slider.doMath(); 3099 3100 // update currentSlide and slider.animatingTo if necessary 3101 if (!carousel) { 3102 if (pos < slider.currentSlide) { 3103 slider.currentSlide += 1; 3104 } else if (pos <= slider.currentSlide && pos !== 0) { 3105 slider.currentSlide -= 1; 3106 } 3107 slider.animatingTo = slider.currentSlide; 3108 } 3109 3110 // update controlNav 3111 if (slider.vars.controlNav && !slider.manualControls) { 3112 if ((action === "add" && !carousel) || slider.pagingCount > slider.controlNav.length) { 3113 methods.controlNav.update("add"); 3114 } else if ((action === "remove" && !carousel) || slider.pagingCount < slider.controlNav.length) { 3115 if (carousel && slider.currentSlide > slider.last) { 3116 slider.currentSlide -= 1; 3117 slider.animatingTo -= 1; 3118 } 3119 methods.controlNav.update("remove", slider.last); 3120 } 3121 } 3122 // update directionNav 3123 if (slider.vars.directionNav) { methods.directionNav.update(); } 3124 3125 }; 3126 3127 slider.addSlide = function(obj, pos) { 3128 var $obj = $(obj); 3129 3130 slider.count += 1; 3131 slider.last = slider.count - 1; 3132 3133 // append new slide 3134 if (vertical && reverse) { 3135 (pos !== undefined) ? slider.slides.eq(slider.count - pos).after($obj) : slider.container.prepend($obj); 3136 } else { 3137 (pos !== undefined) ? slider.slides.eq(pos).before($obj) : slider.container.append($obj); 3138 } 3139 3140 // update currentSlide, animatingTo, controlNav, and directionNav 3141 slider.update(pos, "add"); 3142 3143 // update slider.slides 3144 slider.slides = $(slider.vars.selector + ':not(.clone)', slider); 3145 // re-setup the slider to accomdate new slide 3146 slider.setup(); 3147 3148 //FlexSlider: added() Callback 3149 slider.vars.added(slider); 3150 }; 3151 slider.removeSlide = function(obj) { 3152 var pos = (isNaN(obj)) ? slider.slides.index($(obj)) : obj; 3153 3154 // update count 3155 slider.count -= 1; 3156 slider.last = slider.count - 1; 3157 3158 // remove slide 3159 if (isNaN(obj)) { 3160 $(obj, slider.slides).remove(); 3161 } else { 3162 (vertical && reverse) ? slider.slides.eq(slider.last).remove() : slider.slides.eq(obj).remove(); 3163 } 3164 3165 // update currentSlide, animatingTo, controlNav, and directionNav 3166 slider.doMath(); 3167 slider.update(pos, "remove"); 3168 3169 // update slider.slides 3170 slider.slides = $(slider.vars.selector + ':not(.clone)', slider); 3171 // re-setup the slider to accomdate new slide 3172 slider.setup(); 3173 3174 // FlexSlider: removed() Callback 3175 slider.vars.removed(slider); 3176 }; 3177 3178 //FlexSlider: Initialize 3179 methods.init(); 3180 }; 3181 3182 // Ensure the slider isn't focussed if the window loses focus. 3183 $( window ).blur( function ( e ) { 3184 focused = false;
3185 }).focus( function ( e ) { 3186 focused = true; 3187 }); 3188 3189 //FlexSlider: Default Settings 3190 $.flexslider.defaults = { 3191 namespace: "flex-", //{NEW} String: Prefix string attached to the class of every element generated by the plugin 3192 selector: ".slides > li", //{NEW} Selector: Must match a simple pattern. '{container} > {slide}' -- Ignore pattern at your own peril 3193 animation: "fade", //String: Select your animation type, "fade" or "slide" 3194 easing: "swing", //{NEW} String: Determines the easing method used in jQuery transitions. jQuery easing plugin is supported! 3195 direction: "horizontal", //String: Select the sliding direction, "horizontal" or "vertical" 3196 reverse: false, //{NEW} Boolean: Reverse the animation direction 3197 animationLoop: true, //Boolean: Should the animation loop? If false, directionNav will received "disable" classes at either end 3198 smoothHeight: false, //{NEW} Boolean: Allow height of the slider to animate smoothly in horizontal mode 3199 startAt: 0, //Integer: The slide that the slider should start on. Array notation (0 = first slide) 3200 slideshow: true, //Boolean: Animate slider automatically 3201 slideshowSpeed: 7000, //Integer: Set the speed of the slideshow cycling, in milliseconds 3202 animationSpeed: 600, //Integer: Set the speed of animations, in milliseconds 3203 initDelay: 0, //{NEW} Integer: Set an initialization delay, in milliseconds 3204 randomize: false, //Boolean: Randomize slide order 3205 fadeFirstSlide: true, //Boolean: Fade in the first slide when animation type is "fade" 3206 thumbCaptions: false, //Boolean: Whether or not to put captions on thumbnails when using the "thumbnails" controlNav. 3207 3208 // Usability features 3209 pauseOnAction: true, //Boolean: Pause the slideshow when interacting with control elements, highly recommended. 3210 pauseOnHover: false, //Boolean: Pause the slideshow when hovering over slider, then resume when no longer hovering 3211 pauseInvisible: true, //{NEW} Boolean: Pause the slideshow when tab is invisible, resume when visible. Provides better UX, lower CPU usage. 3212 useCSS: true, //{NEW} Boolean: Slider will use CSS3 transitions if available 3213 touch: true, //{NEW} Boolean: Allow touch swipe navigation of the slider on touch-enabled devices 3214 video: false, //{NEW} Boolean: If using video in the slider, will prevent CSS3 3D Transforms to avoid graphical glitches 3215 3216 // Primary Controls 3217 controlNav: true, //Boolean: Create navigation for paging control of each slide? Note: Leave true for manualControls usage 3218 directionNav: true, //Boolean: Create navigation for previous/next navigation? (true/false) 3219 prevText: "Previous", //String: Set the text for the "previous" directionNav item 3220 nextText: "Next", //String: Set the text for the "next" directionNav item 3221 3222 // Secondary Navigation 3223 keyboard: true, //Boolean: Allow slider navigating via keyboard left/right keys 3224 multipleKeyboard: false, //{NEW} Boolean: Allow keyboard navigation to affect multiple sliders. Default behavior cuts out keyboard navigation with more than one slider present. 3225 mousewheel: false, //{UPDATED} Boolean: Requires jquery.mousewheel.js (https://github.com/brandonaaron/jquery-mousewheel) - Allows slider navigating via mousewheel 3226 pausePlay: false, //Boolean: Create pause/play dynamic element 3227 pauseText: "Pause", //String: Set the text for the "pause" pausePlay item 3228 playText: "Play", //String: Set the text for the "play" pausePlay item 3229 3230 // Special properties 3231 controlsContainer: "", //{UPDATED} jQuery Object/Selector: Declare which container the navigation elements should be appended too. Default container is the FlexSlider element. Example use would be $(".flexslider-container"). Property is ignored if given element is not found. 3232 manualControls: "", //{UPDATED} jQuery Object/Selector: Declare custom control navigation. Examples would be $(".flex-control-nav li") or "#tabs-nav li img", etc. The number of elements in your controlNav should match the number of slides/tabs. 3233 sync: "", //{NEW} Selector: Mirror the actions performed on this slider with another slider. Use with care. 3234 asNavFor: "", //{NEW} Selector: Internal property exposed for turning the slider into a thumbnail navigation for another slider 3235 3236 // Carousel Options 3237 itemWidth: 0, //{NEW} Integer: Box-model width of individual carousel items, including horizontal borders and padding. 3238 itemMargin: 0, //{NEW} Integer: Margin between carousel items. 3239 minItems: 1, //{NEW} Integer: Minimum number of carousel items that should be visible. Items will resize fluidly when below this. 3240 maxItems: 0, //{NEW} Integer: Maxmimum number of carousel items that should be visible. Items will resize fluidly when above this limit. 3241 move: 0, //{NEW} Integer: Number of carousel items that should move on animation. If 0, slider will move all visible items. 3242 allowOneSlide: true, //{NEW} Boolean: Whether or not to allow a slider comprised of a single slide 3243 3244 // Callback API 3245 start: function(){}, //Callback: function(slider) - Fires when the slider loads the first slide 3246 before: function(){}, //Callback: function(slider) - Fires asynchronously with each slider animation 3247 after: function(){}, //Callback: function(slider) - Fires after each slider animation completes 3248 end: function(){}, //Callback: function(slider) - Fires when the slider reaches the last slide (asynchronous) 3249 added: function(){}, //{NEW} Callback: function(slider) - Fires after a slide is added 3250 removed: function(){}, //{NEW} Callback: function(slider) - Fires after a slide is removed 3251 init: function() {} //{NEW}
3251 Callback: function(slider) - Fires after the slider is initially setup 3252 }; 3253 3254 //FlexSlider: Plugin Function 3255 $.fn.flexslider = function(options) { 3256 if (options === undefined) { options = {}; } 3257 3258 if (typeof options === "object") { 3259 return this.each(function() { 3260 var $this = $(this), 3261 selector = (options.selector) ? options.selector : ".slides > li", 3262 $slides = $this.find(selector); 3263 3264 if ( ( $slides.length === 1 && options.allowOneSlide === true ) || $slides.length === 0 ) { 3265 $slides.fadeIn(400); 3266 if (options.start) { options.start($this); } 3267 } else if ($this.data('flexslider') === undefined) { 3268 new $.flexslider(this, options); 3269 } 3270 }); 3271 } else { 3272 // Helper strings to quickly perform functions on the slider 3273 var $slider = $(this).data('flexslider'); 3274 switch (options) { 3275 case "play": $slider.play(); break; 3276 case "pause": $slider.pause(); break; 3277 case "stop": $slider.stop(); break; 3278 case "next": $slider.flexAnimate($slider.getTarget("next"), true); break; 3279 case "prev": 3280 case "previous": $slider.flexAnimate($slider.getTarget("prev"), true); break; 3281 default: if (typeof options === "number") { $slider.flexAnimate(options, true); } 3282 } 3283 } 3284 }; 3285})(jQuery); 3286; 3287/* 3288 By Osvaldas Valutis, www.osvaldas.info 3289 Available for use under the MIT License 3290*/ 3291 3292 3293 3294;(function( $, window, document, undefined ) 3295{ 3296 $.fn.doubleTapToGo = function( params ) 3297 { 3298 if( !( 'ontouchstart' in window ) && 3299 !navigator.msMaxTouchPoints && 3300 !navigator.userAgent.toLowerCase().match( /windows phone os 7/i ) ) return false; 3301 3302 this.each( function() 3303 { 3304 var curItem = false; 3305 3306 $( this ).on( 'click', function( e ) 3307 { 3308 var item = $( this ); 3309 if( item[ 0 ] != curItem[ 0 ] ) 3310 { 3311 e.preventDefault(); 3312 curItem = item; 3313 } 3314 }); 3315 3316 $( document ).on( 'click touchstart MSPointerDown', function( e ) 3317 { 3318 var resetItem = true, 3319 parents = $( e.target ).parents(); 3320 3321 for( var i = 0; i < parents.length; i++ ) 3322 if( parents[ i ] == curItem[ 0 ] ) 3323 resetItem = false; 3324 3325 if( resetItem ) 3326 curItem = false; 3327 }); 3328 }); 3329 return this; 3330 }; 3331})( jQuery, window, document );; 3332/** 3333 * global.js 3334 * 3335 * Handles various UI actions 3336 * - Mobile menu 3337 * - Search form show/hide 3338 * - Touch device dropdowns (requires: doubletaptogo.js) 3339 * - Flexslider (requires: flexslider.js) 3340 * - Scroll to top 3341 * - Smooth page load 3342 * 3343 * @package Rowling 3344 */ 3345( function( $ ) { 3346 3347/** 3348 * On ready 3349 */ 3350 $( document ).ready( function() { 3351 var windowWidth = $( window ).width(); 3352 3353 3354 // Toggle mobile menu 3355 $( ".nav-toggle" ).on( "click", function() { 3356 $( this ).toggleClass( "active" ); 3357 $( ".header-bottom-menu" ).slideToggle(); 3358 if ( $( ".search-toggle" ).hasClass( "active" ) ) { 3359 $( ".search-toggle" ).removeClass( "active" ); 3360 $( ".search-container" ).slideToggle(); 3361 } 3362 return false; 3363 }); 3364 3365 3366 // Toggle search container 3367 $( ".search-toggle" ).on( "click", function() { 3368 $( this ).toggleClass( "active" ); 3369 $( ".search-container" ).slideToggle(); 3370 if ( $( ".nav-toggle" ).hasClass( "active" ) ) { 3371 $( ".nav-toggle" ).removeClass( "active" ); 3372 $( ".header-bottom-menu" ).slideToggle(); 3373 } 3374 return false; 3375 }); 3376 3377 $( window ).resize( function() { 3378 3379 // Prevent the Android keyboard from triggering the following code 3380 if ( windowWidth !== $( window ).width() ) { 3381 3382 // Hide mobile menu/search container at resize 3383 if ( $(window).width() >= 851 ) { 3384 $( ".nav-toggle" ).removeClass( "active" ); 3385 $( ".header-bottom-menu" ).show(); 3386 } 3387 3388 if ( $(window).width() <= 850 ) { 3389 $( ".search-toggle" ).removeClass( "active" ); 3390 $( ".search-container" ).hide(); 3391 } 3392 3393 // Dropdown menus on touch devices, excluding mobile menu 3394 if ( $(window).width() <= 851 ) { 3395 $( ".header-bottom-menu li:has(ul)" ).doubleTapToGo(); 3396 $( ".header-top-menu li:has(ul)" ).doubleTapToGo(); 3397 } 3398 3399 if ( $(window).width() >= 850 ) { 3400 $( ".header-bottom-menu li:has(ul)" ).unbind(); 3401 $( ".header-top-menu li:has(ul)" ).unbind(); 3402 } 3403 } 3404 3405 windowWidth = $( window ).width(); 3406 3407 }).resize(); 3408 3409 // Load Flexslider 3410 $( ".flexslider" ).flexslider( { 3411 animation: "slide", 3412 controlNav: false, 3413 prevText: "", 3414 nextText: "", 3415 smoothHeight: true 3416 }); 3417 3418 // smooth scroll to the top 3419 $( ".to-the-top" ).click( function() { 3420 $( "html, body" ).animate( { scrollTop: 0 }, 500 ); 3421 return false;
3422 }); 3423 3424 }); 3425 3426 3427 /** 3428 * On load 3429 */ 3430 $( document.body ).on( 'post-load', function () { 3431 3432 // Load Flexslider after new posts have been added to the page. 3433 $( ".flexslider" ).flexslider( { 3434 animation: "slide", 3435 controlNav: false, 3436 prevText: "", 3437 nextText: "", 3438 smoothHeight: true 3439 }); 3440 3441 }); 3442 3443} ) ( jQuery ); 3444; 3445window.addComment=function(a){function b(){c(),g()}function c(a){if(t&&(m=j(r.cancelReplyId),n=j(r.commentFormId),m)){m.addEventListener("touchstart",e),m.addEventListener("click",e);for(var b,c=d(a),g=0,h=c.length;g<h;g++)b=c[g],b.addEventListener("touchstart",f),b.addEventListener("click",f)}}function d(a){var b,c=r.commentReplyClass;return a&&a.childNodes||(a=q),b=q.getElementsByClassName?a.getElementsByClassName(c):a.querySelectorAll("."+c)}function e(a){var b=this,c=r.temporaryFormId,d=j(c);d&&o&&(j(r.parentIdFieldId).value="0",d.parentNode.replaceChild(o,d),b.style.display="none",a.preventDefault())}function f(b){var c,d=this,e=i(d,"belowelement"),f=i(d,"commentid"),g=i(d,"respondelement"),h=i(d,"postid");e&&f&&g&&h&&(c=a.addComment.moveForm(e,f,g,h),!1===c&&b.preventDefault())}function g(){if(s){var a={childList:!0,subTree:!0};p=new s(h),p.observe(q.body,a)}}function h(a){for(var b=a.length;b--;)if(a[b].addedNodes.length)return void c()}function i(a,b){return u?a.dataset[b]:a.getAttribute("data-"+b)}function j(a){return q.getElementById(a)}function k(b,c,d,e){var f=j(b);o=j(d);var g,h,i,k=j(r.parentIdFieldId),p=j(r.postIdFieldId);if(f&&o&&k){l(o),e&&p&&(p.value=e),k.value=c,m.style.display="",f.parentNode.insertBefore(o,f.nextSibling),m.onclick=function(){return!1};try{for(var s=0;s<n.elements.length;s++)if(g=n.elements[s],h=!1,"getComputedStyle"in a?i=a.getComputedStyle(g):q.documentElement.currentStyle&&(i=g.currentStyle),(g.offsetWidth<=0&&g.offsetHeight<=0||"hidden"===i.visibility)&&(h=!0),"hidden"!==g.type&&!g.disabled&&!h){g.focus();break}}catch(t){}return!1}}function l(a){var b=r.temporaryFormId,c=j(b);c||(c=q.createElement("div"),c.id=b,c.style.display="none",a.parentNode.insertBefore(c,a))}var m,n,o,p,q=a.document,r={commentReplyClass:"comment-reply-link",cancelReplyId:"cancel-comment-reply-link",commentFormId:"commentform",temporaryFormId:"wp-temp-form-div",parentIdFieldId:"comment_parent",postIdFieldId:"comment_post_ID"},s=a.MutationObserver||a.WebKitMutationObserver||a.MozMutationObserver,t="querySelector"in q&&"addEventListener"in a,u=!!q.documentElement.dataset;return t&&"loading"!==q.readyState?b():t&&a.addEventListener("DOMContentLoaded",b,!1),{init:c,moveForm:k}}(window);; 3446/* global FB, jpfbembed */ 3447( function( window ) { 3448 var facebookEmbed = function() { 3449 var fbroot, src; 3450 3451 if ( 'undefined' !== typeof FB && FB.XFBML ) { 3452 FB.XFBML.parse(); 3453 } else { 3454 fbroot = document.createElement( 'div' ); 3455 fbroot.id = 'fb-root'; 3456 document.getElementsByTagName( 'body' )[ 0 ].appendChild( fbroot ); 3457 3458 src = 'https://connect.facebook.net/' + jpfbembed.locale + '/sdk.js#xfbml=1'; 3459 if ( jpfbembed.appid ) { 3460 src += '&appId=' + jpfbembed.appid; 3461 } 3462 src += '&version=v2.3'; 3463 jQuery.getScript( src ); 3464 } 3465 }; 3466 3467 window.fbAsyncInit = function() { 3468 FB.init( { 3469 appId: jpfbembed.appid, 3470 version: 'v2.3', 3471 } ); 3472 3473 FB.XFBML.parse(); 3474 }; 3475 3476 if ( 'undefined' !== typeof infiniteScroll ) { 3477 jQuery( document.body ).on( 'post-load', facebookEmbed ); 3478 } 3479 3480 facebookEmbed(); 3481} )( this ); 3482; 3483/*** 3484 * Warning: This file is remotely enqueued in Jetpack's Masterbar module. 3485 * Changing it will also affect Jetpack sites. 3486 */ 3487jQuery( document ).ready( function( $, wpcom ) { 3488 var masterbar, 3489 menupops = $( 'li#wp-admin-bar-blog.menupop, li#wp-admin-bar-newdash.menupop, li#wp-admin-bar-my-account.menupop' ), 3490 newmenu = $( '#wp-admin-bar-new-post-types' ); 3491 3492 // Unbind hoverIntent, we want clickable menus. 3493 menupops 3494 .unbind( 'mouseenter mouseleave' ) 3495 .removeProp( 'hoverIntent_t' ) 3496 .removeProp( 'hoverIntent_s' ) 3497 .on( 'mouseover', function(e) { 3498 var li = $(e.target).closest( 'li.menupop' ); 3499 menupops.not(li).removeClass( 'ab-hover' ); 3500 li.toggleClass( 'ab-hover' ); 3501 } ) 3502 .on( 'click touchstart', function(e) { 3503 var $target = $( e.target ); 3504 3505 if ( masterbar.focusSubMenus( $target ) ) { 3506 return; 3507 } 3508 3509 e.preventDefault(); 3510 masterbar.toggleMenu( $target ); 3511 } ); 3512 3513 masterbar = { 3514 focusSubMenus: function( $target ) { 3515 // Handle selection of menu items 3516 if ( ! $target.closest( 'ul' ).hasClass( 'ab-top-menu' ) ) { 3517 $target 3518 .closest( 'li' ); 3519 3520 return true; 3521 } 3522 3523 return false;
3524 }, 3525 3526 toggleMenu: function( $target ) { 3527 var $li = $target.closest( 'li.menupop' ), 3528 $html = $( 'html' ); 3529 3530 $( 'body' ).off( 'click.ab-menu' ); 3531 $( '#wpadminbar li.menupop' ).not($li).removeClass( 'ab-active wpnt-stayopen wpnt-show' ); 3532 3533 if ( $li.hasClass( 'ab-active' ) ) { 3534 $li.removeClass( 'ab-active' ); 3535 $html.removeClass( 'ab-menu-open' ); 3536 } else { 3537 $li.addClass( 'ab-active' ); 3538 $html.addClass( 'ab-menu-open' ); 3539 3540 $( 'body' ).on( 'click.ab-menu', function( e ) { 3541 if ( ! $( e.target ).parents( '#wpadminbar' ).length ) { 3542 e.preventDefault(); 3543 masterbar.toggleMenu( $li ); 3544 $( 'body' ).off( 'click.ab-menu' ); 3545 } 3546 } ); 3547 } 3548 } 3549 }; 3550} );; 3551/*globals JSON */ 3552( function( $ ) { 3553 var eventName = 'wpcom_masterbar_click'; 3554 3555 var linksTracksEvents = { 3556 //top level items 3557 'wp-admin-bar-blog' : 'my_sites', 3558 'wp-admin-bar-newdash' : 'reader', 3559 'wp-admin-bar-ab-new-post' : 'write_button', 3560 'wp-admin-bar-my-account' : 'my_account', 3561 'wp-admin-bar-notes' : 'notifications', 3562 //my sites - top items 3563 'wp-admin-bar-switch-site' : 'my_sites_switch_site', 3564 'wp-admin-bar-blog-info' : 'my_sites_site_info', 3565 'wp-admin-bar-site-view' : 'my_sites_view_site', 3566 'wp-admin-bar-blog-stats' : 'my_sites_site_stats', 3567 'wp-admin-bar-plan' : 'my_sites_plan', 3568 'wp-admin-bar-plan-badge' : 'my_sites_plan_badge', 3569 //my sites - manage 3570 'wp-admin-bar-edit-page' : 'my_sites_manage_site_pages', 3571 'wp-admin-bar-new-page-badge' : 'my_sites_manage_add_page', 3572 'wp-admin-bar-edit-post' : 'my_sites_manage_blog_posts', 3573 'wp-admin-bar-new-post-badge' : 'my_sites_manage_add_post', 3574 'wp-admin-bar-edit-attachment' : 'my_sites_manage_media', 3575 'wp-admin-bar-new-attachment-badge' : 'my_sites_manage_add_media', 3576 'wp-admin-bar-comments' : 'my_sites_manage_comments', 3577 'wp-admin-bar-edit-jetpack-testimonial' : 'my_sites_manage_testimonials', 3578 'wp-admin-bar-new-jetpack-testimonial' : 'my_sites_manage_add_testimonial', 3579 'wp-admin-bar-edit-jetpack-portfolio' : 'my_sites_manage_portfolio', 3580 'wp-admin-bar-new-jetpack-portfolio' : 'my_sites_manage_add_portfolio', 3581 //my sites - personalize 3582 'wp-admin-bar-themes' : 'my_sites_personalize_themes', 3583 'wp-admin-bar-cmz' : 'my_sites_personalize_themes_customize', 3584 //my sites - configure 3585 'wp-admin-bar-sharing' : 'my_sites_configure_sharing', 3586 'wp-admin-bar-people' : 'my_sites_configure_people', 3587 'wp-admin-bar-people-add' : 'my_sites_configure_people_add_button', 3588 'wp-admin-bar-plugins' : 'my_sites_configure_plugins', 3589 'wp-admin-bar-domains' : 'my_sites_configure_domains', 3590 'wp-admin-bar-domains-add' : 'my_sites_configure_add_domain', 3591 'wp-admin-bar-blog-settings' : 'my_sites_configure_settings', 3592 'wp-admin-bar-legacy-dashboard' : 'my_sites_configure_wp_admin', 3593 //reader 3594 'wp-admin-bar-followed-sites' : 'reader_followed_sites', 3595 'wp-admin-bar-reader-followed-sites-manage': 'reader_manage_followed_sites', 3596 'wp-admin-bar-discover-discover' : 'reader_discover', 3597 'wp-admin-bar-discover-search' : 'reader_search', 3598 'wp-admin-bar-my-activity-my-likes' : 'reader_my_likes', 3599 //account 3600 'wp-admin-bar-user-info' : 'my_account_user_name', 3601 // account - profile 3602 'wp-admin-bar-my-profile' : 'my_account_profile_my_profile', 3603 'wp-admin-bar-account-settings' : 'my_account_profile_account_settings', 3604 'wp-admin-bar-billing' : 'my_account_profile_manage_purchases', 3605 'wp-admin-bar-security' : 'my_account_profile_security', 3606 'wp-admin-bar-notifications' : 'my_account_profile_notifications', 3607 //account - special 3608 'wp-admin-bar-get-apps' : 'my_account_special_get_apps', 3609 'wp-admin-bar-next-steps' : 'my_account_special_next_steps', 3610 'wp-admin-bar-help' : 'my_account_special_help', 3611 }; 3612 3613 var notesTracksEvents = { 3614 openSite: function( data ) { 3615 return { 3616 clicked: 'masterbar_notifications_panel_site', 3617 site_id: data.siteId 3618 }; 3619 }, 3620 openPost: function( data ) { 3621 return { 3622 clicked: 'masterbar_notifications_panel_post', 3623 site_id: data.siteId, 3624 post_id: data.postId 3625 }; 3626 }, 3627 openComment: function( data ) { 3628 return { 3629 clicked: 'masterbar_notifications_panel_comment', 3630 site_id: data.siteId, 3631 post_id: data.postId, 3632 comment_id: data.commentId 3633 }; 3634 } 3635 }; 3636 3637 function recordTracksEvent( eventProps ) { 3638 eventProps = eventProps || {}; 3639 window._tkq = window._tkq || []; 3640 window._tkq.push( [ 'recordEvent', eventName, eventProps ] ); 3641 } 3642 3643 function parseJson( s, defaultValue ) { 3644 try { 3645 return JSON.parse( s ); 3646 } catch ( e ) { 3647 return defaultValue; 3648 } 3649 } 3650 3651 $( document ).ready( function() { 3652 var trackableLinks = '.mb-trackable .ab-item:not(div),' + 3653 '#wp-admin-bar-notes .ab-item,' + 3654 '#wp-admin-bar-user-info .ab-item,' + 3655 '.mb-trackable .ab-secondary'; 3656 3657 $( trackableLinks ).on( 'click touchstart', function( e ) { 3658 var $target = $( e.target ), 3659 $parent = $target.closest( 'li' ); 3660 3661 if ( ! $parent ) { 3662 return; 3663 } 3664 3665 var trackingId = $target.attr( 'ID' ) || $parent.attr( 'ID' ); 3666 3667 if ( ! linksTracksEvents.hasOwnProperty( trackingId ) ) { 3668 return; 3669 } 3670 3671 var eventProps = { 'clicked': linksTracksEvents[ trackingId ] }; 3672 3673 recordTracksEvent( eventProps ); 3674 } ); 3675 } ); 3676 3677 // listen for postMessage events from the notifications iframe 3678 $( window ).on( 'message', function( e ) { 3679 var event = ! e.data && e.originalEvent.data ? e.originalEvent : e; 3680 if ( event.origin !== 'https://widgets.wp.com' ) { 3681 return; 3682 } 3683 3684 var data = ( 'string' === typeof event.data ) ? parseJson( event.data, {} ) : event.data; 3685 if ( 'notesIframeMessage' !== data.type ) { 3686 return; 3687 } 3688 3689 var eventData = notesTracksEvents[ data.action ]; 3690 if ( ! eventData ) { 3691 return; 3692 } 3693 3694 recordTracksEvent( eventData( data ) ); 3695 } ); 3696 3697} )( jQuery ); 3698; 3699var wpcom = window.wpcom || {}; 3700wpcom.actionbar = {}; 3701wpcom.actionbar.data = actionbardata; 3702 3703// This might be better in another file, but is here for now 3704(function($){ 3705 var fbd = wpcom.actionbar.data, 3706 d = document, 3707 docHeight = $( d ).height(), 3708 b = d.getElementsByTagName( 'body' )[0], 3709 lastScrollTop = 0, 3710 lastScrollDir, fb, fhtml, fbhtml, fbHtmlLi, 3711 followingbtn, followbtn, fbdf, action, 3712 slkhtml = '', foldhtml = '', reporthtml = '', 3713 customizeIcon, editIcon, statsIcon, themeHtml = '', signupHtml = '', loginHtml = '', 3714 viewReaderHtml = '', editSubsHtml = '', editFollowsHtml = '', 3715 toggleactionbar, $actionbar; 3716 3717 // Don't show actionbar when iframed 3718 if ( window != window.top ) { 3719 return; 3720 } 3721 3722 fhtml = '<ul>'; 3723 3724 // Customize Icon 3725 customizeIcon = '<svg class="gridicon gridicon__customize" height="20px" width="20px" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M2 6c0-1.505.78-3.08 2-4 0 .845.69 2 2 2 1.657 0 3 1.343 3 3 0 .386-.08.752-.212 1.09.74.594 1.476 1.19 2.19 1.81L8.9 11.98c-.62-.716-1.214-1.454-1.807-2.192C6.753 9.92 6.387 10 6 10c-2.21 0-4-1.79-4-4zm12.152 6.848l1.34-1.34c.607.304 1.283.492 2.008.492 2.485 0 4.5-2.015 4.5-4.5 0-.725-.188-1.4-.493-2.007L18 9l-2-2 3.507-3.507C18.9 3.188 18.225 3 17.5 3 15.015 3 13 5.
3725015 13 7.5c0 .725.188 1.4.493 2.007L3 20l2 2 6.848-6.848c1.885 1.928 3.874 3.753 5.977 5.45l1.425 1.148 1.5-1.5-1.15-1.425c-1.695-2.103-3.52-4.092-5.448-5.977z" data-reactid=".2.1.1:0.1b.0"></path></g></svg>'; 3726 3727 if ( fbd.canCustomizeSite && fbd.isLoggedIn ) { 3728 fhtml += '<li class="actnbr-btn actnbr-customize"><a href="'+ fbd.customizeLink +'">' + customizeIcon + '<span>' + fbd.i18n.customize + '<span></a></li>'; 3729 } 3730 3731 // Edit Icon 3732 editIcon = '<svg class="gridicon gridicon__pencil" height="20px" width="20px" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M13 6l5 5-9.507 9.507c-.686-.686-.69-1.794-.012-2.485l-.002-.003c-.69.676-1.8.673-2.485-.013-.677-.677-.686-1.762-.036-2.455l-.008-.008c-.694.65-1.78.64-2.456-.036L13 6zm7.586-.414l-2.172-2.172c-.78-.78-2.047-.78-2.828 0L14 5l5 5 1.586-1.586c.78-.78.78-2.047 0-2.828zM3 18v3h3c0-1.657-1.343-3-3-3z"></path></g></svg>'; 3733 3734 if ( fbd.canEditPost ) { 3735 fhtml += '<li class="actnbr-btn actnbr-edit"><a href="'+ fbd.editLink +'">' + editIcon + '<span>' + fbd.i18n.edit + '</span></a></li>'; 3736 } 3737 3738 // Stats Icon 3739 statsIcon = '<svg class="gridicon gridicon__stats-alt" height="20px" width="20px" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M21,21H3v-2h18V21z M8,10H4v7h4V10z M14,3h-4v14h4V3z M20,6h-4v11h4V6z"/></path></g></svg>'; 3740 3741 if ( fbd.canEditPost ) { 3742 fhtml += '<li class="actnbr-btn actnbr-stats"><a href="'+ fbd.statsLink +'">' + statsIcon + '<span>' + fbd.i18n.stats + '</span></a></li>'; 3743 } 3744 3745 // Follow/Unfollow Icon 3746 followingbtn = '<svg class="gridicon gridicon__following" height="24px" width="24px" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M23 13.482L15.508 21 12 17.4l1.412-1.388 2.106 2.188 6.094-6.094L23 13.482zm-7.455 1.862L20 10.89V2H2v14c0 1.1.9 2 2 2h4.538l4.913-4.832 2.095 2.176zM8 13H4v-1h4v1zm3-2H4v-1h7v1zm0-2H4V8h7v1zm7-3H4V4h14v2z"/></g></svg>'; 3747 followbtn = '<svg class="gridicon gridicon__follow" height="24px" width="24px" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M23 16v2h-3v3h-2v-3h-3v-2h3v-3h2v3h3zM20 2v9h-4v3h-3v4H4c-1.1 0-2-.9-2-2V2h18zM8 13v-1H4v1h4zm3-3H4v1h7v-1zm0-2H4v1h7V8zm7-4H4v2h14V4z"/></g></svg>'; 3748 3749 fbhtml = '<a class="actnbr-action actnbr-actn-follow" href="">' + followbtn + '<span>' + fbd.i18n.follow + '</span></a>'; 3750 if ( fbd.isFollowing ) { 3751 fbhtml = '<a class="actnbr-action actnbr-actn-following" href="">' + followingbtn + '<span>' + fbd.i18n.following + '</span></a>'; 3752 } 3753 3754 // Show follow/unfollow icon on top level, when this is not your own site 3755 if ( fbd.canFollow && ! ( fbd.canEditPost || fbd.canCustomizeSite ) ) { 3756 fhtml += '<li class="actnbr-btn actnbr-hidden"> \ 3757 ' + fbhtml + ' \ 3758 <div class="actnbr-popover tip tip-top-left actnbr-notice"> \ 3759 <div class="tip-arrow"></div> \ 3760 <div class="tip-inner actnbr-follow-bubble"></div> \ 3761 </div> \ 3762 </li>'; 3763 } 3764 3765 if ( ! fbd.canCustomizeSite ) { 3766 // Report Link 3767 reporthtml = '<li class="flb-report"><a href="https://en.wordpress.com/abuse/">' + fbd.i18n.report + '</a></li>'; 3768 } 3769 3770 // Show shortlink on single posts 3771 if ( fbd.isSingular ) { 3772 slkhtml = '<li class="actnbr-shortlink"><a href="' + fbd.shortlink + '">' + fbd.i18n.shortlink + '</a></li>' 3773 } 3774 3775 // Set up fold/unfold menu item 3776 foldhtml = '<li class="actnbr-fold"><a href="">' + fbd.i18n.foldBar + '</a></li>' 3777 if ( fbd.isFolded ) { 3778 foldhtml = '<li class="actnbr-fold"><a href="">' + fbd.i18n.unfoldBar + '</a></li>' 3779 } 3780 if ( ! fbd.isLoggedIn && ! fbd.canFollow ) { 3781 foldhtml = ''; 3782 } 3783 3784 if ( fbd.isLoggedIn ) { 3785 if ( '' != fbd.themeURL ) { 3786 themeHtml = '<li class="actnbr-theme"><a href="' + fbd.themeURL + '">' + fbd.i18n.themeInfo.replace( /{theme}/, fbd.themeName ) + '</a></li>'; 3787 } 3788 if ( fbd.canFollow ) { 3789 if ( fbd.isSingular ) { 3790 viewReaderHtml = '<li class="actnbr-reader"><a href="https://wordpress.com/read/blogs/' + fbd.siteID + '/posts/' + fbd.postID +'">' + fbd.i18n.viewReadPost + '</a></li>'; 3791 } else { 3792 viewReaderHtml = '<li class="actnbr-reader"><a href="https://wordpress.com/read/' + ( fbd.feedID ? 'feeds/' + fbd.feedID : 'blogs/' + fbd.siteID ) + '">' + fbd.i18n.viewReader + '</a></li>'; 3793 } 3794 } 3795 editFollowsHtml = '<li class="actnbr-follows"><a href="https://wordpress.com/following/edit">' + fbd.i18n.editSubs + '</a></li>'; 3796 } else { 3797 loginHtml += '<li class="actnbr-login"><a href="' + fbd.loginURL + '">' + fbd.i18n.login + '</a></li>'; 3798 signupHtml = '<li class="actnbr-signup"><a href="' + fbd.signupURL + '">' + fbd.i18n.signup + '</a></li>'; 3799 editSubsHtml = '<li class="actnbr-subs"><a href="https://subscribe.wordpress.com/">' + fbd.i18n.editSubs + '</a></li>'; 3800 } 3801 3802 // Hide follow/unfollow completely for static sites, and sites not allowing followers (VIP, private, etc). 3803 if ( ! fbd.canFollow ) { 3804 fbHtmlLi = ''; 3805 } else { 3806 fbHtmlLi = '<li class="actnbr-folded-follow">' + fbhtml + '</li>'; 3807 } 3808 3809 // Ellipsis Menu 3810 fhtml += '<li class="actnbr-ellipsis actnbr-hidden"> \ 3811 <svg class="gridicon gridicon__ellipsis" height="24" width="24" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><circle cx="5" cy="12" r="2"/><circle cx="19" cy="12" r="2"/><circle cx="12" cy="12" r="2"/></g></svg> \ 3812 <div class="actnbr-popover tip tip-top-left actnbr-more"> \ 3813 <div class="tip-arrow"></div> \ 3814 <div class="tip-inner"> \ 3815 <ul> \ 3816 <li class="actnbr-sitename"><a href="' + fbd.siteURL + '">' + fbd.icon + ' ' + actionBarEscapeHtml( fbd.siteName ) + '</a></li> \ 3817 <li class="actnbr-folded-customize"><a href="'+ fbd.customizeLink +'">' + customizeIcon + '<span>' + fbd.i18n.customize + '<span></a></li> \ 3818 ' + fbHtmlLi + ' \ 3819 ' + signupHtml + ' \ 3820 ' + loginHtml + ' \ 3821 ' + themeHtml + ' \ 3822 ' + slkhtml + ' \ 3823 ' + reporthtml + ' \ 3824 ' + viewReaderHtml + ' \ 3825 ' + editFollowsHtml + ' \ 3826 ' + editSubsHtml + ' \ 3827 ' + foldhtml + ' \ 3828 </ul> \ 3829 </div> \ 3830 </div> \ 3831 </li> \ 3832 </ul>'; 3833 3834 fbdf = d.createElement( 'div' ); 3835 fbdf.id = 'actionbar'; 3836 fbdf.innerHTML = fhtml; 3837 b.appendChild( fbdf ); 3838 3839 $actionbar = $( '#actionbar' ).addClass( 'actnbr-' + fbd.themeSlug.replace( '/', '-' ) ); 3840 3841 // Add classes based on contents 3842 if ( fbd.canCustomizeSite ) { 3843 $actionbar.addClass( 'actnbr-has-customize' ); 3844 } 3845 3846 if ( fbd.canEditPost ) { 3847 $actionbar.addClass( 'actnbr-has-edit' ); 3848 } 3849 3850 if ( ! fbd.canCustomizeSite ) { 3851 $actionbar.addClass( 'actnbr-has-follow' ); 3852 } 3853 3854 if ( fbd.isFolded ) { 3855 $actionbar.addClass( 'actnbr-folded' ); 3856 } 3857 3858 // Show status message if available 3859 if ( fbd.statusMessage ) { 3860 showActionBarStatusMessage( fbd.statusMessage ); 3861 } 3862 3863 // *** Actions ***************** 3864 3865 // Follow Site 3866 $actionbar.on( 'click', '.actnbr-actn-follow', function(e) { 3867 e.preventDefault(); 3868 3869 if ( fbd.isLoggedIn ) { 3870 showActionBarStatusMessage( '<div class="actnbr-reader">' + fbd.i18n.followedText + '</div>' ); 3871 bumpStat( 'followed' ); 3872 var eventProps = { 3873 'follow_source': 'actionbar', 3874 'url': fbd.siteURL 3875 }; 3876 recordTracksEvent( 'wpcom_actionbar_site_followed', eventProps ); 3877 3878 request( 'ab_subscribe_to_blog' ); 3879 } else { 3880 showActionBarFollowForm(); 3881 } 3882 } ) 3883 3884 // UnFollow Site 3885 .on( 'click', '.actnbr-actn-following', function(e) { 3886 e.preventDefault(); 3887 $( '#actionbar .actnbr-actn-following' ).replaceWith( '<a class="actnbr-action actnbr-actn-follow" href="">' + followbtn + '<span>' + fbd.i18n.follow + '</span></a>' ); 3888 3889 bumpStat( 'unfollowed' ); 3890 var eventProps = { 3891 'follow_source': 'actionbar', 3892 'url': fbd.siteURL 3893 }; 3894 recordTracksEvent( 'wpcom_actionbar_site_unfollowed', eventProps ); 3895 3896 request( 'ab_unsubscribe_from_blog' ); 3897 } ) 3898 3899 // Show shortlink prompt 3900 .on( 'click', '.actnbr-shortlink a', function(e) { 3901 e.preventDefault(); 3902 window.prompt( "Shortlink: ", fbd.shortlink ); 3903 } ) 3904 3905 // Toggle more menu 3906 .on( 'click', '.actnbr-ellipsis', function(e) { 3907 if ( $( e.target ).closest( 'a' ).hasClass( 'actnbr-action' ) ) { 3908 return false;
3909 } 3910 3911 var popoverLi = $( '#actionbar .actnbr-ellipsis' ); 3912 popoverLi.toggleClass( 'actnbr-hidden' ); 3913 3914 setTimeout( function() { 3915 if ( ! popoverLi.hasClass( 'actnbr-hidden' ) ) { 3916 bumpStat( 'show_more_menu' ); 3917 3918 $( document ).on( 'click.actnbr-body-click', function() { 3919 popoverLi.addClass( 'actnbr-hidden' ); 3920 3921 $( document ).off( 'click.actnbr-body-click' ); 3922 } ); 3923 } 3924 }, 10 ); 3925 }) 3926 3927 // Fold/Unfold 3928 .on( 'click', '.actnbr-fold', function(e) { 3929 e.preventDefault(); 3930 3931 if ( $( '#actionbar' ).hasClass( 'actnbr-folded' ) ) { 3932 $( '.actnbr-fold a' ).html( fbd.i18n.foldBar ); 3933 $( '#actionbar' ).removeClass( 'actnbr-folded' ); 3934 3935 $.post( fbd.xhrURL, { 'action': 'unfold_actionbar' } ); 3936 } else { 3937 $( '.actnbr-fold a' ).html( fbd.i18n.unfoldBar ); 3938 $( '#actionbar' ).addClass( 'actnbr-folded' ); 3939 3940 $.post( fbd.xhrURL, { 'action': 'fold_actionbar' } ); 3941 } 3942 }) 3943 3944 // Record stats for clicks 3945 .on( 'click', '.actnbr-sitename a', createStatsBumperEventHandler( 'clicked_site_title' ) ) 3946 .on( 'click', '.actnbr-customize a', createStatsBumperEventHandler( 'customized' ) ) 3947 .on( 'click', '.actnbr-folded-customize a', createStatsBumperEventHandler( 'customized' ) ) 3948 .on( 'click', '.actnbr-theme a', createStatsBumperEventHandler( 'explored_theme' ) ) 3949 .on( 'click', '.actnbr-edit a', createStatsBumperEventHandler( 'edited' ) ) 3950 .on( 'click', '.actnbr-stats a', createStatsBumperEventHandler( 'clicked_stats' ) ) 3951 .on( 'click', '.flb-report a', createStatsBumperEventHandler( 'reported_content' ) ) 3952 .on( 'click', '.actnbr-follows a', createStatsBumperEventHandler( 'managed_following' ) ) 3953 .on( 'click', '.actnbr-shortlink a', function() { 3954 bumpStat( 'copied_shortlink' ); 3955 } ) 3956 .on( 'click', '.actnbr-reader a', createStatsBumperEventHandler( 'view_reader' ) ) 3957 .on( 'submit', '.actnbr-follow-bubble form', createStatsBumperEventHandler( 'submit_follow_form', function() { 3958 $( '#actionbar .actnbr-follow-bubble form button' ).attr( 'disabled', true ); 3959 } ) ) 3960 .on( 'click', '.actnbr-login-nudge a', createStatsBumperEventHandler( 'clicked_login_nudge' ) ) 3961 .on( 'click', '.actnbr-signup a', createStatsBumperEventHandler( 'clicked_signup_link' ) ) 3962 .on( 'click', '.actnbr-login a', createStatsBumperEventHandler( 'clicked_login_link' ) ) 3963 .on( 'click', '.actnbr-subs a', createStatsBumperEventHandler( 'clicked_manage_subs_link' ) ); 3964 3965 // Make Follow/Unfollow requests 3966 var request = function( action ) { 3967 $.post( fbd.xhrURL, { 3968 'action': action, 3969 '_wpnonce': fbd.nonce, 3970 'source': 'actionbar', 3971 'blog_id': fbd.siteID 3972 }); 3973 }; 3974 3975 // Show/Hide actionbar on scroll 3976 fb = $('#actionbar'); 3977 toggleactionbar = function() { 3978 var st = $(window).scrollTop(), 3979 topOffset = 0; 3980 3981 if ( $(window).scrollTop() < 0 ) { 3982 return; 3983 } 3984 3985 // Still 3986 if ( lastScrollTop == 0 || ( ( st == lastScrollTop ) && lastScrollDir == 'up' ) ) { 3987 fb.removeClass( 'actnbr-hidden' ); 3988 3989 // Moving 3990 } else { 3991 // Scrolling Up 3992 if ( st < lastScrollTop ){ 3993 fb.removeClass( 'actnbr-hidden' ); 3994 lastScrollDir = 'up'; 3995 3996 // Scrolling Down 3997 } else { 3998 // check if there are any popovers open, and only hide action bar if not 3999 if ( $( '#actionbar > ul > li:not(.actnbr-hidden) > .actnbr-popover' ).length === 0 ) { 4000 fb.addClass( 'actnbr-hidden' ); 4001 lastScrollDir = 'down'; 4002 4003 // Hide any menus 4004 $( '#actionbar li' ).addClass( 'actnbr-hidden' ); 4005 } 4006 } 4007 } 4008 4009 lastScrollTop = st; 4010 }; 4011 setInterval( toggleactionbar, 100 ); 4012 4013 var bumpStat = function( stat ) { 4014 return $.post( fbd.xhrURL, { 4015 'action': 'actionbar_stats', 4016 'stat': stat 4017 } ); 4018 }; 4019 4020 var recordTracksEvent = function( eventName, eventProps ) { 4021 eventProps = eventProps || {}; 4022 window._tkq = window._tkq || []; 4023 window._tkq.push( [ 'recordEvent', eventName, eventProps ] ); 4024 }; 4025 4026 /** 4027 * A factory method for creating an event handler function that will bump a specific stat and ONLY THEN re-dispatch 4028 * the event. This will ensure that the bumped stat is indeed recorded before navigating the page away, as otherwise 4029 * some browsers may very well decide to cancel the stat request in that case. 4030 * 4031 * @param {String} stat the name of the stat to bump 4032 * @param {Function} additionalEffect an additional function that should be called after the stat is bumped 4033 */ 4034 function createStatsBumperEventHandler( stat, additionalEffect ) { 4035 var completedEvents = {}; 4036 4037 return function eventHandler( event ) { 4038 if ( completedEvents[ event.timeStamp ] ) { 4039 delete completedEvents[ event.timeStamp ]; 4040 4041 // hack-around to submit forms, dispatching "submit" event is not enough for them 4042 if ( event.type === 'submit' ) { 4043 event.target.submit(); 4044 } 4045 4046 if ( typeof additionalEffect === 'function' ) { 4047 return additionalEffect( event ); 4048 } 4049 4050 return true; 4051 } 4052 4053 event.preventDefault(); 4054 event.stopPropagation(); 4055 4056 function dispatchOriginalEvent() { 4057 var newEvent; 4058 4059 // Retrieves the native event object created by the browser from the jQuery event object 4060 var originalEvent = event.originalEvent; 4061 4062 /** 4063 * Handles Internet Explorer that doesn't support Event nor CustomEvent constructors 4064 * 4065 * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/Event 4066 * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent 4067 * @see https://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work/ 4068 */ 4069 if ( typeof window.CustomEvent !== 'function' ) { 4070 newEvent = document.createEvent( 'CustomEvent' ); 4071 newEvent.initCustomEvent( 4072 originalEvent.type, 4073 originalEvent.bubbles, 4074 originalEvent.cancelable, 4075 originalEvent.detail 4076 ); 4077 } else { 4078 newEvent = new originalEvent.constructor( originalEvent.type, originalEvent ); 4079 } 4080 4081 completedEvents[ newEvent.timeStamp ] = true; 4082 4083 originalEvent.target.dispatchEvent( newEvent ); 4084 } 4085 4086 bumpStat( stat ).then( dispatchOriginalEvent, dispatchOriginalEvent ); 4087 } 4088 } 4089 4090 function actionBarEscapeHtml(string) { 4091 return String(string).replace(/[&<>
4091"'\/]/g, function (s) { 4092 var entityMap = { 4093 "&": "&", 4094 "<": "<", 4095 ">": ">", 4096 '"': '"', 4097 "'": ''', 4098 "/": '/' 4099 }; 4100 return entityMap[s]; 4101 }); 4102 } 4103 4104 function showActionBarStatusMessage( message ) { 4105 $( '#actionbar .actnbr-actn-follow' ).replaceWith( '<a class="actnbr-action actnbr-actn-following" href="">' + followingbtn + '<span>' + fbd.i18n.following + '</span></a>' ); 4106 $( '#actionbar .actnbr-follow-bubble' ).html( ' \ 4107 <ul> \ 4108 <li class="actnbr-sitename"><a href="' + fbd.siteURL + '">' + fbd.icon + ' ' + actionBarEscapeHtml( fbd.siteName ) + '</a></li> \ 4109 <li class="actnbr-message">' + message + '</li> \ 4110 </ul> \ 4111 '); 4112 4113 var btn = $( '#actionbar .actnbr-btn' ); 4114 btn.removeClass( 'actnbr-hidden' ); 4115 4116 setTimeout( function() { 4117 if ( ! btn.hasClass( 'actnbr-hidden' ) ) { 4118 $( '#actionbar .actnbr-email-field' ).focus(); 4119 $( document ).on( 'click.actnbr-body-click', function(e) { 4120 if ( $( e.target ).closest( '.actnbr-popover' )[0] ) { 4121 return; 4122 } 4123 btn.addClass( 'actnbr-hidden' ); 4124 $( document ).off( 'click.actnbr-body-click' ); 4125 } ); 4126 } 4127 }, 10 ); 4128 } 4129 4130 function showActionBarFollowForm() { 4131 var btn = $( '#actionbar .actnbr-btn' ); 4132 btn.toggleClass( 'actnbr-hidden' ); 4133 4134 var form = $('<form>'); 4135 4136 if ( fbd.i18n.followers ) { 4137 form.append( $( '<div class="actnbr-follow-count">' ).html( fbd.i18n.followers ) ); 4138 } 4139 4140 form.append($('<div>').append($('<input>').attr({"type": "email", "name": "email", "placeholder": fbd.i18n.enterEmail, "class": "actnbr-email-field"}))); 4141 form.append($('<input type="hidden" name="action" value="subscribe"/>')); 4142 form.append($('<input type="hidden" name="blog_id">').attr('value', fbd.siteID)); 4143 form.append($('<input type="hidden" name="source">').attr('value', fbd.referer)); 4144 form.append($('<input type="hidden" name="sub-type" value="actionbar-follow"/>')); 4145 form.append($(fbd.subscribeNonce)); 4146 form.append($('<div class="actnbr-button-wrap">').append($('<button type="submit">').attr('value', fbd.i18n.subscribe).html(fbd.i18n.subscribe))); 4147 form.attr('method', 'post'); 4148 form.attr('action', 'https://subscribe.wordpress.com'); 4149 form.attr('accept-charset', 'utf-8'); 4150 4151 var html = $('<ul/>'); 4152 html.append($('<li class="actnbr-sitename">').append($('<a>').attr('href', fbd.siteURL).append($(fbd.icon)).append(' ' + actionBarEscapeHtml( fbd.siteName )))) 4153 html.append($('<li>').append(form)); 4154 html.append($('<li class="actnbr-login-nudge">').append($('<div>').html(fbd.i18n.alreadyUser))); 4155 4156 $( '#actionbar .actnbr-follow-bubble' ).empty().append(html); 4157 4158 setTimeout( function() { 4159 if ( ! btn.hasClass( 'actnbr-hidden' ) ) { 4160 bumpStat( 'show_follow_form' ); 4161 4162 $( '#actionbar .actnbr-email-field' ).focus(); 4163 4164 $( document ).on( 'click.actnbr-body-click', function( event ) { 4165 if ( $( event.target ).closest( '.actnbr-popover' )[ 0 ] ) { 4166 return; 4167 } 4168 4169 btn.addClass( 'actnbr-hidden' ); 4170 4171 $( document ).off( 'click.actnbr-body-click' ); 4172 } ); 4173 } 4174 }, 10 ); 4175 } 4176})(jQuery); 4177; 4178//fgnass.github.com/spin.js#v1.3 4179 4180/** 4181 * Copyright (c) 2011-2013 Felix Gnass 4182 * Licensed under the MIT license 4183 */ 4184(function(root, factory) { 4185 4186 /* CommonJS */ 4187 if (typeof exports == 'object') module.exports = factory() 4188 4189 /* AMD module */ 4190 else if (typeof define == 'function' && define.amd) define(factory) 4191 4192 /* Browser global */ 4193 else root.Spinner = factory() 4194} 4195(this, function() { 4196 "use strict"; 4197 4198 var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ 4199 , animations = {} /* Animation rules keyed by their name */ 4200 , useCssAnimations /* Whether to use CSS animations or setTimeout */ 4201 4202 /** 4203 * Utility function to create elements. If no tag name is given, 4204 * a DIV is created. Optionally properties can be passed. 4205 */ 4206 function createEl(tag, prop) { 4207 var el = document.createElement(tag || 'div') 4208 , n 4209 4210 for(n in prop) el[n] = prop[n] 4211 return el 4212 } 4213 4214 /** 4215 * Appends children and returns the parent. 4216 */ 4217 function ins(parent /* child1, child2, ...*/) { 4218 for (var i=1, n=arguments.length; i<n; i++) 4219 parent.appendChild(arguments[i]) 4220 4221 return parent 4222 } 4223 4224 /** 4225 * Insert a new stylesheet to hold the @keyframe or VML rules. 4226 */ 4227 var sheet = (function() { 4228 var el = createEl('style', {type : 'text/css'}) 4229 ins(document.getElementsByTagName('head')[0], el) 4230 return el.sheet || el.styleSheet 4231 }()) 4232 4233 /** 4234 * Creates an opacity keyframe animation rule and returns its name. 4235 * Since most mobile Webkits have timing issues with animation-delay, 4236 * we create separate rules for each line/segment. 4237 */ 4238 function addAnimation(alpha, trail, i, lines) { 4239 var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-') 4240 , start = 0.01 + i/lines * 100 4241 , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha) 4242 , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase() 4243 , pre = prefix && '-' + prefix + '-' || '' 4244 4245 if (!animations[name]) { 4246 sheet.insertRule( 4247 '@' + pre + 'keyframes ' + name + '{' + 4248 '0%{opacity:' + z + '}' + 4249 start + '%{opacity:' + alpha + '}' + 4250 (start+0.01) + '%{opacity:1}' + 4251 (start+trail) % 100 + '%{opacity:' + alpha + '}' + 4252 '100%{opacity:' + z + '}' + 4253 '}', sheet.cssRules.length) 4254 4255 animations[name] = 1 4256 } 4257 4258 return name 4259 } 4260 4261 /**
4262 * Tries various vendor prefixes and returns the first supported property. 4263 */ 4264 function vendor(el, prop) { 4265 var s = el.style 4266 , pp 4267 , i 4268 4269 if(s[prop] !== undefined) return prop 4270 prop = prop.charAt(0).toUpperCase() + prop.slice(1) 4271 for(i=0; i<prefixes.length; i++) { 4272 pp = prefixes[i]+prop 4273 if(s[pp] !== undefined) return pp 4274 } 4275 } 4276 4277 /** 4278 * Sets multiple style properties at once. 4279 */ 4280 function css(el, prop) { 4281 for (var n in prop) 4282 el.style[vendor(el, n)||n] = prop[n] 4283 4284 return el 4285 } 4286 4287 /** 4288 * Fills in default values. 4289 */ 4290 function merge(obj) { 4291 for (var i=1; i < arguments.length; i++) { 4292 var def = arguments[i] 4293 for (var n in def) 4294 if (obj[n] === undefined) obj[n] = def[n] 4295 } 4296 return obj 4297 } 4298 4299 /** 4300 * Returns the absolute page-offset of the given element. 4301 */ 4302 function pos(el) { 4303 var o = { x:el.offsetLeft, y:el.offsetTop } 4304 while((el = el.offsetParent)) 4305 o.x+=el.offsetLeft, o.y+=el.offsetTop 4306 4307 return o 4308 } 4309 4310 // Built-in defaults 4311 4312 var defaults = { 4313 lines: 12, // The number of lines to draw 4314 length: 7, // The length of each line 4315 width: 5, // The line thickness 4316 radius: 10, // The radius of the inner circle 4317 rotate: 0, // Rotation offset 4318 corners: 1, // Roundness (0..1) 4319 color: '#000', // #rgb or #rrggbb 4320 direction: 1, // 1: clockwise, -1: counterclockwise 4321 speed: 1, // Rounds per second 4322 trail: 100, // Afterglow percentage 4323 opacity: 1/4, // Opacity of the lines 4324 fps: 20, // Frames per second when using setTimeout() 4325 zIndex: 2e9, // Use a high z-index by default 4326 className: 'spinner', // CSS class to assign to the element 4327 top: 'auto', // center vertically 4328 left: 'auto', // center horizontally 4329 position: 'relative' // element position 4330 } 4331 4332 /** The constructor */ 4333 function Spinner(o) { 4334 if (typeof this == 'undefined') return new Spinner(o) 4335 this.opts = merge(o || {}, Spinner.defaults, defaults) 4336 } 4337 4338 // Global defaults that override the built-ins: 4339 Spinner.defaults = {} 4340 4341 merge(Spinner.prototype, { 4342 4343 /** 4344 * Adds the spinner to the given target element. If this instance is already 4345 * spinning, it is automatically removed from its previous target b calling 4346 * stop() internally. 4347 */ 4348 spin: function(target) { 4349 this.stop() 4350 4351 var self = this 4352 , o = self.opts 4353 , el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex}) 4354 , mid = o.radius+o.length+o.width 4355 , ep // element position 4356 , tp // target position 4357 4358 if (target) { 4359 target.insertBefore(el, target.firstChild||null) 4360 tp = pos(target) 4361 ep = pos(el) 4362 css(el, { 4363 left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : parseInt(o.left, 10) + mid) + 'px', 4364 top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid) + 'px' 4365 }) 4366 } 4367 4368 el.setAttribute('role', 'progressbar') 4369 self.lines(el, self.opts) 4370 4371 if (!useCssAnimations) { 4372 // No CSS animation support, use setTimeout() instead 4373 var i = 0 4374 , start = (o.lines - 1) * (1 - o.direction) / 2 4375 , alpha 4376 , fps = o.fps 4377 , f = fps/o.speed 4378 , ostep = (1-o.opacity) / (f*o.trail / 100) 4379 , astep = f/o.lines 4380 4381 ;(function anim() { 4382 i++; 4383 for (var j = 0; j < o.lines; j++) { 4384 alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity) 4385 4386 self.opacity(el, j * o.direction + start, alpha, o) 4387 } 4388 self.timeout = self.el && setTimeout(anim, ~~(1000/fps)) 4389 })() 4390 } 4391 return self 4392 }, 4393 4394 /** 4395 * Stops and removes the Spinner. 4396 */ 4397 stop: function() { 4398 var el = this.el 4399 if (el) { 4400 clearTimeout(this.timeout) 4401 if (el.parentNode) el.parentNode.removeChild(el) 4402 this.el = undefined 4403 } 4404 return this 4405 }, 4406 4407 /** 4408 * Internal method that draws the individual lines. Will be overwritten 4409 * in VML fallback mode below. 4410 */ 4411 lines: function(el, o) { 4412 var i = 0 4413 , start = (o.lines - 1) * (1 - o.direction) / 2 4414 , seg 4415 4416 function fill(color, shadow) { 4417 return css(createEl(), { 4418 position: 'absolute', 4419 width: (o.length+o.width) + 'px', 4420 height: o.width + 'px', 4421 background: color, 4422 boxShadow: shadow, 4423 transformOrigin: 'left', 4424 transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)', 4425 borderRadius: (o.corners * o.width>>1) + 'px' 4426 }) 4427 } 4428 4429 for (; i < o.lines; i++) { 4430 seg = css(createEl(), { 4431 position: 'absolute', 4432 top: 1+~(o.width/2) + 'px',
4433 transform: o.hwaccel ? 'translate3d(0,0,0)' : '', 4434 opacity: o.opacity, 4435 animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite' 4436 }) 4437 4438 if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'})) 4439 4440 ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)'))) 4441 } 4442 return el 4443 }, 4444 4445 /** 4446 * Internal method that adjusts the opacity of a single line. 4447 * Will be overwritten in VML fallback mode below. 4448 */ 4449 opacity: function(el, i, val) { 4450 if (i < el.childNodes.length) el.childNodes[i].style.opacity = val 4451 } 4452 4453 }) 4454 4455 4456 function initVML() { 4457 4458 /* Utility function to create a VML tag */ 4459 function vml(tag, attr) { 4460 return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr) 4461 } 4462 4463 // No CSS transforms but VML support, add a CSS rule for VML elements: 4464 sheet.addRule('.spin-vml', 'behavior:url(#default#VML)') 4465 4466 Spinner.prototype.lines = function(el, o) { 4467 var r = o.length+o.width 4468 , s = 2*r 4469 4470 function grp() { 4471 return css( 4472 vml('group', { 4473 coordsize: s + ' ' + s, 4474 coordorigin: -r + ' ' + -r 4475 }), 4476 { width: s, height: s } 4477 ) 4478 } 4479 4480 var margin = -(o.width+o.length)*2 + 'px' 4481 , g = css(grp(), {position: 'absolute', top: margin, left: margin}) 4482 , i 4483 4484 function seg(i, dx, filter) { 4485 ins(g, 4486 ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), 4487 ins(css(vml('roundrect', {arcsize: o.corners}), { 4488 width: r, 4489 height: o.width, 4490 left: o.radius, 4491 top: -o.width>>1, 4492 filter: filter 4493 }), 4494 vml('fill', {color: o.color, opacity: o.opacity}), 4495 vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change 4496 ) 4497 ) 4498 ) 4499 } 4500 4501 if (o.shadow) 4502 for (i = 1; i <= o.lines; i++) 4503 seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)') 4504 4505 for (i = 1; i <= o.lines; i++) seg(i) 4506 return ins(el, g) 4507 } 4508 4509 Spinner.prototype.opacity = function(el, i, val, o) { 4510 var c = el.firstChild 4511 o = o.shadow && o.lines || 0 4512 if (c && i+o < c.childNodes.length) { 4513 c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild 4514 if (c) c.opacity = val 4515 } 4516 } 4517 } 4518 4519 var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}) 4520 4521 if (!vendor(probe, 'transform') && probe.adj) initVML() 4522 else useCssAnimations = vendor(probe, 'animation') 4523 4524 return Spinner 4525 4526})); 4527; 4528/** 4529 * Copyright (c) 2011-2013 Felix Gnass 4530 * Licensed under the MIT license 4531 */ 4532 4533/* 4534 4535Basic Usage: 4536============ 4537 4538$('#el').spin(); // Creates a default Spinner using the text color of #el. 4539$('#el').spin({ ... }); // Creates a Spinner using the provided options. 4540 4541$('#el').spin(false); // Stops and removes the spinner. 4542 4543Using Presets: 4544============== 4545 4546$('#el').spin('small'); // Creates a 'small' Spinner using the text color of #el. 4547$('#el').spin('large', '#fff'); // Creates a 'large' white Spinner. 4548 4549Adding a custom preset: 4550======================= 4551 4552$.fn.spin.presets.flower = { 4553 lines: 9 4554 length: 10 4555 width: 20 4556 radius: 0 4557} 4558 4559$('#el').spin('flower', 'red'); 4560 4561*/ 4562 4563(function(factory) { 4564 4565 if (typeof exports == 'object') { 4566 // CommonJS 4567 factory(require('jquery'), require('spin')) 4568 } 4569 else if (typeof define == 'function' && define.amd) { 4570 // AMD, register as anonymous module 4571 define(['jquery', 'spin'], factory) 4572 } 4573 else { 4574 // Browser globals 4575 if (!window.Spinner) throw new Error('Spin.js not present') 4576 factory(window.jQuery, window.Spinner) 4577 } 4578 4579}(function($, Spinner) { 4580 4581 $.fn.spin = function(opts, color) { 4582 4583 return this.each(function() { 4584 var $this = $(this), 4585 data = $this.data(); 4586 4587 if (data.spinner) { 4588 data.spinner.stop(); 4589 delete data.spinner; 4590 } 4591 if (opts !== false) { 4592 opts = $.extend( 4593 { color: color || $this.css('color') }, 4594 $.fn.spin.presets[opts] || opts 4595 ) 4596 // Begin WordPress Additions 4597 // To use opts.right, you need to have specified a length, width, and radius. 4598 if ( typeof opts.right !== 'undefined' && typeof opts.length !== 'undefined' 4599 && typeof opts.width !== 'undefined' && typeof opts.radius !== 'undefined' ) { 4600 var pad = $this.css( 'padding-left' ); 4601 pad = ( typeof pad === 'undefined' ) ? 0 : parseInt( pad, 10 ); 4602 opts.left = $this.outerWidth() - ( 2 * ( opts.length + opts.width + opts.radius ) ) - pad - opts.right; 4603 delete opts.right; 4604 } 4605 // End WordPress Additions 4606 data.spinner = new Spinner(opts).spin(this) 4607 } 4608 }) 4609 } 4610 4611 $.fn.spin.presets = { 4612 tiny: { lines: 8, length: 2, width: 2, radius: 3 }, 4613 small: { lines: 8, length: 4, width: 3, radius: 5 }, 4614 large: { lines: 10, length: 8, width: 4, radius: 8 } 4615 } 4616 4617})); 4618 4619// Jetpack Presets Overrides: 4620(function($){ 4621 $.fn.spin.presets.wp = { trail: 60, speed: 1.3 }; 4622 $.fn.spin.presets.small = $.extend( { lines: 8, length: 2, width: 2, radius: 3 }, $.fn.spin.presets.wp ); 4623 $.fn.spin.presets.medium = $.extend( { lines: 8, length: 4, width: 3, radius: 5 }, $.fn.spin.presets.wp ); 4624 $.fn.spin.presets.large = $.extend( { lines: 10, length: 6, width: 4, radius: 7 }, $.fn.spin.presets.wp ); 4625 $.fn.spin.presets['small-left'] = $.extend( { left: 5 }, $.fn.spin.presets.small ); 4626 $.fn.spin.presets['small-right'] = $.extend( { right: 5 }, $.fn.spin.presets.small ); 4627 $.fn.spin.presets['medium-left'] = $.extend( { left: 5 }, $.fn.spin.presets.medium ); 4628 $.fn.spin.presets['medium-right'] = $.extend( { right: 5 }, $.fn.spin.presets.medium ); 4629 $.fn.spin.presets['large-left'] = $.extend( { left: 5 }, $.fn.spin.presets.large ); 4630 $.fn.spin.presets['large-right'] = $.extend( { right: 5 }, $.fn.spin.presets.large ); 4631})(jQuery); 4632; 4633/* jshint sub: true, onevar: false, multistr: true, devel: true, smarttabs: true */ 4634/* global jetpackCarouselStrings, DocumentTouch */ 4635 4636// @start-hide-in-jetpack 4637if (typeof wpcom === 'undefined') { 4638 var wpcom = {}; 4639} 4640wpcom.carousel = (function(/*$*/) { 4641 var prebuilt_widths = jetpackCarouselStrings.widths; 4642 var pageviews_stats_args = jetpackCarouselStrings.stats_query_args; 4643 4644 var findFirstLargeEnoughWidth = function(original_w, original_h, dest_w, dest_h) { 4645 var inverse_ratio = original_h / original_w; 4646 4647 for ( var i = 0; i < prebuilt_widths.length; ++i ) { 4648 if ( prebuilt_widths[i] >= dest_w || prebuilt_widths[i] * inverse_ratio >
4648= dest_h ) { 4649 return prebuilt_widths[i]; 4650 } 4651 } 4652 4653 return original_w; 4654 }; 4655 4656 var addWidthToImageURL = function(url, width) { 4657 width = parseInt(width, 10); 4658 // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc) 4659 if ('undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1) { 4660 width = Math.round( width * window.devicePixelRatio ); 4661 } 4662 url = addArgToURL(url, 'w', width); 4663 url = addArgToURL(url, 'h', ''); 4664 return url; 4665 }; 4666 4667 var addArgToURL = function(url, arg, value) { 4668 var re = new RegExp(arg+'=[^?&]+'); 4669 if ( url.match(re) ) { 4670 return url.replace(re, arg + '=' + value); 4671 } else { 4672 var divider = url.indexOf('?') !== -1 ? '&' : '?'; 4673 return url + divider + arg + '=' + value; 4674 } 4675 }; 4676 4677 var stat = function ( names ) { 4678 if ( typeof names !== 'string' ) { 4679 names = names.join( ',' ); 4680 } 4681 4682 new Image().src = window.location.protocol + 4683 '../g.gifv=wpcom-no-pv' + 4684 '&x_carousel=' + names + 4685 '&baba=' + Math.random(); 4686 }; 4687 4688 var pageview = function ( post_id ) { 4689 new Image().src = window.location.protocol + 4690 '../g.gifhost=' + encodeURIComponent( window.location.host ) + 4691 '&ref=' + encodeURIComponent( document.referrer ) + 4692 '&rand=' + Math.random() + 4693 '&' + pageviews_stats_args + 4694 '&post=' + encodeURIComponent( post_id ); 4695 }; 4696 4697 4698 return { 4699 findFirstLargeEnoughWidth: findFirstLargeEnoughWidth, 4700 addWidthToImageURL: addWidthToImageURL, 4701 stat: stat, 4702 pageview: pageview 4703 }; 4704 4705})(jQuery); 4706// @end-hide-in-jetpack 4707 4708jQuery( document ).ready( function( $ ) { 4709 // gallery faded layer and container elements 4710 var overlay, 4711 comments, 4712 gallery, 4713 container, 4714 nextButton, 4715 previousButton, 4716 info, 4717 transitionBegin, 4718 caption, 4719 resizeTimeout, 4720 photo_info, 4721 close_hint, 4722 commentInterval, 4723 lastSelectedSlide, 4724 screenPadding = 110, 4725 originalOverflow = $( 'body' ).css( 'overflow' ), 4726 originalHOverflow = $( 'html' ).css( 'overflow' ), 4727 proportion = 85, 4728 last_known_location_hash = '', 4729 imageMeta, 4730 titleAndDescription, 4731 commentForm, 4732 leftColWrapper, 4733 scrollPos; 4734 4735 if ( window.innerWidth <= 760 ) { 4736 screenPadding = Math.round( ( window.innerWidth / 760 ) * 110 ); 4737 4738 if ( 4739 screenPadding < 40 && 4740 ( 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch ) ) 4741 ) { 4742 screenPadding = 0; 4743 } 4744 } 4745 4746 // Adding a polyfill for browsers that do not have Date.now 4747 if ( 'undefined' === typeof Date.now ) { 4748 Date.now = function now() { 4749 return new Date().getTime(); 4750 }; 4751 } 4752 4753 var keyListener = function( e ) { 4754 switch ( e.which ) { 4755 case 38: // up 4756 e.preventDefault(); 4757 container.scrollTop( container.scrollTop() - 100 ); 4758 break; 4759 case 40: // down 4760 e.preventDefault(); 4761 container.scrollTop( container.scrollTop() + 100 ); 4762 break; 4763 case 39: // right 4764 e.preventDefault(); 4765 gallery.jp_carousel( 'next' ); 4766 break; 4767 case 37: // left 4768 case 8: // backspace 4769 e.preventDefault(); 4770 gallery.jp_carousel( 'previous' ); 4771 break; 4772 case 27: // escape 4773 e.preventDefault(); 4774 container.jp_carousel( 'close' ); 4775 break; 4776 default: 4777 // making jslint happy 4778 break; 4779 } 4780 }; 4781 4782 var resizeListener = function(/*e*/) { 4783 clearTimeout( resizeTimeout ); 4784 resizeTimeout = setTimeout( function() { 4785 gallery.jp_carousel( 'slides' ).jp_carousel( 'fitSlide', true ); 4786 gallery.jp_carousel( 'updateSlidePositions', true ); 4787 gallery.jp_carousel( 'fitMeta', true ); 4788 }, 200 ); 4789 }; 4790 4791 var prepareGallery = function(/*dataCarouselExtra*/) { 4792 if ( ! overlay ) { 4793 overlay = $( '<div></div>' ) 4794 .addClass( 'jp-carousel-overlay' ) 4795 .css( { 4796 position: 'fixed', 4797 top: 0, 4798 right: 0, 4799 bottom: 0, 4800 left: 0, 4801 } ); 4802 4803 var buttons = 4804 '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>'; 4805 if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) { 4806// @start-hide-in-jetpack 4807 if ( 1 === Number( jetpackCarouselStrings.is_public && 1 === Number( jetpackCarouselStrings.reblog_enabled ) ) ) { 4808 buttons += '<a class="jp-carousel-reblog" href="#">
4808' + jetpackCarouselStrings.reblog + '</a>'; 4809 } 4810// @end-hide-in-jetpack 4811 } 4812 4813 buttons = $( '<div class="jp-carousel-buttons">' + buttons + '</div>' ); 4814 4815 caption = $( '<h2 itemprop="caption description"></h2>' ); 4816 photo_info = $( '<div class="jp-carousel-photo-info"></div>' ).append( caption ); 4817 4818 imageMeta = $( '<div></div>' ) 4819 .addClass( 'jp-carousel-image-meta' ) 4820 .css( { 4821 float: 'right', 4822 'margin-top': '20px', 4823 width: '250px', 4824 } ); 4825 4826 imageMeta 4827 .append( buttons ) 4828 .append( "<ul class='jp-carousel-image-exif' style='display:none;'></ul>" ) 4829 .append( "<a class='jp-carousel-image-download' style='display:none;'></a>" ) 4830 .append( "<div class='jp-carousel-image-map' style='display:none;'></div>" ); 4831 4832 titleAndDescription = $( '<div></div>' ) 4833 .addClass( 'jp-carousel-titleanddesc' ) 4834 .css( { 4835 width: '100%', 4836 'margin-top': imageMeta.css( 'margin-top' ), 4837 } ); 4838 4839 var commentFormMarkup = '<div id="jp-carousel-comment-form-container">'; 4840 4841 if ( 4842 jetpackCarouselStrings.local_comments_commenting_as && 4843 jetpackCarouselStrings.local_comments_commenting_as.length 4844 ) { 4845 // Comments not enabled, fallback to local comments 4846 4847 if ( 4848 1 !== Number( jetpackCarouselStrings.is_logged_in ) && 4849 1 === Number( jetpackCarouselStrings.comment_registration ) 4850 ) { 4851 commentFormMarkup += 4852 '<div id="jp-carousel-comment-form-commenting-as">' + 4853 jetpackCarouselStrings.local_comments_commenting_as + 4854 '</div>'; 4855 } else { 4856 commentFormMarkup += '<form id="jp-carousel-comment-form">'; 4857 commentFormMarkup += 4858 '<textarea name="comment" class="jp-carousel-comment-form-field jp-carousel-comment-form-textarea" id="jp-carousel-comment-form-comment-field" placeholder="' + 4859 jetpackCarouselStrings.write_comment + 4860 '"></textarea>'; 4861 commentFormMarkup += '<div id="jp-carousel-comment-form-submit-and-info-wrapper">'; 4862 commentFormMarkup += 4863 '<div id="jp-carousel-comment-form-commenting-as">' + 4864 jetpackCarouselStrings.local_comments_commenting_as + 4865 '</div>'; 4866 commentFormMarkup += 4867 '<input type="submit" name="submit" class="jp-carousel-comment-form-button" id="jp-carousel-comment-form-button-submit" value="' + 4868 jetpackCarouselStrings.post_comment + 4869 '" />'; 4870 commentFormMarkup += '<span id="jp-carousel-comment-form-spinner"> </span>'; 4871 commentFormMarkup += '<div id="jp-carousel-comment-post-results"></div>'; 4872 commentFormMarkup += '</div>'; 4873 commentFormMarkup += '</form>'; 4874 } 4875 } 4876 commentFormMarkup += '</div>'; 4877 4878 commentForm = $( commentFormMarkup ).css( { 4879 width: '100%', 4880 'margin-top': '20px', 4881 color: '#999', 4882 } ); 4883 4884 comments = $( '<div></div>' ) 4885 .addClass( 'jp-carousel-comments' ) 4886 .css( { 4887 width: '100%', 4888 bottom: '10px', 4889 'margin-top': '20px', 4890 } ); 4891 4892 var commentsLoading = $( 4893 '<div id="jp-carousel-comments-loading"><span>' + 4894 jetpackCarouselStrings.loading_comments + 4895 '</span></div>' 4896 ).css( { 4897 width: '100%', 4898 bottom: '10px', 4899 'margin-top': '20px', 4900 } ); 4901 4902 var leftWidth = $( window ).width() - screenPadding * 2 - ( imageMeta.width() + 40 ); 4903 leftWidth += 'px'; 4904 4905 leftColWrapper = $( '<div></div>' ) 4906 .addClass( 'jp-carousel-left-column-wrapper' ) 4907 .css( { 4908 width: Math.floor( leftWidth ), 4909 } ) 4910 .append( titleAndDescription ) 4911 .append( commentForm ) 4912 .append( comments ) 4913 .append( commentsLoading ); 4914 4915 var fadeaway = $( '<div></div>' ).addClass( 'jp-carousel-fadeaway' ); 4916 4917 info = $( '<div></div>' ) 4918 .addClass( 'jp-carousel-info' ) 4919 .css( { 4920 top: Math.floor( ( $( window ).height() / 100 ) * proportion ), 4921 left: screenPadding, 4922 right: screenPadding, 4923 } ) 4924 .append( photo_info ) 4925 .append( imageMeta ); 4926 4927 if ( window.innerWidth <= 760 ) { 4928 photo_info.remove().insertAfter( titleAndDescription ); 4929 info.prepend( leftColWrapper ); 4930 } else { 4931 info.append( leftColWrapper ); 4932 } 4933 4934 var targetBottomPos = $( window ).height() - parseInt( info.css( 'top' ), 10 ) + 'px'; 4935 4936 nextButton = $( '<div><span></span></div>' ) 4937 .addClass( 'jp-carousel-next-button' ) 4938 .css( { 4939 right: '15px', 4940 } ) 4941 .hide(); 4942 4943 previousButton = $( '<div><span></span></div>' ) 4944 .addClass( 'jp-carousel-previous-button' ) 4945 .css( { 4946 left: 0, 4947 } ) 4948 .hide(); 4949 4950 nextButton.add( previousButton ).css( { 4951 position: 'fixed', 4952 top: '40px', 4953 bottom: targetBottomPos, 4954 width: screenPadding, 4955 } ); 4956 4957 gallery = $( '<div></div>' ) 4958 .addClass( 'jp-carousel' ) 4959 .css( { 4960 position: 'absolute', 4961 top: 0, 4962 bottom: targetBottomPos, 4963 left: 0, 4964 right: 0, 4965 } ); 4966 4967 close_hint = $( '<div class="jp-carousel-close-hint"><span>×</span></div>' ).css( { 4968 position: 'fixed', 4969 } ); 4970 4971 container = $( '<div></div>' ) 4972 .addClass( 'jp-carousel-wrap' ) 4973 .addClass( 'jp-carousel-transitions' ); 4974 if ( 'white' === jetpackCarouselStrings.background_color ) { 4975 container.addClass( 'jp-carousel-light' ); 4976 } 4977 4978 container.attr( 'itemscope', '' ); 4979 4980 container.attr( 'itemtype', 'https://schema.org/ImageGallery' ); 4981 4982 container 4983 .css( { 4984 position: 'fixed', 4985 top: 0, 4986 right: 0, 4987 bottom: 0, 4988 left: 0, 4989 'z-index': 2147483647, 4990 'overflow-x': 'hidden', 4991 'overflow-y': 'auto', 4992 direction: 'ltr', 4993 } ) 4994 .hide() 4995 .append( overlay ) 4996 .append( gallery ) 4997 .append( fadeaway ) 4998 .append( info ) 4999 .append( nextButton ) 5000 .append( previousButton ) 5001 .append( close_hint ) 5002 .appendTo( $( 'body' ) ) 5003 .click( function( e ) { 5004 var target = $( e.target ), 5005 wrap = target.parents( 'div.jp-carousel-wrap' ), 5006 data = wrap.data( 'carousel-extra' ), 5007 slide = wrap.find( 'div.selected' ), 5008 attachment_id = slide.data( 'attachment-id' ); 5009 data = data || []; 5010 5011 if ( 5012 target.is( gallery ) || 5013 target 5014 .parents() 5015 .add( target ) 5016 .is( close_hint ) 5017 ) { 5018 container.jp_carousel( 'close' ); 5019// @start-hide-in-jetpack 5020 } else if ( target.hasClass('jp-carousel-reblog') ) { 5021 e.preventDefault(); 5022 e.stopPropagation(); 5023 if ( !target.hasClass('reblogged') ) { 5024 target.jp_carousel('show_reblog_box'); 5025 wpcom.carousel.stat('reblog_show_box'); 5026 } 5027 } else if ( target.parents('#carousel-reblog-box').length ) { 5028 if ( target.is('a.cancel') ) { 5029 e.preventDefault(); 5030 e.stopPropagation(); 5031 target.jp_carousel('hide_reblog_box'); 5032 wpcom.carousel.stat('reblog_cancel'); 5033 } else if ( target.is( 'input[type="submit"]' ) ) { 5034 e.preventDefault(); 5035 e.stopPropagation(); 5036 5037 var note = $('#carousel-reblog-box textarea').val(); 5038 if ( jetpackCarouselStrings.reblog_add_thoughts === note ) { 5039 note = ''; 5040 } 5041 5042 $('#carousel-reblog-submit').val( jetpackCarouselStrings.reblogging ); 5043 $('#carousel-reblog-submit').prop('disabled', true); 5044 $( '#carousel-reblog-box div.submit span.canceltext' ).spin( 'small' ); 5045 5046 $.post( jetpackCarouselStrings.ajaxurl, { 5047 'action': 'post_reblog', 5048 'reblog_source': 'carousel', 5049 'original_blog_id': $('#carousel-reblog-box input#carousel-reblog-blog-id').val(), 5050 'original_post_id': $('.jp-carousel div.selected').data('attachment-id'), 5051 'blog_id': $('#carousel-reblog-box select').val(), 5052 'blog_url': $('#carousel-reblog-box input#carousel-reblog-blog-url').val(), 5053 'blog_title': $('#carousel-reblog-box input#carousel-reblog-blog-title').val(), 5054 'post_url': $('#carousel-reblog-box input#carousel-reblog-post-url').val(), 5055 'post_title': slide.data( 'caption' ) || $('#carousel-reblog-box input#carousel-reblog-post-title').val(), 5056 'note': note, 5057 '_wpnonce': $('#carousel-reblog-box #_wpnonce').val() 5058 }, 5059 function(/*result*/) { 5060 $('#carousel-reblog-box').css({ 'height': $('#carousel-reblog-box').height() + 'px' }).slideUp('fast'); 5061 $('a.jp-carousel-reblog').html( jetpackCarouselStrings.reblogged ).removeClass( 'reblog' ).addClass( 'reblogged' ); 5062 $( '#carousel-reblog-box div.submit span.canceltext' ).spin( false ); 5063 $('#carousel-reblog-submit').val( jetpackCarouselStrings.post_reblog ); 5064 $('div.jp-carousel-info').children().not('#carousel-reblog-box').fadeIn('fast'); 5065 slide.data('reblogged', 1); 5066 $('div.gallery').find('img[data-attachment-id="' + slide.data('attachment-id') + '"]').data('reblogged', 1); 5067 5068 5069 }, 'json' ); 5070 wpcom.carousel.stat('reblog_submit'); 5071 } 5072 } else if ( target.hasClass( 'jp-carousel-image-download' ) ) { 5073 wpcom.carousel.stat( 'download_original_click' ); 5074// @end-hide-in-jetpack 5075 } else if ( target.hasClass( 'jp-carousel-commentlink' ) ) { 5076 e.preventDefault(); 5077 e.stopPropagation(); 5078 $( window ).unbind( 'keydown', keyListener ); 5079 container.animate( { scrollTop: parseInt( info.position()[ 'top' ], 10 ) }, 'fast' ); 5080 $( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' ); 5081 $( '#jp-carousel-comment-form-comment-field' ).focus(); 5082 } else if ( target.hasClass( 'jp-carousel-comment-login' ) ) { 5083 var url = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachment_id; 5084 5085 window.location.href = url; 5086 } else if ( target.parents( '#jp-carousel-comment-form-container' ).length ) { 5087 var textarea = $( '#jp-carousel-comment-form-comment-field' ) 5088 .blur( function() { 5089 $( window ).bind( 'keydown', keyListener ); 5090 } ) 5091 .focus( function() { 5092 $( window ).unbind( 'keydown', keyListener ); 5093 } ); 5094 5095 var emailField = $( '#jp-carousel-comment-form-email-field' ) 5096 .blur( function() { 5097 $( window ).bind( 'keydown', keyListener ); 5098 } ) 5099 .focus( function() { 5100 $( window ).unbind( 'keydown', keyListener ); 5101 } ); 5102 5103 var authorField = $( '#jp-carousel-comment-form-author-field' ) 5104 .blur( function() { 5105 $( window ).bind( 'keydown', keyListener ); 5106 } ) 5107 .focus( function() { 5108 $( window ).unbind( 'keydown', keyListener ); 5109 } ); 5110 5111 var urlField = $( '#jp-carousel-comment-form-url-field' ) 5112 .blur( function() { 5113 $( window ).bind( 'keydown', keyListener ); 5114 } ) 5115 .focus( function() { 5116 $( window ).unbind( 'keydown', keyListener ); 5117 } ); 5118 5119 if ( textarea && textarea.attr( 'id' ) === target.attr( 'id' ) ) { 5120 // For first page load 5121 $( window ).unbind( 'keydown', keyListener ); 5122 $( '#jp-carousel-comment-form-submit-and-info-wrapper' ).slideDown( 'fast' ); 5123 } else if ( target.is( 'input[type="submit"]' ) ) { 5124 e.preventDefault(); 5125 e.stopPropagation(); 5126 5127 $( '#jp-carousel-comment-form-spinner' ).spin( 'small', 'white' ); 5128 5129 var ajaxData = { 5130 action: 'post_attachment_comment', 5131 nonce: jetpackCarouselStrings.nonce, 5132 blog_id: data[ 'blog_id' ], 5133 id: attachment_id, 5134 comment: textarea.val(), 5135 }; 5136 5137 if ( ! ajaxData[ 'comment' ].length ) { 5138 gallery.jp_carousel( 'postCommentError', { 5139 field: 'jp-carousel-comment-form-comment-field', 5140 error: jetpackCarouselStrings.no_comment_text, 5141 } ); 5142 return; 5143 } 5144 5145 if ( 1 !== Number( jetpackCarouselStrings.is_logged_in ) ) { 5146 ajaxData[ 'email' ] = emailField.val(); 5147 ajaxData[ 'author' ] = authorField.val(); 5148 ajaxData[ 'url' ] = urlField.val(); 5149 5150 if ( 1 === Number( jetpackCarouselStrings.require_name_email ) ) { 5151 if ( ! ajaxData[ 'email' ].length || ! ajaxData[ 'email' ].match( '@' ) ) { 5152 gallery.jp_carousel( 'postCommentError', { 5153 field: 'jp-carousel-comment-form-email-field', 5154 error: jetpackCarouselStrings.no_comment_email, 5155 } ); 5156 return; 5157 } else if ( ! ajaxData[ 'author' ].length ) { 5158 gallery.jp_carousel( 'postCommentError', { 5159 field: 'jp-carousel-comment-form-author-field', 5160 error: jetpackCarouselStrings.no_comment_author, 5161 } ); 5162 return; 5163 } 5164 } 5165 } 5166 5167 $.ajax( { 5168 type: 'POST', 5169 url: jetpackCarouselStrings.ajaxurl, 5170 data: ajaxData, 5171 dataType: 'json', 5172 success: function( response /*, status, xhr*/ ) { 5173 if ( 'approved' === response.comment_status ) { 5174 $( '#jp-carousel-comment-post-results' ) 5175 .slideUp( 'fast' ) 5176 .html( 5177 '<span class="jp-carousel-comment-post-success">' + 5178 jetpackCarouselStrings.comment_approved + 5179 '</span>' 5180 ) 5181 .slideDown( 'fast' ); 5182 } else if ( 'unapproved' === response.comment_status ) { 5183 $( '#jp-carousel-comment-post-results' ) 5184 .slideUp( 'fast' ) 5185 .html( 5186 '<span class="jp-carousel-comment-post-success">' + 5187 jetpackCarouselStrings.comment_unapproved + 5188 '</span>' 5189 ) 5190 .slideDown( 'fast' ); 5191 } else { 5192 // 'deleted', 'spam', false 5193 $( '#jp-carousel-comment-post-results' ) 5194 .slideUp( 'fast' ) 5195 .html( 5196 '<span class="jp-carousel-comment-post-error">' + 5197 jetpackCarouselStrings.comment_post_error + 5198 '</span>' 5199 ) 5200 .slideDown( 'fast' ); 5201 } 5202 gallery.jp_carousel( 'clearCommentTextAreaValue' ); 5203 gallery.jp_carousel( 'getComments', { 5204 attachment_id: attachment_id, 5205 offset: 0, 5206 clear: true, 5207 } ); 5208 $( '#jp-carousel-comment-form-button-submit' ).val( 5209 jetpackCarouselStrings.post_comment 5210 ); 5211 $( '#jp-carousel-comment-form-spinner' ).spin( false ); 5212 }, 5213 error: function(/*xhr, status, error*/) { 5214 // TODO: Add error handling and display here 5215 gallery.jp_carousel( 'postCommentError', { 5216 field: 'jp-carousel-comment-form-comment-field', 5217 error: jetpackCarouselStrings.comment_post_error, 5218 } ); 5219 return; 5220 }, 5221 } ); 5222 } 5223 } else if ( ! target.parents( '.jp-carousel-info' ).length ) { 5224 container.jp_carousel( 'next' ); 5225 } 5226 } ) 5227 .bind( 'jp_carousel.afterOpen', function() { 5228 $( window ).bind( 'keydown', keyListener ); 5229 $( window ).bind( 'resize', resizeListener ); 5230 gallery.opened = true; 5231 5232 resizeListener(); 5233 } ) 5234 .bind( 'jp_carousel.beforeClose', function() { 5235 var scroll = $( window ).scrollTop(); 5236 5237 $( window ).unbind( 'keydown', keyListener ); 5238 $( window ).unbind( 'resize', resizeListener ); 5239 $( window ).scrollTop( scroll ); 5240 $( '.jp-carousel-previous-button' ).hide(); 5241 $( '.jp-carousel-next-button' ).hide(); 5242 // Set height to original value 5243 // Fix some themes where closing carousel brings view back to top 5244 $( 'html' ).css( 'height', '' ); 5245 5246 gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack 5247 }) 5248 .bind( 'jp_carousel.afterClose', function() { 5249 if ( window.location.hash && history.back ) { 5250 history.back(); 5251 } 5252 last_known_location_hash = ''; 5253 gallery.opened = false;
5254 } ) 5255 .on( 'transitionend.jp-carousel ', '.jp-carousel-slide', function( e ) { 5256 // If the movement transitions take more than twice the allotted time, disable them. 5257 // There is some wiggle room in the 2x, since some of that time is taken up in 5258 // JavaScript, setting up the transition and calling the events. 5259 if ( 'transform' === e.originalEvent.propertyName ) { 5260 var transitionMultiplier = 5261 ( Date.now() - transitionBegin ) / 1000 / e.originalEvent.elapsedTime; 5262 5263 container.off( 'transitionend.jp-carousel' ); 5264 5265 if ( transitionMultiplier >= 2 ) { 5266 $( '.jp-carousel-transitions' ).removeClass( 'jp-carousel-transitions' ); 5267 } 5268 } 5269 } ); 5270 5271 $( '.jp-carousel-wrap' ).touchwipe( { 5272 wipeLeft: function( e ) { 5273 e.preventDefault(); 5274 gallery.jp_carousel( 'next' ); 5275 }, 5276 wipeRight: function( e ) { 5277 e.preventDefault(); 5278 gallery.jp_carousel( 'previous' ); 5279 }, 5280 preventDefaultEvents: false, 5281 } ); 5282 5283 nextButton.add( previousButton ).click( function( e ) { 5284 e.preventDefault(); 5285 e.stopPropagation(); 5286 if ( nextButton.is( this ) ) { 5287 gallery.jp_carousel( 'next' ); 5288 } else { 5289 gallery.jp_carousel( 'previous' ); 5290 } 5291 } ); 5292 } 5293 }; 5294 5295 var processSingleImageGallery = function() { 5296 // process links that contain img tag with attribute data-attachment-id 5297 $( 'a img[data-attachment-id]' ).each( function() { 5298 var container = $( this ).parent(); 5299 5300 // skip if image was already added to gallery by shortcode 5301 if ( container.parent( '.gallery-icon' ).length ) { 5302 return; 5303 } 5304 5305 // skip if the container is not a link 5306 if ( 'undefined' === typeof $( container ).attr( 'href' ) ) { 5307 return; 5308 } 5309 5310 var valid = false; 5311 5312 // if link points to 'Media File' (ignoring GET parameters) and flag is set allow it 5313 if ( 5314 $( container ) 5315 .attr( 'href' ) 5316 .split( '?' )[ 0 ] === 5317 $( this ) 5318 .attr( 'data-orig-file' ) 5319 .split( '?' )[ 0 ] && 5320 1 === Number( jetpackCarouselStrings.single_image_gallery_media_file ) 5321 ) { 5322 valid = true; 5323 } 5324 5325 // if link points to 'Attachment Page' allow it 5326 if ( $( container ).attr( 'href' ) === $( this ).attr( 'data-permalink' ) ) { 5327 valid = true; 5328 } 5329 5330 // links to 'Custom URL' or 'Media File' when flag not set are not valid 5331 if ( ! valid ) { 5332 return; 5333 } 5334 5335 // make this node a gallery recognizable by event listener above 5336 $( container ).addClass( 'single-image-gallery' ); 5337 // blog_id is needed to allow posting comments to correct blog 5338 $( container ).data( 'carousel-extra', { 5339 blog_id: Number( jetpackCarouselStrings.blog_id ), 5340 } ); 5341 } ); 5342 }; 5343 5344 var methods = { 5345 testForData: function( gallery ) { 5346 gallery = $( gallery ); // make sure we have it as a jQuery object. 5347 return ! ( ! gallery.length || ! gallery.data( 'carousel-extra' ) ); 5348 }, 5349 5350 testIfOpened: function() { 5351 return !! ( 5352 'undefined' !== typeof gallery && 5353 'undefined' !== typeof gallery.opened && 5354 gallery.opened 5355 ); 5356 }, 5357 5358 openOrSelectSlide: function( index ) { 5359 // The `open` method triggers an asynchronous effect, so we will get an 5360 // error if we try to use `open` then `selectSlideAtIndex` immediately 5361 // after it. We can only use `selectSlideAtIndex` if the carousel is 5362 // already open. 5363 if ( ! $( this ).jp_carousel( 'testIfOpened' ) ) { 5364 // The `open` method selects the correct slide during the 5365 // initialization. 5366 $( this ).jp_carousel( 'open', { start_index: index } ); 5367 } else { 5368 gallery.jp_carousel( 'selectSlideAtIndex', index ); 5369 } 5370 }, 5371 5372 open: function( options ) { 5373 var settings = { 5374 items_selector: 5375 '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id]', 5376 start_index: 0, 5377 }, 5378 data = $( this ).data( 'carousel-extra' ); 5379 5380 if ( ! data ) { 5381 return; // don't run if the default gallery functions weren't used 5382 } 5383 5384 prepareGallery( data ); 5385 5386 if ( gallery.jp_carousel( 'testIfOpened' ) ) { 5387 return; // don't open if already opened 5388 } 5389 5390 // make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger 5391 // infiniscroll for it when enabled (Reader, theme infiniscroll, etc). 5392 originalOverflow = $( 'body' ).css( 'overflow' ); 5393 $( 'body' ).css( 'overflow', 'hidden' ); 5394 // prevent html from overflowing on some of the new themes. 5395 originalHOverflow = $( 'html' ).css( 'overflow' ); 5396 $( 'html' ).css( 'overflow', 'hidden' ); 5397 scrollPos = $( window ).scrollTop(); 5398 5399 container.data( 'carousel-extra', data ); 5400// @start-hide-in-jetpack 5401 wpcom.carousel.stat( ['open', 'view_image'] ); 5402// @end-hide-in-jetpack 5403 5404 return this.each( function() { 5405 // If options exist, lets merge them 5406 // with our default settings 5407 var $this = $( this ); 5408 5409 if ( options ) { 5410 $.extend( settings, options ); 5411 } 5412 if ( -1 === settings.start_index ) { 5413 settings.start_index = 0; //-1 returned if can't find index, so start from beginning 5414 } 5415 5416 container.trigger( 'jp_carousel.beforeOpen' ).fadeIn( 'fast', function() { 5417 container.trigger( 'jp_carousel.afterOpen' ); 5418 gallery 5419 .jp_carousel( 5420 'initSlides', 5421 $this.find( settings.items_selector ), 5422 settings.start_index 5423 ) 5424 .jp_carousel( 'selectSlideAtIndex', settings.start_index ); 5425 } ); 5426 gallery.html( '' ); 5427 } ); 5428 }, 5429 5430 selectSlideAtIndex: function( index ) { 5431 var slides = this.jp_carousel( 'slides' ), 5432 selected = slides.eq( index ); 5433 5434 if ( 0 === selected.length ) { 5435 selected = slides.eq( 0 ); 5436 } 5437 5438 gallery.jp_carousel( 'selectSlide', selected, false ); 5439 return this; 5440 }, 5441 5442 close: function() { 5443 // make sure to let the page scroll again 5444 $( 'body' ).css( 'overflow', originalOverflow ); 5445 $( 'html' ).css( 'overflow', originalHOverflow ); 5446 this.jp_carousel( 'clearCommentTextAreaValue' ); 5447 return container.trigger( 'jp_carousel.beforeClose' ).fadeOut( 'fast', function() { 5448 container.trigger( 'jp_carousel.afterClose' ); 5449 $( window ).scrollTop( scrollPos ); 5450 } ); 5451 }, 5452 5453 next: function() { 5454 this.jp_carousel( 'previousOrNext', 'nextSlide' ); 5455 gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack 5456 }, 5457 5458 previous: function() { 5459 this.jp_carousel( 'previousOrNext', 'prevSlide' ); 5460 gallery.jp_carousel( 'hide_reblog_box' ); // @hide-in-jetpack 5461 }, 5462 5463 previousOrNext: function( slideSelectionMethodName ) { 5464 if ( ! this.jp_carousel( 'hasMultipleImages' ) ) { 5465 return false;
5466 } 5467 5468 var slide = gallery.jp_carousel( slideSelectionMethodName ); 5469 5470 if ( slide ) { 5471 container.animate( { scrollTop: 0 }, 'fast' ); 5472 this.jp_carousel( 'clearCommentTextAreaValue' ); 5473 this.jp_carousel( 'selectSlide', slide ); 5474 wpcom.carousel.stat( ['previous', 'view_image'] ); // @hide-in-jetpack 5475 } 5476 }, 5477 5478 // @start-hide-in-jetpack 5479 resetButtons : function(current) { 5480 if ( current.data( 'reblogged' ) ) { 5481 $('.jp-carousel-buttons a.jp-carousel-reblog').addClass( 'reblogged' ).text( jetpackCarouselStrings.reblogged ); 5482 } else { 5483 $('.jp-carousel-buttons a.jp-carousel-reblog').removeClass( 'reblogged' ).text( jetpackCarouselStrings.reblog ); 5484 } 5485 // Must also take care of reblog/reblogged here 5486 }, 5487 // @end-hide-in-jetpack 5488 5489 5490 selectedSlide: function() { 5491 return this.find( '.selected' ); 5492 }, 5493 5494 setSlidePosition: function( x ) { 5495 transitionBegin = Date.now(); 5496 5497 return this.css( { 5498 '-webkit-transform': 'translate3d(' + x + 'px,0,0)', 5499 '-moz-transform': 'translate3d(' + x + 'px,0,0)', 5500 '-ms-transform': 'translate(' + x + 'px,0)', 5501 '-o-transform': 'translate(' + x + 'px,0)', 5502 transform: 'translate3d(' + x + 'px,0,0)', 5503 } ); 5504 }, 5505 5506 updateSlidePositions: function( animate ) { 5507 var current = this.jp_carousel( 'selectedSlide' ), 5508 galleryWidth = gallery.width(), 5509 currentWidth = current.width(), 5510 previous = gallery.jp_carousel( 'prevSlide' ), 5511 next = gallery.jp_carousel( 'nextSlide' ), 5512 previousPrevious = previous.prev(), 5513 nextNext = next.next(), 5514 left = Math.floor( ( galleryWidth - currentWidth ) * 0.5 ); 5515 5516 current.jp_carousel( 'setSlidePosition', left ).show(); 5517 5518 // minimum width 5519 gallery.jp_carousel( 'fitInfo', animate ); 5520 5521 // prep the slides 5522 var direction = lastSelectedSlide.is( current.prevAll() ) ? 1 : -1; 5523 5524 // Since we preload the `previousPrevious` and `nextNext` slides, we need 5525 // to make sure they technically visible in the DOM, but invisible to the 5526 // user. To hide them from the user, we position them outside the edges 5527 // of the window. 5528 // 5529 // This section of code only applies when there are more than three 5530 // slides. Otherwise, the `previousPrevious` and `nextNext` slides will 5531 // overlap with the `previous` and `next` slides which must be visible 5532 // regardless. 5533 if ( 1 === direction ) { 5534 if ( ! nextNext.is( previous ) ) { 5535 nextNext.jp_carousel( 'setSlidePosition', galleryWidth + next.width() ).show(); 5536 } 5537 5538 if ( ! previousPrevious.is( next ) ) { 5539 previousPrevious 5540 .jp_carousel( 'setSlidePosition', -previousPrevious.width() - currentWidth ) 5541 .show(); 5542 } 5543 } else { 5544 if ( ! nextNext.is( previous ) ) { 5545 nextNext.jp_carousel( 'setSlidePosition', galleryWidth + currentWidth ).show(); 5546 } 5547 } 5548 5549 previous 5550 .jp_carousel( 'setSlidePosition', Math.floor( -previous.width() + screenPadding * 0.75 ) ) 5551 .show(); 5552 next 5553 .jp_carousel( 'setSlidePosition', Math.ceil( galleryWidth - screenPadding * 0.75 ) ) 5554 .show(); 5555 }, 5556 5557 selectSlide: function( slide, animate ) { 5558 lastSelectedSlide = this.find( '.selected' ).removeClass( 'selected' ); 5559 5560 var slides = gallery.jp_carousel( 'slides' ).css( { position: 'fixed' } ), 5561 current = $( slide ) 5562 .addClass( 'selected' ) 5563 .css( { position: 'relative' } ), 5564 attachmentId = current.data( 'attachment-id' ), 5565 previous = gallery.jp_carousel( 'prevSlide' ), 5566 next = gallery.jp_carousel( 'nextSlide' ), 5567 previousPrevious = previous.prev(), 5568 nextNext = next.next(), 5569 animated, 5570 captionHtml; 5571 5572 // center the main image 5573 gallery.jp_carousel( 'loadFullImage', current ); 5574 5575 caption.hide(); 5576 5577 if ( next.length === 0 && slides.length <= 2 ) { 5578 $( '.jp-carousel-next-button' ).hide(); 5579 } else { 5580 $( '.jp-carousel-next-button' ).show(); 5581 } 5582 5583 if ( previous.length === 0 && slides.length <= 2 ) { 5584 $( '.jp-carousel-previous-button' ).hide(); 5585 } else { 5586 $( '.jp-carousel-previous-button' ).show(); 5587 } 5588 5589 animated = current 5590 .add( previous ) 5591 .add( previousPrevious ) 5592 .add( next ) 5593 .add( nextNext ) 5594 .jp_carousel( 'loadSlide' ); 5595 5596 // slide the whole view to the x we want 5597 slides.not( animated ).hide(); 5598 5599 gallery.jp_carousel( 'updateSlidePositions', animate ); 5600 5601 gallery.jp_carousel( 'resetButtons', current ); // @hide-in-jetpack 5602 container.trigger( 'jp_carousel.selectSlide', [ current ] ); 5603 5604 gallery.jp_carousel( 'getTitleDesc', { 5605 title: current.data( 'title' ), 5606 desc: current.data( 'desc' ), 5607 } ); 5608 5609 var imageMeta = current.data( 'image-meta' ); 5610 gallery.jp_carousel( 'updateExif', imageMeta ); 5611 gallery.jp_carousel( 'updateFullSizeLink', current ); 5612 gallery.jp_carousel( 'updateMap', imageMeta ); 5613 gallery.jp_carousel( 'testCommentsOpened', current.data( 'comments-opened' ) ); 5614 gallery.jp_carousel( 'getComments', { 5615 attachment_id: attachmentId, 5616 offset: 0, 5617 clear: true, 5618 } );
5619 $( '#jp-carousel-comment-post-results' ).slideUp(); 5620 5621 // $('<div />').text(sometext).html() is a trick to go to HTML to plain 5622 // text (including HTML entities decode, etc) 5623 if ( current.data( 'caption' ) ) { 5624 captionHtml = $( '<div />' ) 5625 .text( current.data( 'caption' ) ) 5626 .html(); 5627 5628 if ( 5629 captionHtml === 5630 $( '<div />' ) 5631 .text( current.data( 'title' ) ) 5632 .html() 5633 ) { 5634 $( '.jp-carousel-titleanddesc-title' ) 5635 .fadeOut( 'fast' ) 5636 .empty(); 5637 } 5638 5639 if ( 5640 captionHtml === 5641 $( '<div />' ) 5642 .text( current.data( 'desc' ) ) 5643 .html() 5644 ) { 5645 $( '.jp-carousel-titleanddesc-desc' ) 5646 .fadeOut( 'fast' ) 5647 .empty(); 5648 } 5649 5650 caption.html( current.data( 'caption' ) ).fadeIn( 'slow' ); 5651 } else { 5652 caption.fadeOut( 'fast' ).empty(); 5653 } 5654 5655 // Record pageview in WP Stats, for each new image loaded full-screen. 5656 if ( jetpackCarouselStrings.stats ) { 5657 new Image().src = 5658 document.location.protocol + 5659 '../g.gif' + 5660 jetpackCarouselStrings.stats + 5661 '&post=' + 5662 encodeURIComponent( attachmentId ) + 5663 '&rand=' + 5664 Math.random(); 5665 } 5666 wpcom.carousel.pageview( attachmentId ); // @hide-in-jetpack 5667 // Load the images for the next and previous slides. 5668 $( next ) 5669 .add( previous ) 5670 .each( function() { 5671 gallery.jp_carousel( 'loadFullImage', $( this ) ); 5672 } ); 5673 5674 window.location.hash = last_known_location_hash = '#jp-carousel-' + attachmentId; 5675 }, 5676 5677 slides: function() { 5678 return this.find( '.jp-carousel-slide' ); 5679 }, 5680 5681 slideDimensions: function() { 5682 return { 5683 width: $( window ).width() - screenPadding * 2, 5684 height: Math.floor( ( $( window ).height() / 100 ) * proportion - 60 ), 5685 }; 5686 }, 5687 5688 loadSlide: function() { 5689 return this.each( function() { 5690 var slide = $( this ); 5691 slide.find( 'img' ).one( 'load', function() { 5692 // set the width/height of the image if it's too big 5693 slide.jp_carousel( 'fitSlide', false ); 5694 } ); 5695 } ); 5696 }, 5697 5698 bestFit: function() { 5699 var max = gallery.jp_carousel( 'slideDimensions' ), 5700 orig = this.jp_carousel( 'originalDimensions' ), 5701 orig_ratio = orig.width / orig.height, 5702 w_ratio = 1, 5703 h_ratio = 1, 5704 width, 5705 height; 5706 5707 if ( orig.width > max.width ) { 5708 w_ratio = max.width / orig.width; 5709 } 5710 if ( orig.height > max.height ) { 5711 h_ratio = max.height / orig.height; 5712 } 5713 5714 if ( w_ratio < h_ratio ) { 5715 width = max.width; 5716 height = Math.floor( width / orig_ratio ); 5717 } else if ( h_ratio < w_ratio ) { 5718 height = max.height; 5719 width = Math.floor( height * orig_ratio ); 5720 } else { 5721 width = orig.width; 5722 height = orig.height; 5723 } 5724 5725 return { 5726 width: width, 5727 height: height, 5728 }; 5729 }, 5730 5731 fitInfo: function(/*animated*/) { 5732 var current = this.jp_carousel( 'selectedSlide' ), 5733 size = current.jp_carousel( 'bestFit' ); 5734 5735 photo_info.css( { 5736 left: Math.floor( ( info.width() - size.width ) * 0.5 ), 5737 width: Math.floor( size.width ), 5738 } ); 5739 5740 return this; 5741 }, 5742 5743 fitMeta: function( animated ) { 5744 var newInfoTop = { 5745 top: Math.floor( ( $( window ).height() / 100 ) * proportion + 5 ) + 'px', 5746 }; 5747 var newLeftWidth = { width: info.width() - ( imageMeta.width() + 80 ) + 'px' }; 5748 5749 if ( animated ) { 5750 info.animate( newInfoTop ); 5751 leftColWrapper.animate( newLeftWidth ); 5752 } else { 5753 info.animate( newInfoTop ); 5754 leftColWrapper.css( newLeftWidth ); 5755 } 5756 }, 5757 5758 fitSlide: function(/*animated*/) { 5759 return this.each( function() { 5760 var $this = $( this ), 5761 dimensions = $this.jp_carousel( 'bestFit' ), 5762 method = 'css', 5763 max = gallery.jp_carousel( 'slideDimensions' ); 5764 5765 dimensions.left = 0; 5766 dimensions.top = Math.floor( ( max.height - dimensions.height ) * 0.5 ) + 40; 5767 $this[ method ]( dimensions ); 5768 } ); 5769 }, 5770 5771 texturize: function( text ) { 5772 text = '' + text; // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace(). 5773 text = text 5774 .replace( /'/g, '’' ) 5775 .replace( /'/g, '’' ) 5776 .replace( /[\u2019]/g, '’' ); 5777 text = text 5778 .replace( /"/g, '”' ) 5779 .replace( /"/g, '”' ) 5780 .replace( /"/g, '”' ) 5781 .replace( /[\u201D]/g, '”' ); 5782 text = text.replace( /([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"' ); // untexturize allowed HTML tags params double-quotes 5783 return $.trim( text ); 5784 }, 5785 5786 initSlides: function( items, start_index ) { 5787 if ( items.length < 2 ) { 5788 $( '.jp-carousel-next-button, .jp-carousel-previous-button' ).hide(); 5789 } else { 5790 $( '.jp-carousel-next-button, .jp-carousel-previous-button' ).show(); 5791 } 5792 5793 // Calculate the new src. 5794 items.each( function(/*i*/) { 5795 var src_item = $( this ), 5796 orig_size = src_item.data( 'orig-size' ) || '', 5797 max = gallery.jp_carousel( 'slideDimensions' ), 5798 parts = orig_size.split( ',' ), 5799 medium_file = src_item.data( 'medium-file' ) || '', 5800 large_file = src_item.data( 'large-file' ) || '', 5801 src; 5802 orig_size = { width: parseInt( parts[ 0 ], 10 ), height: parseInt( parts[ 1 ], 10 ) }; 5803 5804// @start-hide-in-jetpack 5805 if ( 'undefined' !== typeof wpcom ) { 5806 src = src_item.attr('src') || src_item.attr('original') || src_item.data('original') || src_item.data('lazy-src'); 5807 if (src.indexOf('imgpress') !== -1) { 5808 src = src_item.data('orig-file'); 5809 } 5810 src = wpcom.carousel.addWidthToImageURL( src, wpcom.carousel.findFirstLargeEnoughWidth( orig_size.width, orig_size.height, max.width, max.height ) ); 5811 } else { 5812 5813// @end-hide-in-jetpack 5814 src = src_item.data( 'orig-file' ); 5815 5816 src = gallery.jp_carousel( 'selectBestImageSize', { 5817 orig_file: src, 5818 orig_width: orig_size.width, 5819 orig_height: orig_size.height, 5820 max_width: max.width, 5821 max_height: max.height, 5822 medium_file: medium_file, 5823 large_file: large_file, 5824 } ); 5825// @start-hide-in-jetpack 5826 } // end else of if ( 'undefined' != typeof wpcom ) 5827// @end-hide-in-jetpack 5828 5829 // Set the final src 5830 $( this ).data( 'gallery-src', src ); 5831 } ); 5832 5833 // If the start_index is not 0 then preload the clicked image first. 5834 if ( 0 !== start_index ) { 5835 $( '<img/>' )[ 0 ].src = $( items[ start_index ] ).data( 'gallery-src' ); 5836 } 5837 5838 var useInPageThumbnails = 5839 items.first().closest( '.tiled-gallery.type-rectangular' ).length > 0; 5840 5841 // create the 'slide' 5842 items.each( function( i ) { 5843 var src_item = $( this ), 5844 reblogged = src_item.data( 'reblogged' ) || 0, // @hide-in-jetpack 5845 attachment_id = src_item.data( 'attachment-id' ) || 0, 5846 comments_opened = src_item.data( 'comments-opened' ) || 0, 5847 image_meta = src_item.data( 'image-meta' ) || {}, 5848 orig_size = src_item.data( 'orig-size' ) || '',
5849 thumb_size = { width: src_item[ 0 ].naturalWidth, height: src_item[ 0 ].naturalHeight }, 5850 title = src_item.data( 'image-title' ) || '', 5851 description = src_item.data( 'image-description' ) || '', 5852 caption = 5853 src_item 5854 .parents( '.gallery-item' ) 5855 .find( '.gallery-caption' ) 5856 .html() || '', 5857 src = src_item.data( 'gallery-src' ) || '', 5858 medium_file = src_item.data( 'medium-file' ) || '', 5859 large_file = src_item.data( 'large-file' ) || '', 5860 orig_file = src_item.data( 'orig-file' ) || ''; 5861 5862 var tiledCaption = src_item 5863 .parents( 'div.tiled-gallery-item' ) 5864 .find( 'div.tiled-gallery-caption' ) 5865 .html(); 5866 if ( tiledCaption ) { 5867 caption = tiledCaption; 5868 } 5869 5870 if ( attachment_id && orig_size.length ) { 5871 title = gallery.jp_carousel( 'texturize', title ); 5872 description = gallery.jp_carousel( 'texturize', description ); 5873 caption = gallery.jp_carousel( 'texturize', caption ); 5874 5875 // Initially, the image is a 1x1 transparent gif. The preview is shown as a background image on the slide itself. 5876 var image = $( '<img/>' ) 5877 .attr( 5878 'src', 5879 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' 5880 ) 5881 .css( 'width', '100%' ) 5882 .css( 'height', '100%' ); 5883 5884 var slide = $( 5885 '<div class="jp-carousel-slide" itemprop="associatedMedia" itemscope itemtype="https://schema.org/ImageObject"></div>' 5886 ) 5887 .hide() 5888 .css( { 5889 //'position' : 'fixed', 5890 left: i < start_index ? -1000 : gallery.width(), 5891 } ) 5892 .append( image ) 5893 .appendTo( gallery ) 5894 .data( 'src', src ) 5895 .data( 'title', title ) 5896 .data( 'desc', description ) 5897 .data( 'caption', caption ) 5898 .data( 'attachment-id', attachment_id ) 5899 .data( 'permalink', src_item.parents( 'a' ).attr( 'href' ) ) 5900 .data( 'orig-size', orig_size ) 5901 .data( 'comments-opened', comments_opened ) 5902 .data( 'image-meta', image_meta ) 5903 .data( 'medium-file', medium_file ) 5904 .data( 'large-file', large_file ) 5905 .data( 'orig-file', orig_file ) 5906 .data( 'thumb-size', thumb_size ) 5907 .data( 'reblogged', reblogged ) // @hide-in-jetpack 5908 ; 5909 5910 if ( useInPageThumbnails ) { 5911 // Use the image already loaded in the gallery as a preview. 5912 slide.data( 'preview-image', src_item.attr( 'src' ) ).css( { 5913 'background-image': 'url("' + src_item.attr( 'src' ) + '")', 5914 'background-size': '100% 100%', 5915 'background-position': 'center center', 5916 } ); 5917 } 5918 5919 slide.jp_carousel( 'fitSlide', false ); 5920 } 5921 } ); 5922 return this; 5923 }, 5924 5925 selectBestImageSize: function( args ) { 5926 if ( 'object' !== typeof args ) { 5927 args = {}; 5928 } 5929 5930 if ( 'undefined' === typeof args.orig_file ) { 5931 return ''; 5932 } 5933 5934 if ( 'undefined' === typeof args.orig_width || 'undefined' === typeof args.max_width ) { 5935 return args.orig_file; 5936 } 5937 5938 if ( 'undefined' === typeof args.medium_file || 'undefined' === typeof args.large_file ) { 5939 return args.orig_file; 5940 } 5941 5942 // Check if the image is being served by Photon (using a regular expression on the hostname). 5943 5944 var imageLinkParser = document.createElement( 'a' ); 5945 imageLinkParser.href = args.large_file; 5946 5947 var isPhotonUrl = /^i[0-2].wp.com$/i.test( imageLinkParser.hostname ); 5948 5949 var medium_size_parts = gallery.jp_carousel( 5950 'getImageSizeParts', 5951 args.medium_file, 5952 args.orig_width, 5953 isPhotonUrl 5954 ); 5955 var large_size_parts = gallery.jp_carousel( 5956 'getImageSizeParts', 5957 args.large_file, 5958 args.orig_width, 5959 isPhotonUrl 5960 ); 5961 5962 var large_width = parseInt( large_size_parts[ 0 ], 10 ), 5963 large_height = parseInt( large_size_parts[ 1 ], 10 ), 5964 medium_width = parseInt( medium_size_parts[ 0 ], 10 ), 5965 medium_height = parseInt( medium_size_parts[ 1 ], 10 ); 5966 5967 // Assign max width and height. 5968 args.orig_max_width = args.max_width; 5969 args.orig_max_height = args.max_height; 5970 5971 // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android ph
5971ones = 1.5, etc) 5972 if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) { 5973 args.max_width = args.max_width * window.devicePixelRatio; 5974 args.max_height = args.max_height * window.devicePixelRatio; 5975 } 5976 5977 if ( large_width >= args.max_width || large_height >= args.max_height ) { 5978 return args.large_file; 5979 } 5980 5981 if ( medium_width >= args.max_width || medium_height >= args.max_height ) { 5982 return args.medium_file; 5983 } 5984 5985 if ( isPhotonUrl ) { 5986 // args.orig_file doesn't point to a Photon url, so in this case we use args.large_file 5987 // to return the photon url of the original image. 5988 var largeFileIndex = args.large_file.lastIndexOf( '?' ); 5989 var origPhotonUrl = args.large_file; 5990 if ( -1 !== largeFileIndex ) { 5991 origPhotonUrl = args.large_file.substring( 0, largeFileIndex ); 5992 // If we have a really large image load a smaller version 5993 // that is closer to the viewable size 5994 if ( args.orig_width > args.max_width || args.orig_height > args.max_height ) { 5995 origPhotonUrl += '?fit=' + args.orig_max_width + '%2C' + args.orig_max_height; 5996 } 5997 } 5998 return origPhotonUrl; 5999 } 6000 6001 return args.orig_file; 6002 }, 6003 6004 getImageSizeParts: function( file, orig_width, isPhotonUrl ) { 6005 var size = isPhotonUrl 6006 ? file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' ) 6007 : file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' ); 6008 6009 var size_parts = 6010 size !== file 6011 ? isPhotonUrl 6012 ? size.split( '%2C' ) 6013 : size.split( 'x' ) 6014 : [ orig_width, 0 ]; 6015 6016 // If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url. 6017 // In that case, we set the value to 0. 6018 if ( '9999' === size_parts[ 0 ] ) { 6019 size_parts[ 0 ] = '0'; 6020 } 6021 6022 if ( '9999' === size_parts[ 1 ] ) { 6023 size_parts[ 1 ] = '0'; 6024 } 6025 6026 return size_parts; 6027 }, 6028 6029// @start-hide-in-jetpack 6030 show_reblog_box: function() { 6031 $('#carousel-reblog-box textarea').val(jetpackCarouselStrings.reblog_add_thoughts); 6032 //t.addClass('selected'); 6033 $('#carousel-reblog-box p.response').remove(); 6034 $('#carousel-reblog-box div.submit, #carousel-reblog-box div.submit span.canceltext').show(); 6035 $('#carousel-reblog-box div.submit input[type=submit]').prop('disabled', false); 6036 6037 var current = $('.jp-carousel div.selected'); 6038 $('#carousel-reblog-box input#carousel-reblog-post-url').val( current.data('permalink') ); 6039 $('#carousel-reblog-box input#carousel-reblog-post-title').val( $('div.jp-carousel-info').children('h2').text() ); 6040 6041 $('div.jp-carousel-info').append( $('#carousel-reblog-box') ).children().fadeOut('fast'); 6042 $('#carousel-reblog-box').fadeIn('fast'); 6043 }, 6044 6045 hide_reblog_box: function () { 6046 $( 'div.jp-carousel-info' ).children().not( '#carousel-reblog-box' ).fadeIn( 'fast' ); 6047 $( '#carousel-reblog-box' ).fadeOut( 'fast' ); 6048 }, 6049// @end-hide-in-jetpack 6050 6051 originalDimensions: function() { 6052 var splitted = $( this ) 6053 .data( 'orig-size' ) 6054 .split( ',' ); 6055 return { width: parseInt( splitted[ 0 ], 10 ), height: parseInt( splitted[ 1 ], 10 ) }; 6056 }, 6057 6058 format: function( args ) { 6059 if ( 'object' !== typeof args ) { 6060 args = {}; 6061 } 6062 if ( ! args.text || 'undefined' === typeof args.text ) { 6063 return; 6064 } 6065 if ( ! args.replacements || 'undefined' === typeof args.replacements ) { 6066 return args.text; 6067 } 6068 return args.text.replace( /{(\d+)}/g, function( match, number ) { 6069 return typeof args.replacements[ number ] !== 'undefined' 6070 ? args.replacements[ number ] 6071 : match; 6072 } ); 6073 }, 6074 6075 /** 6076 * Returns a number in a fraction format that represents the shutter speed. 6077 * @param Number speed 6078 * @return String 6079 */ 6080 shutterSpeed: function( speed ) { 6081 var denominator; 6082 6083 // round to one decimal if value > 1s by multiplying it by 10, rounding, then dividing by 10 again 6084 if ( speed >= 1 ) { 6085 return Math.round( speed * 10 ) / 10 + 's'; 6086 } 6087 6088 // If the speed is less than one, we find the denominator by invert
6088ing 6089 // the number. Since cameras usually use rational numbers as shutter 6090 // speeds, we should get a nice round number. Or close to one in cases 6091 // like 1/30. So we round it. 6092 denominator = Math.round( 1 / speed ); 6093 6094 return '1/' + denominator + 's'; 6095 }, 6096 6097 parseTitleDesc: function( value ) { 6098 if ( ! value.match( ' ' ) && value.match( '_' ) ) { 6099 return ''; 6100 } 6101 6102 return value; 6103 }, 6104 6105 getTitleDesc: function( data ) { 6106 var title = '', 6107 desc = '', 6108 markup = '', 6109 target; 6110 6111 target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' ); 6112 target.hide(); 6113 6114 title = gallery.jp_carousel( 'parseTitleDesc', data.title ) || ''; 6115 desc = gallery.jp_carousel( 'parseTitleDesc', data.desc ) || ''; 6116 6117 if ( title.length || desc.length ) { 6118 // Convert from HTML to plain text (including HTML entities decode, etc) 6119 if ( 6120 $( '<div />' ) 6121 .html( title ) 6122 .text() === 6123 $( '<div />' ) 6124 .html( desc ) 6125 .text() 6126 ) { 6127 title = ''; 6128 } 6129 6130 markup = title.length 6131 ? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>' 6132 : ''; 6133 markup += desc.length 6134 ? '<div class="jp-carousel-titleanddesc-desc">' + desc + '</div>' 6135 : ''; 6136 6137 target.html( markup ).fadeIn( 'slow' ); 6138 } 6139 6140 $( 'div#jp-carousel-comment-form-container' ).css( 'margin-top', '20px' ); 6141 $( 'div#jp-carousel-comments-loading' ).css( 'margin-top', '20px' ); 6142 }, 6143 6144 // updateExif updates the contents of the exif UL (.jp-carousel-image-exif) 6145 updateExif: function( meta ) { 6146 if ( ! meta || 1 !== Number( jetpackCarouselStrings.display_exif ) ) { 6147 return false; 6148 } 6149 6150 var $ul = $( "<ul class='jp-carousel-image-exif'></ul>" ); 6151 6152 $.each( meta, function( key, val ) { 6153 if ( 6154 0 === parseFloat( val ) || 6155 ! val.length || 6156 -1 === $.inArray( key, $.makeArray( jetpackCarouselStrings.meta_data ) ) 6157 ) { 6158 return; 6159 } 6160 6161 switch ( key ) { 6162 case 'focal_length': 6163 val = val + 'mm'; 6164 break; 6165 case 'shutter_speed': 6166 val = gallery.jp_carousel( 'shutterSpeed', val ); 6167 break; 6168 case 'aperture': 6169 val = 'f/' + val; 6170 break; 6171 } 6172 6173 $ul.append( '<li><h5>' + jetpackCarouselStrings[ key ] + '</h5>' + val + '</li>' ); 6174 } ); 6175 6176 // Update (replace) the content of the ul 6177 $( 'div.jp-carousel-image-meta ul.jp-carousel-image-exif' ).replaceWith( $ul ); 6178 }, 6179 6180 // updateFullSizeLink updates the contents of the jp-carousel-image-download link 6181 updateFullSizeLink: function( current ) { 6182 if ( ! current || ! current.data ) { 6183 return false; 6184 } 6185 var original, 6186 origSize = current.data( 'orig-size' ).split( ',' ), 6187 imageLinkParser = document.createElement( 'a' ); 6188 6189 imageLinkParser.href = current.data( 'src' ).replace( /\?.+$/, '' ); 6190 6191 // Is this a Photon URL? 6192 if ( imageLinkParser.hostname.match( /^i[\d]{1}.wp.com$/i ) !== null ) { 6193 original = imageLinkParser.href; 6194 } else { 6195 original = current.data( 'orig-file' ).replace( /\?.+$/, '' ); 6196 } 6197 6198 var permalink = $( 6199 '<a>' + 6200 gallery.jp_carousel( 'format', { 6201 text: jetpackCarouselStrings.download_original, 6202 replacements: origSize, 6203 } ) + 6204 '</a>' 6205 ) 6206 .addClass( 'jp-carousel-image-download' ) 6207 .attr( 'href', original ) 6208 .attr( 'target', '_blank' ); 6209 6210 // Update (replace) the content of the anchor 6211 $( 'div.jp-carousel-image-meta a.jp-carousel-image-download' ).replaceWith( permalink ); 6212 }, 6213 6214 updateMap: function( meta ) { 6215 if ( 6216 ! meta.latitude || 6217 ! meta.longitude || 6218 1 !== Number( jetpackCarouselStrings.display_geo ) 6219 ) { 6220 return; 6221 } 6222 6223 var latitude = meta.latitude, 6224 longitude = meta.longitude, 6225 $metabox = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ), 6226 $mapbox = $( '<div></div>' ), 6227 style = 6228 '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91'; 6229 6230 $mapbox 6231 .addClass( 'jp-carousel-image-map' ) 6232 .html( 6233 '<img width="154" height="154" src="https://maps.googleapis.com/maps/api/staticmap?\ 6234 center=' + 6235 latitude + 6236 ',' + 6237 longitude + 6238 '&\ 6239 zoom=8&\ 6240 size=154x154&\ 6241 sensor=false&\ 6242 markers=size:medium%7Ccolor:blue%7C' + 6243 latitude + 6244 ',' + 6245 longitude + 6246 style + 6247 '" class="gmap-main" />\ 6248 \ 6249 <div class="gmap-topright"><div class="imgclip"><img width="175" height="154" src="https://maps.googleapis.com/ma
6249ps/api/staticmap?\ 6250 center=' + 6251 latitude + 6252 ',' + 6253 longitude + 6254 '&\ 6255 zoom=3&\ 6256 size=175x154&\ 6257 sensor=false&\ 6258 markers=size:small%7Ccolor:blue%7C' + 6259 latitude + 6260 ',' + 6261 longitude + 6262 style + 6263 '"c /></div></div>\ 6264 \ 6265 ' 6266 ) 6267 .prependTo( $metabox ); 6268 }, 6269 6270 testCommentsOpened: function( opened ) { 6271 if ( 1 === parseInt( opened, 10 ) ) { 6272// @start-hide-in-jetpack 6273 if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) { 6274 $('.jp-carousel-commentlink').fadeIn('fast'); 6275 } else { 6276// @end-hide-in-jetpack 6277 $( '.jp-carousel-buttons' ).fadeIn( 'fast' ); 6278// @start-hide-in-jetpack 6279 } 6280// @end-hide-in-jetpack 6281 commentForm.fadeIn( 'fast' ); 6282 } else { 6283// @start-hide-in-jetpack 6284 if ( 1 === Number( jetpackCarouselStrings.is_logged_in ) ) { 6285 $('.jp-carousel-commentlink').fadeOut('fast'); 6286 } else { 6287// @end-hide-in-jetpack 6288 $( '.jp-carousel-buttons' ).fadeOut( 'fast' ); 6289// @start-hide-in-jetpack 6290 } 6291// @end-hide-in-jetpack 6292 commentForm.fadeOut( 'fast' ); 6293 } 6294 }, 6295 6296 getComments: function( args ) { 6297 clearInterval( commentInterval ); 6298 6299 if ( 'object' !== typeof args ) { 6300 return; 6301 } 6302 6303 if ( 'undefined' === typeof args.attachment_id || ! args.attachment_id ) { 6304 return; 6305 } 6306 6307 if ( ! args.offset || 'undefined' === typeof args.offset || args.offset < 1 ) { 6308 args.offset = 0; 6309 } 6310 6311 var comments = $( '.jp-carousel-comments' ), 6312 commentsLoading = $( '#jp-carousel-comments-loading' ).show(); 6313 6314 if ( args.clear ) { 6315 comments.hide().empty(); 6316 } 6317 6318 $.ajax( { 6319 type: 'GET', 6320 url: jetpackCarouselStrings.ajaxurl, 6321 dataType: 'json', 6322 data: { 6323 action: 'get_attachment_comments', 6324 nonce: jetpackCarouselStrings.nonce, 6325 id: args.attachment_id, 6326 offset: args.offset, 6327 }, 6328 success: function( data /*, status, xhr*/ ) { 6329 if ( args.clear ) { 6330 comments.fadeOut( 'fast' ).empty(); 6331 } 6332 6333 $( data ).each( function() { 6334 var comment = $( '<div></div>' ) 6335 .addClass( 'jp-carousel-comment' ) 6336 .attr( 'id', 'jp-carousel-comment-' + this[ 'id' ] ) 6337 .html( 6338 '<div class="comment-gravatar">' + 6339 this[ 'gravatar_markup' ] + 6340 '</div>' + 6341 '<div class="comment-author">' + 6342 this[ 'author_markup' ] + 6343 '</div>' + 6344 '<div class="comment-date">' + 6345 this[ 'date_gmt' ] + 6346 '</div>' + 6347 '<div class="comment-content">' + 6348 this[ 'content' ] + 6349 '</div>' 6350 ); 6351 comments.append( comment ); 6352 6353 // Set the interval to check for a new page of comments. 6354 clearInterval( commentInterval ); 6355 commentInterval = setInterval( function() { 6356 if ( 6357 $( '.jp-carousel-overlay' ).height() - 150 < 6358 $( '.jp-carousel-wrap' ).scrollTop() + $( window ).height() 6359 ) { 6360 gallery.jp_carousel( 'getComments', { 6361 attachment_id: args.attachment_id, 6362 offset: args.offset + 10, 6363 clear: false, 6364 } ); 6365 clearInterval( commentInterval ); 6366 } 6367 }, 300 ); 6368 } ); 6369 6370 // Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested 6371 // attachment id might no longer match the current attachment id by the time we get the data back or a now 6372 // registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake. 6373 var current = $( '.jp-carousel div.selected' ); 6374 if ( 6375 current && 6376 current.data && 6377 current.data( 'attachment-id' ) != args.attachment_id // jshint ignore:line 6378 ) { 6379 comments.fadeOut( 'fast' ); 6380 comments.empty(); 6381 return; 6382 } 6383 6384 // Increase the height of the background, semi-transparent overlay to match the new length of the comments list. 6385 $( '.jp-carousel-overlay' ).height( 6386 $( window ).height() + 6387 titleAndDescription.height() + 6388 commentForm.height() + 6389 ( comments.height() > 0 ? comments.height() : imageMeta.height() ) + 6390 200 6391 ); 6392 6393 comments.show(); 6394 commentsLoading.hide(); 6395 }, 6396 error: function( xhr, status, error ) { 6397 // TODO: proper error handling 6398 console.log( 'Comment get fail...', xhr, status, error ); 6399 comments.fadeIn( 'fast' ); 6400 commentsLoading.fadeOut( 'fast' ); 6401 }, 6402 } ); 6403 }, 6404 6405 postCommentError: function( args ) { 6406 if ( 'object' !== typeof args ) { 6407 args = {}; 6408 } 6409 if ( 6410 ! args.field || 6411 'undefined' === typeof args.field || 6412 ! args.error || 6413 'undefined' === typeof args.error 6414 ) { 6415 return; 6416 } 6417 $( '#jp-carousel-comment-post-results' ) 6418 .slideUp( 'fast' ) 6419 .html( '<span class="jp-carousel-comment-post-error">' + args.error + '</span>' ) 6420 .slideDown( 'fast' ); 6421 $( '#jp-carousel-comment-form-spinner' ).spin( false ); 6422 }, 6423 6424 setCommentIframeSrc: function( attachment_id ) { 6425 var iframe = $( '#jp-carousel-comment-iframe' ); 6426 // Set the proper irame src for the current attachment id 6427 if ( iframe && iframe.length ) { 6428 iframe.attr( 'src', iframe.attr( 'src' ).replace( /(postid=)\d+/, '$1' + attachment_id ) ); 6429 iframe.attr( 6430 'src', 6431 iframe.attr( 'src' ).replace( /(%23.+)?$/, '%23jp-carousel-' + attachment_id ) 6432 ); 6433 } 6434 }, 6435 6436 clearCommentTextAreaValue: function() { 6437 var commentTextArea = $( '#jp-carousel-comment-form-comment-field' ); 6438 if ( commentTextArea ) { 6439 commentTextArea.val( '' ); 6440 } 6441 }, 6442 6443 nextSlide: function() { 6444 var slides = this.jp_carousel( 'slides' ); 6445 var selected = this.jp_carousel( 'selectedSlide' ); 6446 6447 if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.last() ) ) ) { 6448 return slides.first(); 6449 } 6450 6451 return selected.next(); 6452 }, 6453 6454 prevSlide: function() { 6455 var slides = this.jp_carousel( 'slides' ); 6456 var selected = this.jp_carousel( 'selectedSlide' ); 6457 6458 if ( selected.length === 0 || ( slides.length > 2 && selected.is( slides.first() ) ) ) { 6459 return slides.last(); 6460 } 6461 6462 return selected.prev(); 6463 }, 6464 6465 loadFullImage: function( slide ) {
6466 var image = slide.find( 'img:first' ); 6467 6468 if ( ! image.data( 'loaded' ) ) { 6469 // If the width of the slide is smaller than the width of the "thumbnail" we're already using, 6470 // don't load the full image. 6471 6472 image.on( 'load.jetpack', function() { 6473 image.off( 'load.jetpack' ); 6474 $( this ) 6475 .closest( '.jp-carousel-slide' ) 6476 .css( 'background-image', '' ); 6477 } ); 6478 6479 if ( 6480 ! slide.data( 'preview-image' ) || 6481 ( slide.data( 'thumb-size' ) && slide.width() > slide.data( 'thumb-size' ).width ) 6482 ) { 6483 image 6484 .attr( 'src', image.closest( '.jp-carousel-slide' ).data( 'src' ) ) 6485 .attr( 'itemprop', 'image' ); 6486 } else { 6487 image.attr( 'src', slide.data( 'preview-image' ) ).attr( 'itemprop', 'image' ); 6488 } 6489 6490 image.data( 'loaded', 1 ); 6491 } 6492 }, 6493 6494 hasMultipleImages: function() { 6495 return gallery.jp_carousel( 'slides' ).length > 1; 6496 }, 6497 }; 6498 6499 $.fn.jp_carousel = function( method ) { 6500 // ask for the HTML of the gallery 6501 // Method calling logic 6502 if ( methods[ method ] ) { 6503 return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); 6504 } else if ( typeof method === 'object' || ! method ) { 6505 return methods.open.apply( this, arguments ); 6506 } else { 6507 $.error( 'Method ' + method + ' does not exist on jQuery.jp_carousel' ); 6508 } 6509 }; 6510 6511 // register the event listener for starting the gallery 6512 $( document.body ).on( 6513 'click.jp-carousel', 6514 'div.gallery, div.tiled-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery', 6515 function( e ) { 6516 if ( ! $( this ).jp_carousel( 'testForData', e.currentTarget ) ) { 6517 return; 6518 } 6519 6520 // Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link. 6521 if ( 6522 $( e.target ) 6523 .parent() 6524 .hasClass( 'gallery-caption' ) 6525 ) { 6526 return; 6527 } 6528 6529 // Do not open the modal if we are looking at a caption of a gallery block, which may contain a link. 6530 if ( 6531 $( e.target ) 6532 .parent() 6533 .is( 'figcaption' ) 6534 ) { 6535 return; 6536 } 6537 6538 // Set height to auto 6539 // Fix some themes where closing carousel brings view back to top 6540 $( 'html' ).css( 'height', 'auto' ); 6541 6542 e.preventDefault(); 6543 6544 // Stopping propagation in case there are parent elements 6545 // with .gallery or .tiled-gallery class 6546 e.stopPropagation(); 6547 $( this ).jp_carousel( 'open', { 6548 start_index: $( this ) 6549 .find( '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item' ) 6550 .index( 6551 $( e.target ).parents( 6552 '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, .tiled-gallery__item' 6553 ) 6554 ), 6555 } ); 6556 } 6557 ); 6558 6559 // handle lightbox (single image gallery) for images linking to 'Attachment Page' 6560 if ( 1 === Number( jetpackCarouselStrings.single_image_gallery ) ) { 6561 processSingleImageGallery(); 6562 $( document.body ).on( 'post-load', function() { 6563 processSingleImageGallery(); 6564 } ); 6565 } 6566 6567 // Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger) 6568 $( window ).on( 'hashchange.jp-carousel', function() { 6569 var hashRegExp = /jp-carousel-(\d+)/, 6570 matches, 6571 attachmentId, 6572 galleries, 6573 selectedThumbnail; 6574 6575 if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) { 6576 if ( gallery && gallery.opened ) { 6577 container.jp_carousel( 'close' ); 6578 } 6579 6580 return; 6581 } 6582 6583 if ( window.location.hash === last_known_location_hash && gallery.opened ) { 6584 return; 6585 } 6586 6587 if ( window.location.hash && gallery && ! gallery.opened && history.back ) { 6588 history.back(); 6589 return; 6590 } 6591 6592 last_known_location_hash = window.location.hash; 6593 matches = window.location.hash.match( hashRegExp ); 6594 attachmentId = parseInt( matches[ 1 ], 10 ); 6595 galleries = $( 6596 'div.gallery, div.tiled-gallery, a.single-image-gallery, ul.wp-block-gallery, div.wp-block-jetpack-tiled-gallery' 6597 ); 6598 6599 // Find the first thumbnail that matches the attachment ID in the location 6600 // hash, then open the gallery that contains it. 6601 galleries.each( function( _, galleryEl ) { 6602 $( galleryEl )
6603 .find( 'img' ) 6604 .each( function( imageIndex, imageEl ) { 6605 if ( $( imageEl ).data( 'attachment-id' ) === parseInt( attachmentId, 10 ) ) { 6606 selectedThumbnail = { index: imageIndex, gallery: galleryEl }; 6607 return false; 6608 } 6609 } ); 6610 6611 if ( selectedThumbnail ) { 6612 $( selectedThumbnail.gallery ).jp_carousel( 'openOrSelectSlide', selectedThumbnail.index ); 6613 return false; 6614 } 6615 } ); 6616 } ); 6617 6618 if ( window.location.hash ) { 6619 $( window ).trigger( 'hashchange' ); 6620 } 6621} ); 6622 6623/** 6624 * jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!) 6625 * Common usage: wipe images (left and right to show the previous or next image) 6626 * 6627 * @author Andreas Waltl, netCU Internetagentur (https://www.netcu.de) 6628 * Version 1.1.1, modified to pass the touchmove event to the callbacks. 6629 */ 6630( function( $ ) { 6631 $.fn.touchwipe = function( settings ) { 6632 var config = { 6633 min_move_x: 20, 6634 min_move_y: 20, 6635 wipeLeft: function(/*e*/) {}, 6636 wipeRight: function(/*e*/) {}, 6637 wipeUp: function(/*e*/) {}, 6638 wipeDown: function(/*e*/) {}, 6639 preventDefaultEvents: true, 6640 }; 6641 6642 if ( settings ) { 6643 $.extend( config, settings ); 6644 } 6645 6646 this.each( function() { 6647 var startX; 6648 var startY; 6649 var isMoving = false; 6650 6651 function cancelTouch() { 6652 this.removeEventListener( 'touchmove', onTouchMove ); 6653 startX = null; 6654 isMoving = false; 6655 } 6656 6657 function onTouchMove( e ) { 6658 if ( config.preventDefaultEvents ) { 6659 e.preventDefault(); 6660 } 6661 if ( isMoving ) { 6662 var x = e.touches[ 0 ].pageX; 6663 var y = e.touches[ 0 ].pageY; 6664 var dx = startX - x; 6665 var dy = startY - y; 6666 if ( Math.abs( dx ) >= config.min_move_x ) { 6667 cancelTouch(); 6668 if ( dx > 0 ) { 6669 config.wipeLeft( e ); 6670 } else { 6671 config.wipeRight( e ); 6672 } 6673 } else if ( Math.abs( dy ) >= config.min_move_y ) { 6674 cancelTouch(); 6675 if ( dy > 0 ) { 6676 config.wipeDown( e ); 6677 } else { 6678 config.wipeUp( e ); 6679 } 6680 } 6681 } 6682 } 6683 6684 function onTouchStart( e ) { 6685 if ( e.touches.length === 1 ) { 6686 startX = e.touches[ 0 ].pageX; 6687 startY = e.touches[ 0 ].pageY; 6688 isMoving = true; 6689 this.addEventListener( 'touchmove', onTouchMove, false ); 6690 } 6691 } 6692 if ( 'ontouchstart' in document.documentElement ) { 6693 this.addEventListener( 'touchstart', onTouchStart, false ); 6694 } 6695 } ); 6696 6697 return this; 6698 }; 6699} )( jQuery ); 6700;
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.