1/** 2 * Copyright (c) 2012 Anders Ekdahl (http://coffeescripter.com/) 3 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 4 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 5 * 6 * Version: 1.2.7 7 * 8 * Demo and documentation: http://coffeescripter.com/code/ad-gallery/ 9 */ 10(function($) { 11 $.fn.adGallery = function(options) { 12 var defaults = { loader_image: 'loader.gif', 13 start_at_index: 0, 14 update_window_hash: true, 15 description_wrapper: false, 16 thumb_opacity: 0.8, 17 animate_first_image: false, 18 animation_speed: 400, 19 width: false, 20 height: false, 21 display_next_and_prev: true, 22 display_back_and_forward: true, 23 scroll_jump: 0, // If 0, it jumps the width of the container 24 slideshow: { 25 enable: true, 26 autostart: false, 27 speed: 5000, 28 start_label: 'Start', 29 stop_label: 'Stop', 30 stop_on_scroll: true, 31 countdown_prefix: '(', 32 countdown_sufix: ')', 33 onStart: false, 34 onStop: false 35 }, 36 effect: 'slide-hori', // or 'slide-vert', 'fade', or 'resize', 'none' 37 enable_keyboard_move: true, 38 cycle: true, 39 hooks: { 40 displayDescription: false 41 }, 42 callbacks: { 43 init: false, 44 afterImageVisible: false, 45 beforeImageVisible: false 46 } 47 }; 48 var settings = $.extend(false, defaults, options); 49 if(options && options.slideshow) { 50 settings.slideshow = $.extend(false, defaults.slideshow, options.slideshow); 51 }; 52 if(!settings.slideshow.enable) { 53 settings.slideshow.autostart = false;
54 }; 55 var galleries = []; 56 $(this).each(function() { 57 var gallery = new AdGallery(this, settings); 58 galleries[galleries.length] = gallery; 59 }); 60 // Sorry, breaking the jQuery chain because the gallery instances 61 // are returned so you can fiddle with them 62 return galleries; 63 }; 64 65 function VerticalSlideAnimation(img_container, direction, desc) { 66 var current_top = parseInt(img_container.css('top'), 10); 67 if(direction == 'left') { 68 var old_image_top = '-'+ this.image_wrapper_height +'px'; 69 img_container.css('top', this.image_wrapper_height +'px'); 70 } else { 71 var old_image_top = this.image_wrapper_height +'px'; 72 img_container.css('top', '-'+ this.image_wrapper_height +'px'); 73 }; 74 if(desc) { 75 desc.css('bottom', '-'+ desc[0].offsetHeight +'px'); 76 desc.animate({bottom: 0}, this.settings.animation_speed * 2); 77 }; 78 if(this.current_description) { 79 this.current_description.animate({bottom: '-'+ this.current_description[0].offsetHeight +'px'}, this.settings.animation_speed * 2); 80 }; 81 return {old_image: {top: old_image_top}, 82 new_image: {top: current_top}}; 83 }; 84 85 function HorizontalSlideAnimation(img_container, direction, desc) { 86 var current_left = parseInt(img_container.css('left'), 10); 87 if(direction == 'left') { 88 var old_image_left = '-'+ this.image_wrapper_width +'px'; 89 img_container.css('left',this.image_wrapper_width +'px'); 90 } else { 91 var old_image_left = this.image_wrapper_width +'px'; 92 img_container.css('left','-'+ this.image_wrapper_width +'px'); 93 }; 94 if(desc) { 95 desc.css('bottom', '-'+ desc[0].offsetHeight +'px'); 96 desc.animate({bottom: 0}, this.settings.animation_speed * 2); 97 }; 98 if(this.current_description) { 99 this.current_description.animate({bottom: '-'+ this.current_description[0].offsetHeight +'px'}, this.settings.animation_speed * 2); 100 }; 101 return {old_image: {left: old_image_left}, 102 new_image: {left: current_left}}; 103 }; 104 105 function ResizeAnimation(img_container, direction, desc) { 106 var image_width = img_container.width(); 107 var image_height = img_container.height(); 108 var current_left = parseInt(img_container.css('left'), 10); 109 var current_top = parseInt(img_container.css('top'), 10); 110 img_container.css({width: 0, height: 0, top: this.image_wrapper_height / 2, left: this.image_wrapper_width / 2}); 111 return {old_image: {width: 0, 112 height: 0, 113 top: this.image_wrapper_height / 2, 114 left: this.image_wrapper_width / 2}, 115 new_image: {width: image_width, 116 height: image_height, 117 top: current_top, 118 left: current_left}}; 119 }; 120 121 function FadeAnimation(img_container, direction, desc) { 122 img_container.css('opacity', 0); 123 return {old_image: {opacity: 0}, 124 new_image: {opacity: 1}}; 125 }; 126 127 // Sort of a hack, will clean this up... eventually 128 function NoneAnimation(img_container, direction, desc) { 129 img_container.css('opacity', 0); 130 return {old_image: {opacity: 0}, 131 new_image: {opacity: 1}, 132 speed: 0}; 133 }; 134 135 function AdGallery(wrapper, settings) { 136 this.init(wrapper, settings); 137 }; 138 AdGallery.prototype = { 139 // Elements 140 wrapper: false, 141 image_wrapper: false, 142 gallery_info: false, 143 nav: false, 144 loader: false, 145 preloads: false, 146 thumbs_wrapper: false, 147 thumbs_wrapper_width: 0, 148 scroll_back: false, 149 scroll_forward: false, 150 next_link: false, 151 prev_link: false, 152 153 slideshow: false, 154 image_wrapper_width: 0, 155 image_wrapper_height: 0, 156 current_index: -1, 157 current_image: false, 158 current_description: false, 159 nav_display_width: 0, 160 settings: false, 161 images: false, 162 in_transition: false, 163 animations: false, 164 init: function(wrapper, settings) { 165 var context = this; 166 this.wrapper = $(wrapper); 167 this.settings = settings; 168 this.setupElements(); 169 this.setupAnimations(); 170 if(this.settings.width) { 171 this.image_wrapper_width = this.settings.width; 172 this.image_wrapper.width(this.settings.width); 173 this.wrapper.width(this.settings.width); 174 } else { 175 this.image_wrapper_width = this.image_wrapper.width(); 176 }; 177 if(this.settings.height) { 178 this.image_wrapper_height = this.settings.height; 179 this.image_wrapper.height(this.settings.height); 180 } else { 181 this.image_wrapper_height = this.image_wrapper.height(); 182 }; 183 this.nav_display_width = this.nav.width(); 184 this.current_index = -1; 185 this.current_image = false;
186 this.current_description = false; 187 this.in_transition = false; 188 this.findImages(); 189 if(this.settings.display_next_and_prev) { 190 this.initNextAndPrev(); 191 }; 192 // The slideshow needs a callback to trigger the next image to be shown 193 // but we don't want to give it access to the whole gallery instance 194 var nextimage_callback = function(callback) { 195 return context.nextImage(callback); 196 }; 197 this.slideshow = new AdGallerySlideshow(nextimage_callback, this.settings.slideshow); 198 this.controls.append(this.slideshow.create()); 199 if(this.settings.slideshow.enable) { 200 this.slideshow.enable(); 201 } else { 202 this.slideshow.disable(); 203 }; 204 if(this.settings.display_back_and_forward) { 205 this.initBackAndForward(); 206 }; 207 if(this.settings.enable_keyboard_move) { 208 this.initKeyEvents(); 209 }; 210 this.initHashChange(); 211 var start_at = parseInt(this.settings.start_at_index, 10); 212 if(typeof this.getIndexFromHash() != "undefined") { 213 start_at = this.getIndexFromHash(); 214 }; 215 this.loading(true); 216 this.showImage(start_at, 217 function() { 218 // We don't want to start the slideshow before the image has been 219 // displayed 220 if(context.settings.slideshow.autostart) { 221 context.preloadImage(start_at + 1); 222 context.slideshow.start(); 223 }; 224 } 225 ); 226 this.fireCallback(this.settings.callbacks.init); 227 }, 228 setupAnimations: function() { 229 this.animations = { 230 'slide-vert': VerticalSlideAnimation, 231 'slide-hori': HorizontalSlideAnimation, 232 'resize': ResizeAnimation, 233 'fade': FadeAnimation, 234 'none': NoneAnimation 235 }; 236 }, 237 setupElements: function() { 238 this.controls = this.wrapper.find('.ad-controls'); 239 this.gallery_info = $('<p class="ad-info"></p>'); 240 this.controls.append(this.gallery_info); 241 this.image_wrapper = this.wrapper.find('.ad-image-wrapper'); 242 this.image_wrapper.empty(); 243 this.nav = this.wrapper.find('.ad-nav'); 244 this.thumbs_wrapper = this.nav.find('.ad-thumbs'); 245 this.preloads = $('<div class="ad-preloads"></div>'); 246 this.loader = $('<img class="ad-loader" src="'+ this.settings.loader_image +'">'); 247 this.image_wrapper.append(this.loader); 248 this.loader.hide(); 249 $(document.body).append(this.preloads); 250 }, 251 loading: function(bool) { 252 if(bool) { 253 this.loader.show(); 254 } else { 255 this.loader.hide(); 256 }; 257 }, 258 addAnimation: function(name, fn) { 259 if($.isFunction(fn)) { 260 this.animations[name] = fn; 261 }; 262 }, 263 findImages: function() { 264 var context = this; 265 this.images = []; 266 var thumbs_loaded = 0; 267 var thumbs = this.thumbs_wrapper.find('a'); 268 var thumb_count = thumbs.length; 269 if(this.settings.thumb_opacity < 1) { 270 thumbs.find('img').css('opacity', this.settings.thumb_opacity); 271 }; 272 thumbs.each( 273 function(i) { 274 var link = $(this); 275 link.data("ad-i", i); 276 var image_src = link.attr('href'); 277 var thumb = link.find('img'); 278 context.whenImageLoaded(thumb[0], function() { 279 var width = thumb[0].parentNode.parentNode.offsetWidth; 280 if(thumb[0].width == 0) { 281 // If the browser tells us that the image is loaded, but the width 282 // is still 0 for some reason, we default to 100px width. 283 // It's not very nice, but it's better than 0. 284 width = 100; 285 }; 286 context.thumbs_wrapper_width += width; 287 thumbs_loaded++; 288 }); 289 context._initLink(link); 290 context.images[i] = context._createImageData(link, image_src); 291 } 292 ); 293 // Wait until all thumbs are loaded, and then set the width of the ul 294 var inter = setInterval( 295 function() { 296 if(thumb_count == thumbs_loaded) { 297 context._setThumbListWidth(context.thumbs_wrapper_width); 298 clearInterval(inter); 299 }; 300 }, 301 100 302 ); 303 }, 304 _setThumbListWidth: function(wrapper_width) { 305 wrapper_width -= 100; 306 var list = this.nav.find('.ad-thumb-list'); 307 list.css('width', wrapper_width +'px'); 308 var i = 1; 309 var last_height = list.height(); 310 while(i < 201) { 311 list.css('width', (wrapper_width + i) +'px'); 312 if(last_height != list.height()) { 313 break; 314 }; 315 last_height = list.height(); 316 i++; 317 }; 318 if(list.width() < this.nav.width()) { 319 list.width(this.nav.width()); 320 }; 321 }, 322 _initLink: function(link) { 323 var context = this; 324 link.click( 325 function() { 326 context.showImage(link.data("ad-i")); 327 context.slideshow.stop(); 328 return false;
329 } 330 ).hover( 331 function() { 332 if(!$(this).is('.ad-active') && context.settings.thumb_opacity < 1) { 333 $(this).find('img').fadeTo(300, 1); 334 }; 335 context.preloadImage(link.data("ad-i")); 336 }, 337 function() { 338 if(!$(this).is('.ad-active') && context.settings.thumb_opacity < 1) { 339 $(this).find('img').fadeTo(300, context.settings.thumb_opacity); 340 }; 341 } 342 ); 343 }, 344 _createImageData: function(thumb_link, image_src) { 345 var link = false; 346 var thumb_img = thumb_link.find("img"); 347 if(thumb_img.data('ad-link')) { 348 link = thumb_link.data('ad-link'); 349 } else if(thumb_img.attr('longdesc') && thumb_img.attr('longdesc').length) { 350 link = thumb_img.attr('longdesc'); 351 }; 352 var desc = false; 353 if(thumb_img.data('ad-desc')) { 354 desc = thumb_img.data('ad-desc'); 355 } else if(thumb_img.attr('alt') && thumb_img.attr('alt').length) { 356 desc = thumb_img.attr('alt'); 357 }; 358 var title = false; 359 if(thumb_img.data('ad-title')) { 360 title = thumb_img.data('ad-title'); 361 } else if(thumb_img.attr('title') && thumb_img.attr('title').length) { 362 title = thumb_img.attr('title'); 363 }; 364 return { thumb_link: thumb_link, image: image_src, error: false, 365 preloaded: false, desc: desc, title: title, size: false, 366 link: link }; 367 }, 368 initKeyEvents: function() { 369 var context = this; 370 $(document).keydown( 371 function(e) { 372 if(e.keyCode == 39) { 373 // right arrow 374 context.nextImage(); 375 context.slideshow.stop(); 376 } else if(e.keyCode == 37) { 377 // left arrow 378 context.prevImage(); 379 context.slideshow.stop(); 380 }; 381 } 382 ); 383 }, 384 getIndexFromHash: function() { 385 if(window.location.hash && window.location.hash.indexOf('#ad-image-') === 0) { 386 var id = window.location.hash.replace(/^#ad-image-/g, ''); 387 var thumb = this.thumbs_wrapper.find("#"+ id); 388 if(thumb.length) { 389 return this.thumbs_wrapper.find("a").index(thumb); 390 } else if(!isNaN(parseInt(id, 10))) { 391 return parseInt(id, 10); 392 }; 393 }; 394 return undefined; 395 }, 396 removeImage: function(index) { 397 if(index < 0 || index >= this.images.length) { 398 throw "Cannot remove image for index "+ index; 399 }; 400 var image = this.images[index]; 401 this.images.splice(index, 1); 402 var thumb_link = image.thumb_link; 403 var thumb_width = thumb_link[0].parentNode.offsetWidth; 404 this.thumbs_wrapper_width -= thumb_width; 405 thumb_link.remove(); 406 this._setThumbListWidth(this.thumbs_wrapper_width); 407 this.gallery_info.html((this.current_index + 1) +' / '+ this.images.length); 408 this.thumbs_wrapper.find('a').each( 409 function(i) { 410 $(this).data("ad-i", i); 411 } 412 ); 413 if(index == this.current_index && this.images.length != 0) { 414 this.showImage(0); 415 }; 416 }, 417 removeAllImages: function() { 418 for (var i = this.images.length - 1; i >= 0; i--) { 419 this.removeImage(i); 420 }; 421 }, 422 addImage: function(thumb_url, image_url, image_id, title, description) { 423 image_id = image_id || ""; 424 title = title || ""; 425 description = description || ""; 426 var li = $('<li><a href="'+ image_url +'" id="'+ image_id +'">' + 427 '<img src="'+ thumb_url +'" title="'+ title +'" alt="'+ description +'">' + 428 '</a></li>'); 429 var context = this; 430 this.thumbs_wrapper.find("ul").append(li); 431 432 var link = li.find("a"); 433 var thumb = link.find("img"); 434 thumb.css('opacity', this.settings.thumb_opacity); 435 436 this.whenImageLoaded(thumb[0], function() { 437 var thumb_width = thumb[0].parentNode.parentNode.offsetWidth; 438 if(thumb[0].width == 0) { 439 // If the browser tells us that the image is loaded, but the width 440 // is still 0 for some reason, we default to 100px width. 441 // It's not very nice, but it's better than 0. 442 thumb_width = 100; 443 }; 444 445 context.thumbs_wrapper_width += thumb_width; 446 context._setThumbListWidth(context.thumbs_wrapper_width); 447 }); 448 var i = this.images.length; 449 link.data("ad-i", i); 450 this._initLink(link); 451 this.images[i] = context._createImageData(link, image_url); 452 this.gallery_info.html((this.current_index + 1) +' / '+ this.images.length); 453 }, 454 initHashChange: function() { 455 var context = this; 456 if("onhashchange" in window) { 457 $(window).bind("hashchange", function() { 458 var index = context.getIndexFromHash(); 459 if(typeof index != "undefined" && index != context.current_index) { 460 context.showImage(index); 461 }; 462 }); 463 } else { 464 var current_hash = window.location.hash; 465 setInterval(function() { 466 if(window.location.hash != current_hash) { 467 current_hash = window.location.hash; 468 var index = context.getIndexFromHash(); 469 if(typeof index != "undefined" && index != context.current_index) { 470 context.showImage(index); 471 }; 472 }; 473 }, 200); 474 }; 475 }, 476 initNextAndPrev: function() { 477 this.next_link = $('<div class="ad-next"><div class="ad-next-image"></div></div>'); 478 this.prev_link = $('<div class="ad-prev"><div class="ad-prev-image"></div></div>'); 479 this.image_wrapper.append(this.next_link); 480 this.image_wrapper.append(this.prev_link); 481 var context = this; 482 this.prev_link.add(this.next_link).mouseover( 483 function(e) { 484 // IE 6 hides the wrapper div, so we have to set it's width
485 $(this).css('height', context.image_wrapper_height); 486 $(this).find('div').show(); 487 } 488 ).mouseout( 489 function(e) { 490 $(this).find('div').hide(); 491 } 492 ).click( 493 function() { 494 if($(this).is('.ad-next')) { 495 context.nextImage(); 496 context.slideshow.stop(); 497 } else { 498 context.prevImage(); 499 context.slideshow.stop(); 500 }; 501 } 502 ).find('div').css('opacity', 0.7); 503 }, 504 initBackAndForward: function() { 505 var context = this; 506 this.scroll_forward = $('<div class="ad-forward"></div>'); 507 this.scroll_back = $('<div class="ad-back"></div>'); 508 this.nav.append(this.scroll_forward); 509 this.nav.prepend(this.scroll_back); 510 var has_scrolled = 0; 511 var thumbs_scroll_interval = false; 512 $(this.scroll_back).add(this.scroll_forward).click( 513 function() { 514 // We don't want to jump the whole width, since an image 515 // might be cut at the edge 516 var width = context.nav_display_width - 50; 517 if(context.settings.scroll_jump > 0) { 518 var width = context.settings.scroll_jump; 519 }; 520 if($(this).is('.ad-forward')) { 521 var left = context.thumbs_wrapper.scrollLeft() + width; 522 } else { 523 var left = context.thumbs_wrapper.scrollLeft() - width; 524 }; 525 if(context.settings.slideshow.stop_on_scroll) { 526 context.slideshow.stop(); 527 }; 528 context.thumbs_wrapper.animate({scrollLeft: left +'px'}); 529 return false; 530 } 531 ).hover( 532 function() { 533 var direction = 'left'; 534 if($(this).is('.ad-forward')) { 535 direction = 'right'; 536 }; 537 thumbs_scroll_interval = setInterval( 538 function() { 539 has_scrolled++; 540 // Don't want to stop the slideshow just because we scrolled a pixel or two 541 if(has_scrolled > 30 && context.settings.slideshow.stop_on_scroll) { 542 context.slideshow.stop(); 543 }; 544 var left = context.thumbs_wrapper.scrollLeft() + 1; 545 if(direction == 'left') { 546 left = context.thumbs_wrapper.scrollLeft() - 1; 547 }; 548 context.thumbs_wrapper.scrollLeft(left); 549 }, 550 10 551 ); 552 }, 553 function() { 554 has_scrolled = 0; 555 clearInterval(thumbs_scroll_interval); 556 } 557 ); 558 }, 559 _afterShow: function() { 560 this.gallery_info.html((this.current_index + 1) +' / '+ this.images.length); 561 if(!this.settings.cycle) { 562 // Needed for IE 563 this.prev_link.show().css('height', this.image_wrapper_height); 564 this.next_link.show().css('height', this.image_wrapper_height); 565 if(this.current_index == (this.images.length - 1)) { 566 this.next_link.hide(); 567 }; 568 if(this.current_index == 0) { 569 this.prev_link.hide(); 570 }; 571 }; 572 if(this.settings.update_window_hash) { 573 var thumb_link = this.images[this.current_index].thumb_link; 574 if (thumb_link.attr("id")) { 575 window.location.hash = "#ad-image-"+ thumb_link.attr("id"); 576 } else { 577 window.location.hash = "#ad-image-"+ this.current_index; 578 }; 579 }; 580 this.fireCallback(this.settings.callbacks.afterImageVisible); 581 }, 582 /** 583 * Checks if the image is small enough to fit inside the container 584 * If it's not, shrink it proportionally 585 */ 586 _getContainedImageSize: function(image_width, image_height) { 587 if(image_height > this.image_wrapper_height) { 588 var ratio = image_width / image_height; 589 image_height = this.image_wrapper_height; 590 image_width = this.image_wrapper_height * ratio; 591 }; 592 if(image_width > this.image_wrapper_width) { 593 var ratio = image_height / image_width; 594 image_width = this.image_wrapper_width; 595 image_height = this.image_wrapper_width * ratio; 596 }; 597 return {width: image_width, height: image_height}; 598 }, 599 /** 600 * If the image dimensions are smaller than the wrapper, we position 601 * it in the middle anyway 602 */ 603 _centerImage: function(img_container, image_width, image_height) { 604 img_container.css('top', '0px'); 605 if(image_height < this.image_wrapper_height) { 606 var dif = this.image_wrapper_height - image_height; 607 img_container.css('top', (dif / 2) +'px'); 608 }; 609 img_container.css('left', '0px'); 610 if(image_width < this.image_wrapper_width) { 611 var dif = this.image_wrapper_width - image_width; 612 img_container.css('left', (dif / 2) +'px'); 613 }; 614 }, 615 _getDescription: function(image) { 616 var desc = false;
617 if(image.desc.length || image.title.length) { 618 var title = ''; 619 if(image.title.length) { 620 title = '<strong class="ad-description-title">'+ image.title +'</strong>'; 621 }; 622 var desc = ''; 623 if(image.desc.length) { 624 desc = '<span>'+ image.desc +'</span>'; 625 }; 626 desc = $('<p class="ad-image-description">'+ title + desc +'</p>'); 627 }; 628 return desc; 629 }, 630 /** 631 * @param function callback Gets fired when the image has loaded, is displaying 632 * and it's animation has finished 633 */ 634 showImage: function(index, callback) { 635 if(this.images[index] && !this.in_transition && index != this.current_index) { 636 var context = this; 637 var image = this.images[index]; 638 this.in_transition = true; 639 if(!image.preloaded) { 640 this.loading(true); 641 this.preloadImage(index, function() { 642 context.loading(false); 643 context._showWhenLoaded(index, callback); 644 }); 645 } else { 646 this._showWhenLoaded(index, callback); 647 }; 648 }; 649 }, 650 /** 651 * @param function callback Gets fired when the image has loaded, is displaying 652 * and it's animation has finished 653 */ 654 _showWhenLoaded: function(index, callback) { 655 if(this.images[index]) { 656 var context = this; 657 var image = this.images[index]; 658 var img_container = $(document.createElement('div')).addClass('ad-image'); 659 var img = $(new Image()).attr('src', image.image); 660 if(image.link) { 661 var link = $('<a href="'+ image.link +'" target="_blank"></a>'); 662 link.append(img); 663 img_container.append(link); 664 } else { 665 img_container.append(img); 666 }; 667 this.image_wrapper.prepend(img_container); 668 var size = this._getContainedImageSize(image.size.width, image.size.height); 669 img.attr('width', size.width); 670 img.attr('height', size.height); 671 img_container.css({width: size.width +'px', height: size.height +'px'}); 672 this._centerImage(img_container, size.width, size.height); 673 var desc = this._getDescription(image); 674 if(desc) { 675 if(!this.settings.description_wrapper && !this.settings.hooks.displayDescription) { 676 img_container.append(desc); 677 var width = size.width - parseInt(desc.css('padding-left'), 10) - parseInt(desc.css('padding-right'), 10); 678 desc.css('width', width +'px'); 679 } else if(this.settings.hooks.displayDescription) { 680 this.settings.hooks.displayDescription.call(this, image); 681 } else { 682 var wrapper = this.settings.description_wrapper; 683 wrapper.append(desc); 684 }; 685 }; 686 this.highLightThumb(this.images[index].thumb_link); 687 688 var direction = 'right'; 689 if(this.current_index < index) { 690 direction = 'left'; 691 }; 692 this.fireCallback(this.settings.callbacks.beforeImageVisible); 693 if(this.current_image || this.settings.animate_first_image) { 694 var animation_speed = this.settings.animation_speed; 695 var easing = 'swing'; 696 var animation = this.animations[this.settings.effect].call(this, img_container, direction, desc); 697 if(typeof animation.speed != 'undefined') { 698 animation_speed = animation.speed; 699 }; 700 if(typeof animation.easing != 'undefined') { 701 easing = animation.easing; 702 }; 703 if(this.current_image) { 704 var old_image = this.current_image; 705 var old_description = this.current_description; 706 old_image.animate(animation.old_image, animation_speed, easing, 707 function() { 708 old_image.remove(); 709 if(old_description) old_description.remove(); 710 } 711 ); 712 }; 713 img_container.animate(animation.new_image, animation_speed, easing, 714 function() { 715 context.current_index = index; 716 context.current_image = img_container; 717 context.current_description = desc;
718 context.in_transition = false; 719 context._afterShow(); 720 context.fireCallback(callback); 721 } 722 ); 723 } else { 724 this.current_index = index; 725 this.current_image = img_container; 726 context.current_description = desc; 727 this.in_transition = false; 728 context._afterShow(); 729 this.fireCallback(callback); 730 }; 731 }; 732 }, 733 nextIndex: function() { 734 if(this.current_index == (this.images.length - 1)) { 735 if(!this.settings.cycle) { 736 return false; 737 }; 738 var next = 0; 739 } else { 740 var next = this.current_index + 1; 741 }; 742 return next; 743 }, 744 nextImage: function(callback) { 745 var next = this.nextIndex(); 746 if(next === false) return false; 747 this.preloadImage(next + 1); 748 this.showImage(next, callback); 749 return true; 750 }, 751 prevIndex: function() { 752 if(this.current_index == 0) { 753 if(!this.settings.cycle) { 754 return false; 755 }; 756 var prev = this.images.length - 1; 757 } else { 758 var prev = this.current_index - 1; 759 }; 760 return prev; 761 }, 762 prevImage: function(callback) { 763 var prev = this.prevIndex(); 764 if(prev === false) return false; 765 this.preloadImage(prev - 1); 766 this.showImage(prev, callback); 767 return true; 768 }, 769 preloadAll: function() { 770 var context = this; 771 var i = 0; 772 function preloadNext() {
773 if(i < context.images.length) { 774 i++; 775 context.preloadImage(i, preloadNext); 776 }; 777 }; 778 context.preloadImage(i, preloadNext); 779 }, 780 preloadImage: function(index, callback) { 781 if(this.images[index]) { 782 var image = this.images[index]; 783 if(!this.images[index].preloaded) { 784 var img = $(new Image()); 785 img.attr('src', image.image); 786 if(!this.isImageLoaded(img[0])) { 787 this.preloads.append(img); 788 var context = this; 789 img.load( 790 function() { 791 image.preloaded = true; 792 image.size = { width: this.width, height: this.height }; 793 context.fireCallback(callback); 794 } 795 ).error( 796 function() { 797 image.error = true; 798 image.preloaded = false; 799 image.size = false; 800 } 801 ); 802 } else { 803 image.preloaded = true; 804 image.size = { width: img[0].width, height: img[0].height }; 805 this.fireCallback(callback); 806 }; 807 } else { 808 this.fireCallback(callback); 809 }; 810 }; 811 }, 812 whenImageLoaded: function(img, callback) { 813 if (this.isImageLoaded(img)) { 814 callback && callback(); 815 } else { 816 $(img).load(callback); 817 }; 818 }, 819 isImageLoaded: function(img) { 820 if(typeof img.complete != 'undefined' && !img.complete) { 821 return false; 822 }; 823 if(typeof img.naturalWidth != 'undefined' && img.naturalWidth == 0) { 824 return false; 825 }; 826 return true; 827 }, 828 highLightThumb: function(thumb) { 829 this.thumbs_wrapper.find('.ad-active').removeClass('ad-active'); 830 thumb.addClass('ad-active'); 831 if(this.settings.thumb_opacity < 1) { 832 this.thumbs_wrapper.find('a:not(.ad-active) img').fadeTo(300, this.settings.thumb_opacity); 833 thumb.find('img').fadeTo(300, 1); 834 }; 835 var left = thumb[0].parentNode.offsetLeft; 836 left -= (this.nav_display_width / 2) - (thumb[0].offsetWidth / 2); 837 this.thumbs_wrapper.animate({scrollLeft: left +'px'}); 838 }, 839 fireCallback: function(fn) { 840 if($.isFunction(fn)) { 841 fn.call(this); 842 }; 843 } 844 }; 845 846 function AdGallerySlideshow(nextimage_callback, settings) { 847 this.init(nextimage_callback, settings); 848 }; 849 AdGallerySlideshow.prototype = { 850 start_link: false, 851 stop_link: false, 852 countdown: false, 853 controls: false, 854 855 settings: false, 856 nextimage_callback: false, 857 enabled: false, 858 running: false, 859 countdown_interval: false, 860 init: function(nextimage_callback, settings) { 861 var context = this; 862 this.nextimage_callback = nextimage_callback; 863 this.settings = settings; 864 }, 865 create: function() { 866 this.start_link = $('<span class="ad-slideshow-start">'+ this.settings.start_label +'</span>'); 867 this.stop_link = $('<span class="ad-slideshow-stop">'+ this.settings.stop_label +'</span>'); 868 this.countdown = $('<span class="ad-slideshow-countdown"></span>'); 869 this.controls = $('<div class="ad-slideshow-controls"></div>'); 870 this.controls.append(this.start_link).append(this.stop_link).append(this.countdown); 871 this.countdown.hide(); 872 873 var context = this; 874 this.start_link.click( 875 function() { 876 context.start(); 877 } 878 ); 879 this.stop_link.click( 880 function() { 881 context.stop(); 882 } 883 ); 884 $(document).keydown( 885 function(e) { 886 if(e.keyCode == 83) { 887 // 's' 888 if(context.running) { 889 context.stop(); 890 } else { 891 context.start(); 892 }; 893 }; 894 } 895 ); 896 return this.controls; 897 }, 898 disable: function() { 899 this.enabled = false; 900 this.stop(); 901 this.controls.hide(); 902 }, 903 enable: function() { 904 this.enabled = true; 905 this.controls.show(); 906 }, 907 toggle: function() { 908 if(this.enabled) { 909 this.disable(); 910 } else { 911 this.enable(); 912 }; 913 }, 914 start: function() { 915 if(this.running || !this.enabled) return false;
916 var context = this; 917 this.running = true; 918 this.controls.addClass('ad-slideshow-running'); 919 this._next(); 920 this.fireCallback(this.settings.onStart); 921 return true; 922 }, 923 stop: function() { 924 if(!this.running) return false; 925 this.running = false; 926 this.countdown.hide(); 927 this.controls.removeClass('ad-slideshow-running'); 928 clearInterval(this.countdown_interval); 929 this.fireCallback(this.settings.onStop); 930 return true; 931 }, 932 _next: function() { 933 var context = this; 934 var pre = this.settings.countdown_prefix; 935 var su = this.settings.countdown_sufix; 936 clearInterval(context.countdown_interval); 937 this.countdown.show().html(pre + (this.settings.speed / 1000) + su); 938 var slide_timer = 0; 939 this.countdown_interval = setInterval( 940 function() { 941 slide_timer += 1000; 942 if(slide_timer >= context.settings.speed) { 943 var whenNextIsShown = function() { 944 // A check so the user hasn't stoped the slideshow during the 945 // animation 946 if(context.running) { 947 context._next(); 948 }; 949 slide_timer = 0; 950 }; 951 if(!context.nextimage_callback(whenNextIsShown)) { 952 context.stop(); 953 }; 954 slide_timer = 0; 955 }; 956 var sec = parseInt(context.countdown.text().replace(/[^0-9]/g, ''), 10); 957 sec--; 958 if(sec > 0) { 959 context.countdown.html(pre + sec + su); 960 }; 961 }, 962 1000 963 ); 964 }, 965 fireCallback: function(fn) { 966 if($.isFunction(fn)) { 967 fn.call(this); 968 }; 969 } 970 }; 971})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.