1/* 2 * Copyright 2012-2015, Theia Post Slider, WeCodePixels, http://wecodepixels.com 3 */ 4var tps = tps || {}; 5tps.slideshowsOnPage = 0; 6tps.createSlideshowDefaults = { 7 slideContainer: '', 8 nav: [], 9 navText: '%{currentSlide} of %{totalSlides}', 10 helperText: '', 11 defaultSlide: 0, 12 transitionEffect: 'none', 13 transitionSpeed: 400, 14 keyboardShortcuts: false, 15 scrollAfterRefresh: true, 16 numberOfSlides: 0, // Total number of slides, including the ones not loaded. 17 slides: [], 18 useSlideSources: false, 19 themeType: 'classic', 20 prevPost: null, 21 nextPost: null, 22 prevText: null, 23 nextText: null, 24 buttonWidth: 0, 25 prevText_post: null, 26 nextText_post: null, 27 buttonWidth_post: 0, 28 onShowPost: null, 29 postUrl: null, 30 postId: null, 31 refreshAds: false, 32 refreshAdsEveryNSlides: 1, 33 adRefreshingMechanism: 'javascript', 34 siteUrl: '/', 35 loopSlides: false, 36 scrollTopOffset: 0 37}; 38tps.createSlideshow = function (options) { 39 var i, j; 40 var me = this; 41 var $ = jQuery; 42 43 // Merge options with defaults and sanitize values. 44 me.options = $.extend(tps.createSlideshowDefaults, options); 45 for (i in me.options) { 46 // If the default is a number and the option is a string, then convert it to a number if possible. 47 if (!isNaN(tps.createSlideshowDefaults[i]) && (typeof me.options[i] == 'string')) { 48 var value = parseFloat(me.options[i]); 49 if (!isNaN(value)) { 50 me.options[i] = value; 51 } 52 } 53 } 54 55 me.slides = []; 56 me.slideContainer = $(me.options.slideContainer); 57 if (me.slideContainer.length == 0) { 58 return null; 59 } 60 61 tps.slideshowsOnPage++; 62 me.navEl = []; 63 for (i = 0; i < me.options.nav.length; i++) { 64 var e = $(me.options.nav[i]).get(); 65 for (j = 0; j < e.length; j++) { 66 me.navEl.push({ 67 container: $(e[j]) 68 }); 69 } 70 } 71 72 // Initialize variables. 73 me.currentPostId = me.options.postId; 74 me.isLoading = false; 75 me.slidesSinceLastAdRefresh = 0; 76 77 // Remote loading queue. 78 me.remoteLoadingQueue = async.queue(function (task, callback) { 79 callback(); 80 }, 1); 81 82 // The current slide. 83 me.currentSlide = null; 84 85 // The slide that is currently displayed. This may lag behind "me.currentSlide" because of the animations. 86 me.currentlyDisplayedSlide = null; 87 88 // The number of animations that are currently running. 89 me.animations = 0; 90 91 // A queue that is executed when no animation is running. 92 me.semaphore = 0; 93 me.asyncQueue = async.queue(function (task, callback) { 94 callback(); 95 }, 1); 96 97 me.incrementSemaphore = function () { 98 me.asyncQueue.pause(); 99 me.semaphore++; 100 }; 101 102 me.decrementSemaphore = function () { 103 me.semaphore--; 104 if (me.semaphore == 0) { 105 me.asyncQueue.resume(); 106 } 107 }; 108 109 // Initialization function 110 me.init = function () { 111 var i; 112 113 // Load scroll position, if available. 114 me.loadScrollTop(); 115 116 // Get slides from me.options.slides 117 for (i in me.options.slides) { 118 if ('content' in me.options.slides[i]) { 119 if (me.options.useSlideSources) { 120 me.options.slides[i].source = me.options.slides[i].content; 121 } 122 123 me.options.slides[i].content = $('<div>').html(me.options.slides[i].content); 124 } 125 126 me.slides[i] = me.options.slides[i]; 127 } 128 me.options.slides = null; 129 130 // Get slides from HTML. The first slide is considered the default one, while the rest are detached. 131 me.slideContainer.children().each(function (index, slide) { 132 index = me.options.defaultSlide + index; 133 slide = $(slide); 134 me.slides[index] = me.slides[index] || {}; 135 me.slides[index].title = document.title; 136 me.slides[index].permalink = document.location.href; 137 me.slides[index].content = slide; 138 139 if (index != me.options.defaultSlide) { 140 slide.detach(); 141 } 142 }); 143 144 // Count the slides. 145 me.numberOfSlides = me.options.numberOfSlides; 146 147 // Setup the navigation bars. 148 for (i = 0; i < me.navEl.length; i++) { 149 var navEl = me.navEl[i]; 150 navEl.text = navEl.container.find('._text'); 151 navEl.prev = navEl.container.find('._prev') 152 .click(function (me) { 153 return function (event) { 154 me.onNavClick(this, 'prev', event); 155 } 156 }(me)); 157 navEl.next = navEl.container.find('._next') 158 .click(function (me) { 159 return function (event) { 160 me.onNavClick(this, 'next', event); 161 } 162 }(me)); 163 164 navEl.title = navEl.container.find('._title'); 165 166 // Get the default slide's title. The title will be the same for all navigation bars, so get it only from the first. 167 if (i === 0) { 168 me.slides[me.options.defaultSlide].shortCodeTitle = navEl.title.html(); 169 } 170 171 /* 172 Add _active class on mousedown. This is a fix for IE and Opera which don't match :active on container elements. 173 Also, return false to prevent double click context menu in Opera. 174 */ 175 navEl.container.find('._prev, ._next') 176 .mousedown(function () { 177 $(this).addClass('_active'); 178 }) 179 .mouseup(function () { 180 $(this).removeClass('_active'); 181 }); 182 } 183 184 // Skip loading the slide if we're not going to display another one anyway. 185 if (me.numberOfSlides == 1 || (me.options.refreshAds && me.options.adRefreshingMechanism == 'page' && me.options.refreshAdsEveryNSlides <= 1)
185) { 186 me.currentSlide = 0; 187 } 188 else { 189 // Show the first slide 190 me.setSlide(me.options.defaultSlide); 191 192 // Add history handler. 193 var history = window.History; 194 if (history.enabled) { 195 history.Adapter.bind(window, 'statechange', function (me) { 196 return function () { 197 var state = History.getState(); 198 if (state.data.currentPostId != undefined) { 199 if (state.data.currentSlide != undefined) { 200 me.setSlide(state.data.currentSlide); 201 } 202 else { 203 me.setSlide(me.options.defaultSlide); 204 } 205 } 206 }; 207 }(me)); 208 } 209 } 210 211 // Enable keyboard shortcuts 212 if (me.options.keyboardShortcuts) { 213 $(document).keydown(function (me) { 214 return function (e) { 215 // Disable shortcut if there is more than one slideshow on the page. 216 if (tps.slideshowsOnPage > 1) { 217 return true; 218 } 219 220 // Disable shortcut if the target element is editable (input boxes, textareas, etc.). 221 if ( 222 this !== e.target && 223 ( 224 /textarea|select/i.test(e.target.nodeName) || 225 e.target.type === "text" || 226 ( 227 $(e.target).prop && 228 $(e.target).prop('contenteditable') == 'true' 229 ) 230 ) 231 ) { 232 return true; 233 } 234 235 switch (e.which) { 236 case 37: 237 me.navEl[0].prev[0].click(); 238 return false; 239 240 case 39: 241 me.navEl[0].next[0].click(); 242 return false; 243 } 244 245 return true; 246 }; 247 }(me)); 248 } 249 }; 250 251 // Load slides remotely 252 me.loadSlides = function (slides, callback) { 253 me.remoteLoadingQueue.push({name: ''}, function (me, slides, callback) { 254 return function (err) { 255 // Check if the slides are already loaded. 256 var allSlidesAreLoaded = true; 257 for (var i in slides) { 258 if (!(slides[i] in me.slides) || !('content' in me.slides[slides[i]])) { 259 allSlidesAreLoaded = false; 260 break; 261 } 262 } 263 if (allSlidesAreLoaded) { 264 if (callback) { 265 callback(); 266 } 267 return; 268 } 269 270 // Load the slides and don't load anything else in the meantime. 271 me.remoteLoadingQueue.concurrency = 0; 272 $.ajax({ 273 dataType: 'json', 274 data: { 275 theiaPostSlider: 'get-slides', 276 postId: me.currentPostId, 277 slides: slides 278 }, 279 url: me.options.siteUrl, 280 success: function (me) { 281 return function (data) { 282 if (!data) { 283 return; 284 } 285 if (data.postId == me.currentPostId) { 286 // Add each slide. 287 for (var i in data.slides) { 288 if (!(i in me.slides)) { 289 me.slides[i] = {}; 290 } 291 292 // Add content.
293 if ('content' in data.slides[i]) { 294 if (me.options.useSlideSources) { 295 data.slides[i].source = data.slides[i].content; 296 } 297 298 data.slides[i].content = $('<div>').html(data.slides[i].content); 299 } 300 301 // Overwrite old data with new data. 302 for (var j in data.slides[i]) { 303 me.slides[i][j] = data.slides[i][j]; 304 } 305 } 306 } 307 else { 308 console.error('Received AJAX results for a different post ID.'); 309 } 310 }; 311 }(me), 312 complete: function (me, callback) { 313 return function () { 314 if (callback) { 315 callback(); 316 } 317 me.remoteLoadingQueue.concurrency = 1; 318 me.remoteLoadingQueue.push({}); 319 }; 320 }(me, callback) 321 }); 322 }; 323 }(me, slides, callback)); 324 }; 325 326 // Set the current slide. 327 me.setSlide = function (index, isCallback) { 328 if (me.isLoading == true && isCallback != true) { 329 return; 330 } 331 332 // Is the slide already displayed? 333 if (me.currentSlide == index) { 334 return; 335 } 336 337 // Do we need to refresh the ads by refreshing the entire page? 338 if (me.needToRefreshPage() && index in me.slides) { 339 me.saveScrollTop(); 340 window.location = me.slides[index].permalink; 341 342 return; 343 } 344 345 // Fire events. 346 $(document).trigger('theiaPostSlider.willChangeSlide', [index]); 347 348 // Is the slide not yet loaded? 349 if (!me.isSlideLoaded(index)) { 350 if (!isCallback) { 351 me.showLoading(); 352 me.loadSlides([index], function (me, index) { 353 return function () { 354 me.hideLoading(); 355 me.setSlide(index, true); 356 } 357 }(me, index)); 358 359 return; 360 } 361 else { 362 // The slide could not be loaded via AJAX. Abort. 363 console.error("The slide could not be loaded via AJAX."); 364 return; 365 } 366 } 367 368 var previousSlide = me.currentSlide; 369 me.currentSlide = index; 370 371 if (previousSlide != null) { 372 // Scroll the window up, if the beginning of the slide is out-of-view. 373 // Get the lowest offset.top 374 var scrollTop = me.slideContainer.offset().top; 375 for (var i = 0; i < me.navEl.length; i++) { 376 scrollTop = Math.min(scrollTop, me.navEl[i].container.offset().top); 377 } 378 scrollTop += me.options.scrollTopOffset; 379 if ($(window).scrollTop() > scrollTop) { 380 $('body,html').animate({scrollTop: scrollTop}); 381 } 382 } 383 384 // Set the title text. 385 me.updateTitleText(); 386 387 // Change URL, but only if this isn't the first slide set (i.e. the default slide). 388 var history = window.History; 389 if (history.enabled) { 390 var url = me.slides[me.currentSlide].permalink; 391 if (url && previousSlide !== null) { 392 var f; 393 if (url == window.location) { 394 f = history.replaceState; 395 } 396 else { 397 f = history.pushState; 398 } 399 f({ 400 currentSlide: index, 401 currentPostId: me.currentPostId 402 }, me.slides[me.currentSlide].title, url); 403 } 404 } 405 406 // Show the slide. 407 me.asyncQueue.push({name: ''}, me.showSlide); 408 409 // Refresh ads using JavaScript. 410 if (previousSlide != null) { 411 if (me.options.refreshAds) { 412 me.slidesSinceLastAdRefresh++; 413 414 if (me.slidesSinceLastAdRefresh >= me.options.refreshAdsEveryNSlides) { 415 me.slidesSinceLastAdRefresh = 0; 416 417 if (me.options.adRefreshingMechanism == 'javascript') { 418 var p = null; 419 420 if (typeof pubService === 'undefined') { 421 if (typeof googletag !== 'undefined') { 422 p = googletag.pubads(); 423 } 424 } 425 else { 426 p = pubService; 427 } 428 429 if (p) { 430 p.refresh(); 431 } 432 433 $(document).trigger('theiaPostSlider.refreshAds'); 434 } 435 } 436 } 437 } 438 439 // Set the navigation bars. 440 me.updateNavigationBars(); 441 442 // Preload, but only if we don't have to refresh the page immediately after. 443 if ( 444 !( 445 me.options.refreshAds && 446 me.slidesSinceLastAdRefresh + 1 >= me.options.refreshAdsEveryNSlides && 447 me.options.adRefreshingMechanism == 'page' 448 ) 449 ) { 450 // Direction is either +1 if the user is browsing forward (i.e. using the "next" button), or -1 otherwise. 451 var direction; 452 if (previousSlide == null) { 453 direction = 1; 454 } 455 else if (me.currentSlide == 0 && previousSlide == me.numberOfSlides - 1) { 456 direction = 1; 457 } 458 else if (me.currentSlide == me.numberOfSlides - 1 && previousSlide == 0) { 459 direction = -1; 460 } 461 else { 462 direction = me.currentSlide - previousSlide; 463 direction = Math.max(Math.min(direction, 1), -1); 464 } 465 466 var slideToPreload = me.currentSlide + direction; 467 468 // If the loop-slides settings is activated, also preload the first/last slide when applicable. 469 if ( slideToPreload == -1 && me.options.loopSlides ) { 470 slideToPreload = me.numberOfSlides - 1; 471 } 472 else if ( slideToPreload == me.numberOfSlides && me.options.loopSlides ) { 473 slideToPreload = 0; 474 } 475 476 if ( 477 slideToPreload >= 0 && 478 slideToPreload < me.numberOfSlides && !me.isSlideLoaded((slideToPreload)) 479 ) { 480 me.loadSlides([slideToPreload]); 481 } 482 483 } 484 485 // Set up touch events. 486 if (typeof Hammer !== 'undefined') { 487 me.previousTouch = 0; 488 me.minimumTimeBetweenGestures = 500; 489 var hammertime = new Hammer(me.slideContainer[0]); 490 hammertime 491 .on('swipeleft', function () { 492 var t = (new Date).getTime(); 493 if (t - me.minimumTimeBetweenGestures >= me.previousTouch) { 494 me.setNext(); 495 me.previousTouch = t; 496 } 497 }) 498 .on('swiperight', function () { 499 var t = (new Date).getTime(); 500 if (t - me.minimumTimeBetweenGestures >= me.previousTouch) {
501 me.setPrev(); 502 me.previousTouch = t; 503 } 504 }); 505 } 506 }; 507 508 // Show (display) the current slide using the chosen animation. 509 me.showSlide = function () { 510 // Don't do anything if the current slide is already shown. 511 if (me.currentlyDisplayedSlide == me.currentSlide) { 512 return; 513 } 514 515 // Track the pageview if this isn't the first slide displayed. 516 if (me.currentlyDisplayedSlide != null && me.slides[me.currentSlide]['permalink']) { 517 // URL Path 518 var path = me.slides[me.currentSlide]['permalink'].split('/'); 519 if (path.length >= 4) { 520 path = '/' + path.slice(3).join('/'); 521 522 // Google Analytics by Yoast, which renames the "ga" variable. 523 if (typeof __gaTracker !== 'undefined') { 524 __gaTracker('send', 'pageview', path); 525 } 526 // Google Analytics (Analytics.js). 527 else if (typeof ga !== 'undefined') { 528 ga('send', 'pageview', path); 529 } 530 // Google Analytics (ga.js, deprecated). 531 else if (typeof _gaq !== 'undefined' && typeof _gaq.push !== 'undefined') { 532 _gaq.push(['_trackPageview', path]); 533 } 534 // Google Analytics Traditional. 535 else if (typeof pageTracker !== 'undefined' && typeof pageTracker._trackPageview !== 'undefined') { 536 pageTracker._trackPageview(path); 537 } 538 539 // Piwik 540 if (typeof piwikTracker !== 'undefined' && typeof piwikTracker.trackPageView !== 'undefined') { 541 piwikTracker.trackPageView(path); 542 } 543 544 // StatCounter 545 if (typeof sc_project !== 'undefined' && typeof sc_security !== 'undefined') { 546 var img = new Image(); 547 img.src = '//c.statcounter.com/' + sc_project + '/0/' + sc_security + '/1/'; 548 } 549 550 // Quantcast 551 if (typeof _qacct !== 'undefined') { 552 var img = new Image(); 553 img.src = '//pixel.quantserve.com/pixel/' + _qacct + '.gif'; 554 } 555 } 556 } 557 558 var previousIndex = me.currentlyDisplayedSlide; 559 me.currentlyDisplayedSlide = me.currentSlide; 560 561 me.createSlideContentFromSource(me.slides[me.currentSlide]); 562 563 // Change the slide while applying a certain effect/animation. 564 var animationsQueue = tps.transitions[me.options.transitionEffect](me, previousIndex, me.currentlyDisplayedSlide); 565 566 // Execute all "attachAnimation" methods before starting any animation. 567 // Otherwise, we might get a race condition when one animation finishes before others are started, 568 // thus triggering unwanted events before all animations have ended. 569 for (var i = 0; i < animationsQueue.length; i++) { 570 me.incrementSemaphore(); 571 } 572 573 // This will be called after all animations finish, before any other items in the queue. 574 if (previousIndex !== null) { 575 me.asyncQueue.unshift({name: ''}, function (me, previousIndex) { 576 return function () { 577 me.onRemovedSlide(previousIndex); 578 } 579 }(me, previousIndex)); 580 } 581 582 // Start all animations. 583 for (var i = 0; i < animationsQueue.length; i++) { 584 animationsQueue[i](); 585 } 586 }; 587 588 me.createSlideContentFromSource = function (slide) { 589 if (!('content' in slide) && ('source' in slide)) { 590 slide.content = $('<div>').html(slide.source); 591 592 if (false == me.options.useSlideSources) { 593 delete slide.source; 594 } 595 } 596 }; 597 598 me.isSlideLoaded = function (index) { 599 if (!(index in me.slides)) { 600 return false; 601 } 602 603 // Only if this isn't the first slide. 604 if (me.currentlyDisplayedSlide !== null && me.options.useSlideSources && !('source' in me.slides[index])) { 605 return false;
606 } 607 608 if (!(('content' in me.slides[index]) || ('source' in me.slides[index]))) { 609 return false; 610 } 611 612 return true; 613 }; 614 615 // Function that is called right after a new slide has been added to the DOM, and right before the animation (if available) has started. 616 me.onNewSlide = function () { 617 // "BJ Lazy Load" plugin. 618 $(".lazy-hidden").not(".data-lazy-ready").one("scrollin.bj_lazy_load", { 619 distance: 200 620 }, function () { 621 var b = $(this), 622 d = b.attr("data-lazy-type"); 623 if (d == "image") { 624 b.hide().attr("src", b.attr("data-lazy-src")).removeClass("lazy-hidden").fadeIn() 625 } else if (d == "iframe") { 626 b.replaceWith(c(b.attr("data-lazy-src"))) 627 } 628 }).addClass("data-lazy-ready"); 629 630 // "Lazy Load" plugin. 631 var events = $('body').data('events'); 632 if (events && events['post-load']) { 633 for (var i = 0; i < events['post-load'].length; i++) { 634 if (events['post-load'][i].handler.name == 'lazy_load_init') { 635 events['post-load'][i].handler(); 636 } 637 } 638 } 639 640 // WordPress [audio] and [video] shortcodes. 641 if (typeof _wpmejsSettings !== 'undefined') { 642 $('.wp-audio-shortcode, .wp-video-shortcode').mediaelementplayer(_wpmejsSettings); 643 } 644 645 // Instagram embeds. 646 if (typeof instgrm !== 'undefined' && typeof instgrm.Embeds !== 'undefined' && typeof instgrm.Embeds.process !== 'undefined') { 647 instgrm.Embeds.process(); 648 } 649 650 // Fire events. 651 $(document).trigger('theiaPostSlider.changeSlide', [me.currentSlide]); 652 }; 653 654 // Function that is called right after a slide has been removed from the DOM. 655 me.onRemovedSlide = function (slideId) { 656 if (me.options.useSlideSources) { 657 me.slides[slideId].content.remove(); 658 delete me.slides[slideId].content; 659 } 660 }; 661 662 // Update the title text. 663 me.updateTitleText = function () { 664 var shortCodeTitle = me.slides[me.currentSlide].shortCodeTitle; 665 if (!shortCodeTitle) { 666 shortCodeTitle = '<span class="_helper">' + me.options['helperText'] + '</span>'; 667 } 668 for (i = 0; i < me.navEl.length; i++) { 669 me.navEl[i].title.html(shortCodeTitle); 670 } 671 }; 672 673 // Update the navigation bar's text and buttons. 674 me.updateNavigationBars = function () { 675 for (var i = 0; i < me.navEl.length; i++) { 676 var navEl = me.navEl[i]; 677 var navText; 678 if (me.numberOfSlides == 1) { 679 navText = ''; 680 } 681 else { 682 navText = me.options.navText; 683 navText = navText.replace('%{currentSlide}', me.currentSlide + 1); 684 navText = navText.replace('%{totalSlides}', me.numberOfSlides); 685 } 686 navEl.text.html(navText); 687 688 // Update buttons. 689 me.updateNavigationBarButton(navEl, false); 690 me.updateNavigationBarButton(navEl, true); 691 } 692 }; 693 694 // Update a button from a navigation bar. 695 me.updateNavigationBarButton = function (navEl, direction) { 696 var width, 697 html1 = '', 698 html2 = '', 699 html3 = '', 700 href, 701 directionName = direction ? 'next' : 'prev', 702 buttonEl = navEl[directionName]; 703 704 if (me.options.themeType == 'font') { 705 width = 0; 706 html2 = me.options[directionName + 'FontIcon']; 707 708 if (direction == false) { 709 if (me.options.prevText_post) { 710 if (me.currentSlide == 0) { 711 html3 = me.options.prevText_post; 712 } else { 713 html3 = me.options.prevText; 714 } 715 } else { 716 html3 = me.options.prevText; 717 } 718 } else { 719 if (me.options.nextText_post) { 720 if (me.currentSlide == me.numberOfSlides - 1) { 721 html1 = me.options.nextText_post; 722 } else { 723 html1 = me.options.nextText; 724 } 725 } else { 726 html1 = me.options.nextText; 727 } 728 } 729 } else { 730 width = me.options.buttonWidth; 731 732 if ( 733 (direction == false && me.options.prevPost && me.currentSlide == 0) || 734 (direction == true && me.options.nextPost && me.currentSlide == me.numberOfSlides - 1) 735 ) { 736 html2 = me.options[directionName + 'Text_post']; 737 href = direction ? me.options.nextPost : me.options.prevPost; 738 } 739 else { 740 html2 = me.options[directionName + 'Text']; 741 } 742 } 743 744 // Set link. 745 { 746 var jumpToSlide = null; 747 if (direction == false && me.options.loopSlides && me.currentSlide == 0) { 748 jumpToSlide = me.numberOfSlides - 1; 749 } 750 else if (direction == true && me.options.loopSlides && me.currentSlide == me.numberOfSlides - 1) { 751 jumpToSlide = 0; 752 } 753 else if ( 754 (direction == false && me.options.prevPost && me.currentSlide == 0) || 755 (direction == true && me.options.nextPost && me.currentSlide == me.numberOfSlides - 1) 756 ) { 757 buttonEl.addClass('_another_post'); 758 href = direction ? me.options.nextPost : me.options.prevPost; 759 } else { 760 buttonEl.removeClass('_another_post'); 761 jumpToSlide = me.currentSlide + (direction ? 1 : -1); 762 } 763 764 if (jumpToSlide !== null) { 765 if (me.needToRefreshPage()) { 766 var slide = me.slides[jumpToSlide]; 767 if (slide) { 768 href = slide.permalink; 769 } 770 } 771 else { 772 href = '#'; 773 } 774 } 775 } 776 777 buttonEl.find('._1').html(html1); 778 buttonEl.find('._2') 779 .css('width', width > 0 ? width : '') 780 .html(html2); 781 buttonEl.find('._3').html(html3); 782 783 // Disable or enable 784 if ( 785 (direction == false && me.options.prevPost == null && !me.options.loopSlides && me.currentSlide == 0) || 786 (direction == true && me.options.nextPost == null && !me.options.loopSlides && me.currentSlide == me.numberOfSlides - 1) 787 ) { 788 buttonEl.addClass('_disabled'); 789 href = '#'; 790 } 791 else { 792 buttonEl.removeClass('_disabled'); 793 } 794 795 buttonEl.attr('href', href); 796 }; 797 798 // Handler for previous and next buttons. 799 me.onNavClick = function (that, direction, event) { 800 var $that = $(that); 801 802 if (typeof $that.attr('href') === 'undefined') { 803 return; 804 } 805 806 // Jump to another page. 807 // Some 3rd party plugins misbehave and turn the '#' into '', namely https://github.com/medialize/URI.js with "Blackout Image Gallery". 808 if ($that.attr('href') != '#' && $that.attr('href') != '') { 809 me.showLoading(); 810 me.saveScrollTop(); 811 return; 812 } 813 814 if (direction == 'prev') {
815 me.setPrev(); 816 } else { 817 me.setNext(); 818 } 819 820 $that.focus(); 821 if (event) { 822 event.preventDefault(); 823 } 824 }; 825 826 me.isHtml5StorageAvailable = function () { 827 try { 828 return 'localStorage' in window && window['localStorage'] !== null; 829 } catch (e) { 830 return false; 831 } 832 }; 833 834 me.saveScrollTop = function () { 835 if (!me.options.scrollAfterRefresh) { 836 return; 837 } 838 839 if (!me.isHtml5StorageAvailable) { 840 return; 841 } 842 843 localStorage.setItem('scrollTop', JSON.stringify({ 844 postId: me.postId, 845 scrollTop: $(window).scrollTop() 846 })); 847 }; 848 849 me.loadScrollTop = function () { 850 if (!me.options.scrollAfterRefresh) { 851 return; 852 } 853 854 if (!me.isHtml5StorageAvailable) { 855 return; 856 } 857 858 var data = JSON.parse(localStorage.getItem('scrollTop')); 859 if (data && data.postId == me.postId) { 860 var scrollTop = Math.min(data.scrollTop, me.slideContainer.offset().top); 861 $(window).scrollTop(scrollTop); 862 } 863 864 localStorage.removeItem('scrollTop'); 865 }; 866 867 // Go to the previous slide. 868 me.setPrev = function () { 869 if (me.currentSlide == 0) { 870 if (me.options.loopSlides) { 871 me.setSlide(me.numberOfSlides - 1); 872 } 873 else if (me.options.prevPost) { 874 me.showPost(me.options.prevPost); 875 } 876 } 877 else { 878 me.setSlide(me.currentSlide - 1); 879 } 880 }; 881 882 // Go to the next slide. 883 me.setNext = function () { 884 if (me.currentSlide == me.numberOfSlides - 1) { 885 if (me.options.loopSlides) { 886 me.setSlide(0); 887 } 888 else if (me.options.nextPost) { 889 me.showPost(me.options.nextPost); 890 } 891 } 892 else { 893 me.setSlide(me.currentSlide + 1); 894 } 895 }; 896 897 // Go to another post. This method will be called when navigating using the keyboard shortcuts. 898 me.showPost = function (postUrl) { 899 document.location = postUrl; 900 }; 901 902 // Set the transition properties (used in Live Preview). 903 me.setTransition = function (options) { 904 var defaults = { 905 'effect': me.options.transitionEffect, 906 'speed': me.options.transitionSpeed 907 }; 908 options = $.extend(defaults, options); 909 me.options.transitionEffect = options.effect; 910 me.options.transitionSpeed = options.speed; 911 }; 912 913 // Set the navigation bar's text template (used in Live Preview). 914 me.setNavText = function (text) { 915 me.options.navText = text; 916 me.updateNavigationBars(); 917 }; 918 919 // Set the title for all slides (used in Live Preview). 920 me.setTitleText = function (text) { 921 for (i = 0; i < me.slides.length; i++) { 922 me.slides[i].shortCodeTitle = ''; 923 } 924 me.options.helperText = text; 925 me.updateTitleText(); 926 }; 927 928 me.showLoading = function () { 929 me.isLoading = true; 930 for (i = 0; i < me.navEl.length; i++) { 931 me.navEl[i].container 932 .append($('<div class="_loading"></div>')) 933 .find('._buttons > a').addClass('_disabled'); 934 } 935 }; 936 937 me.hideLoading = function () { 938 me.isLoading = false; 939 for (i = 0; i < me.navEl.length; i++) { 940 me.navEl[i].container.find('._loading').remove(); 941 } 942 me.updateNavigationBars(); 943 }; 944 945 me.needToRefreshPage = function () { 946 return ( 947 me.currentSlide !== null && 948 me.options.refreshAds && 949 me.slidesSinceLastAdRefresh + 1 >= me.options.refreshAdsEveryNSlides && 950 me.options.adRefreshingMechanism == 'page' 951 ); 952 }; 953 954 // Initialize the slider.
955 me.init(); 956 957 return me; 958};
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.