PageSourceSearch

https://mbntexas.biz/lib/JQuery/Gallery/jquery.ad-gallery.js?v=1785417801

js mbntexas.biz collected 2026-09-30 13:15:11 UTC 30,504 bytes, 851 lines download raw bytes

1
2/**
3 * Copyright (c) 2010 Anders Ekdahl (http://coffeescripter.com/)
4 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
5 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
6 *
7 * Version: 1.2.4
8 *
9 * Demo and documentation: http://coffeescripter.com/code/ad-gallery/
10 */
11(function($) {
12  $.fn.adGallery = function(options) {
13    var defaults = { loader_image: '/JQuery/Gallery/loader.gif',
14                     start_at_index: 0,
15                     description_wrapper: false,
16                     thumb_opacity: 0.7,
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                     callbacks: {
40                       init: false,
41                       afterImageVisible: false,
42                       beforeImageVisible: false
43                     }
44    };
45    var settings = $.extend(false, defaults, options);
46    if(options && options.slideshow) {
47      settings.slideshow = $.extend(false, defaults.slideshow, options.slideshow);
48    };
49    if(!settings.slideshow.enable) {
50      settings.slideshow.autostart = false;
51    };
52    var galleries = [];
53    $(this).each(function() {
54      var gallery = new AdGallery(this, settings);
55      galleries[galleries.length] = gallery;
56    });
57    // Sorry, breaking the jQuery chain because the gallery instances
58    // are returned so you can fiddle with them
59    return galleries;
60  };
61
62  function VerticalSlideAnimation(img_container, direction, desc) {
63    var current_top = parseInt(img_container.css('top'), 10);
64    if(direction == 'left') {
65      var old_image_top = '-'+ this.image_wrapper_height +'px';
66      img_container.css('top', this.image_wrapper_height +'px');
67    } else {
68      var old_image_top = this.image_wrapper_height +'px';
69      img_container.css('top', '-'+ this.image_wrapper_height +'px');
70    };
71    if(desc) {
72      desc.css('bottom', '-'+ desc[0].offsetHeight +'px');
73      desc.animate({bottom: 0}, this.settings.animation_speed * 2);
74    };
75    if(this.current_description) {
76      this.current_description.animate({bottom: '-'+ this.current_description[0].offsetHeight +'px'}, this.settings.animation_speed * 2);
77    };
78    return {old_image: {top: old_image_top},
79            new_image: {top: current_top}};
80  };
81
82  function HorizontalSlideAnimation(img_container, direction, desc) {
83    var current_left = parseInt(img_container.css('left'), 10);
84    if(direction == 'left') {
85      var old_image_left = '-'+ this.image_wrapper_width +'px';
86      img_container.css('left',this.image_wrapper_width +'px');
87    } else {
88      var old_image_left = this.image_wrapper_width +'px';
89      img_container.css('left','-'+ this.image_wrapper_width +'px');
90    };
91    if(desc) {
92      desc.css('bottom', '-'+ desc[0].offsetHeight +'px');
93      desc.animate({bottom: 0}, this.settings.animation_speed * 2);
94    };
95    if(this.current_description) {
96      this.current_description.animate({bottom: '-'+ this.current_description[0].offsetHeight +'px'}, this.settings.animation_speed * 2);
97    };
98    return {old_image: {left: old_image_left},
99            new_image: {left: current_left}};
100  };
101
102  function ResizeAnimation(img_container, direction, desc) {
103    var image_width = img_container.width();
104    var image_height = img_container.height();
105    var current_left = parseInt(img_container.css('left'), 10);
106    var current_top = parseInt(img_container.css('top'), 10);
107    img_container.css({width: 0, height: 0, top: this.image_wrapper_height / 2, left: this.image_wrapper_width / 2});
108    return {old_image: {width: 0,
109                        height: 0,
110                        top: this.image_wrapper_height / 2,
111                        left: this.image_wrapper_width / 2},
112            new_image: {width: image_width,
113                        height: image_height,
114                        top: current_top,
115                        left: current_left}};
116  };
117
118  function FadeAnimation(img_container, direction, desc) {
119    img_container.css('opacity', 0);
120    return {old_image: {opacity: 0},
121            new_image: {opacity: 1}};
122  };
123
124  // Sort of a hack, will clean this up... eventually
125  function NoneAnimation(img_container, direction, desc) {
126    img_container.css('opacity', 0);
127    return {old_image: {opacity: 0},
128            new_image: {opacity: 1},
129            speed: 0};
130  };
131
132  function AdGallery(wrapper, settings) {
133    this.init(wrapper, settings);
134  };
135  AdGallery.prototype = {
136    // Elements
137    wrapper: false,
138    image_wrapper: false,
139    gallery_info: false,
140    nav: false,
141    loader: false,
142    preloads: false,
143    thumbs_wrapper: false,
144    scroll_back: false,
145    scroll_forward: false,
146    next_link: false,
147    prev_link: false,
148
149    slideshow: false,
150    image_wrapper_width: 0,
151    image_wrapper_height: 0,
152    current_index: 0,
153    current_image: false,
154    current_description: false,
155    nav_display_width: 0,
156    settings: false,
157    images: false,
158    in_transition: false,
159    animations: false,
160    init: function(wrapper, settings) {
161      var context = this;
162      this.wrapper = $(wrapper);
163      this.settings = settings;
164      this.setupElements();
165      this.setupAnimations();
166      if(this.settings.width) {
167        this.image_wrapper_width = this.settings.width;
168        this.image_wrapper.width(this.settings.width);
169        this.wrapper.width(this.settings.width);
170      } else {
171        this.image_wrapper_width = this.image_wrapper.width();
172      };
173      if(this.settings.height) {
174        this.image_wrapper_height = this.settings.height;
175        this.image_wrapper.height(this.settings.height);
176      } else {
177        this.image_wrapper_height = this.image_wrapper.height();
178      };
179      this.nav_display_width = this.nav.width();
180      this.current_index = 0;
181      this.current_image = false;
182      this.current_description = false;
183      this.in_transition = false;
184      this.findImages();
185      if(this.settings.display_next_and_prev) {
186        this.initNextAndPrev();
187      };
188      // The slideshow needs a callback to trigger the next image to be shown
189      // but we don't want to give it access to the whole gallery instance
190      var nextimage_callback = function(callback) {
191        return context.nextImage(callback);
192      };
193      this.slideshow = new AdGallerySlideshow(nextimage_callback, this.settings.slideshow);
194      this.controls.append(this.slideshow.create());
195      if(this.settings.slideshow.enable) {
196        this.slideshow.enable();
197      } else {
198        this.slideshow.disable();
199      };
200      if(this.settings.display_back_and_forward) {
201        this.initBackAndForward();
202      };
203      if(this.settings.enable_keyboard_move) {
204        this.initKeyEvents();
205      };
206      var start_at = parseInt(this.settings.start_at_index, 10);
207      if(window.location.hash && window.location.hash.indexOf('#ad-image') === 0) {
208        start_at = window.location.hash.replace(/[^0-9]+/g, '');
209        // Check if it's a number
210        if((start_at * 1) != start_at) {
211          start_at = this.settings.start_at_index;
212        };
213      };
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 thumb_wrapper_width = 0;
267      var thumbs_loaded = 0;
268      var thumbs = this.thumbs_wrapper.find('a');
269      var thumb_count = thumbs.length;
270      if(this.settings.thumb_opacity < 1) {
271        thumbs.find('img').css('opacity', this.settings.thumb_opacity);
272      };
273      thumbs.each(
274        function(i) {
275          var link = $(this);
276          var image_src = link.attr('href');
277          var thumb = link.find('img');
278          // Check if the thumb has already loaded
279          if(!context.isImageLoaded(thumb[0])) {
280            thumb.load(
281              function() {
282                thumb_wrapper_width += this.parentNode.parentNode.offsetWidth;
283                thumbs_loaded++;
284              }
285            );
286          } else{
287            thumb_wrapper_width += thumb[0].parentNode.parentNode.offsetWidth;
288            thumbs_loaded++;
289          };
290          link.addClass('ad-thumb'+ i);
291          link.click(
292            function() {
293              context.showImage(i);
294              context.slideshow.stop();
295              return false;
296            }
297          ).hover(
298            function() {
299              if(!$(this).is('.ad-active') && context.settings.thumb_opacity < 1) {
300                $(this).find('img').fadeTo(300, 1);
301              };
302              context.preloadImage(i);
303            },
304            function() {
305              if(!$(this).is('.ad-active') && context.settings.thumb_opacity < 1) {
306                $(this).find('img').fadeTo(300, context.settings.thumb_opacity);
307              };
308            }
309          );
310          var link = false;
311          if(thumb.data('ad-link')) {
312            link = thumb.data('ad-link');
313          } else if(thumb.attr('longdesc') && thumb.attr('longdesc').length) {
314            link = thumb.attr('longdesc');
315          };
316          var desc = false;
317          if(thumb.data('ad-desc')) {
318            desc = thumb.data('ad-desc');
319          } else if(thumb.attr('alt') && thumb.attr('alt').length) {
320            desc = thumb.attr('alt');
321          };
322          var title = false;
323          if(thumb.data('ad-title')) {
324            title = thumb.data('ad-title');
325          } else if(thumb.attr('title') && thumb.attr('title').length) {
326            title = thumb.attr('title');
327          };
328          context.images[i] = { thumb: thumb.attr('src'), image: image_src, error: false,
329                                preloaded: false, desc: desc, title: title, size: false,
330                                link: link };
331        }
332      );
333      // Wait until all thumbs are loaded, and then set the width of the ul
334      var inter = setInterval(
335        function() {
336          if(thumb_count == thumbs_loaded) {
337            thumb_wrapper_width -= 100;
338            var list = context.nav.find('.ad-thumb-list');
339            list.css('width', thumb_wrapper_width +'px');
340            var i = 1;
341            var last_height = list.height();
342            while(i < 201) {
343              list.css('width', (thumb_wrapper_width + i) +'px');
344              if(last_height != list.height()) {
345                break;
346              }
347              last_height = list.height();
348              i++;
349            }
350            clearInterval(inter);
351          };
352        },
353        100
354      );
355    },
356    initKeyEvents: function() {
357      var context = this;
358      $(document).keydown(
359        function(e) {
360          if(e.keyCode == 39) {
361            // right arrow
362            context.nextImage();
363            context.slideshow.stop();
364          } else if(e.keyCode == 37) {
365            // left arrow
366            context.prevImage();
367            context.slideshow.stop();
368          };
369        }
370      );
371    },
372    initNextAndPrev: function() {
373      this.next_link = $('<div class="ad-next"><div class="ad-next-image"></div></div>');
374      this.prev_link = $('<div class="ad-prev"><div class="ad-prev-image"></div></div>');
375      this.image_wrapper.append(this.next_link);
376      this.image_wrapper.append(this.prev_link);
377      var context = this;
378      this.prev_link.add(this.next_link).mouseover(
379        function(e) {
380          // IE 6 hides the wrapper div, so we have to set it's width
381          $(this).css('height', context.image_wrapper_height);
382          $(this).find('div').show();
383        }
384      ).mouseout(
385        function(e) {
386          $(this).find('div').hide();
387        }
388      ).click(
389        function() {
390          if($(this).is('.ad-next')) {
391            context.nextImage();
392            context.slideshow.stop();
393          } else {
394            context.prevImage();
395            context.slideshow.stop();
396          };
397        }
398      ).find('div').css('opacity', 0.7);
399    },
400    initBackAndForward: function() {
401      var context = this;
402      this.scroll_forward = $('<div class="ad-forward"></div>');
403      this.scroll_back = $('<div class="ad-back"></div>');
404      this.nav.append(this.scroll_forward);
405      this.nav.prepend(this.scroll_back);
406      var has_scrolled = 0;
407      var thumbs_scroll_interval = false;
408      $(this.scroll_back).add(this.scroll_forward).click(
409        function() {
410          // We don't want to jump the whole width, since an image
411          // might be cut at the edge
412          var width = context.nav_display_width - 50;
413          if(context.settings.scroll_jump > 0) {
414            var width = context.settings.scroll_jump;
415          };
416          if($(this).is('.ad-forward')) {
417            var left = context.thumbs_wrapper.scrollLeft() + width;
418          } else {
419            var left = context.thumbs_wrapper.scrollLeft() - width;
420          };
421          if(context.settings.slideshow.stop_on_scroll) {
422            context.slideshow.stop();
423          };
424          context.thumbs_wrapper.animate({scrollLeft: left +'px'});
425          return false;
426        }
427      ).css('opacity', 0.6).hover(
428        function() {
429          var direction = 'left';
430          if($(this).is('.ad-forward')) {
431            direction = 'right';
432          };
433          thumbs_scroll_interval = setInterval(
434            function() {
435              has_scrolled++;
436              // Don't want to stop the slideshow just because we scrolled a pixel or two
437              if(has_scrolled > 30 && context.settings.slideshow.stop_on_scroll) {
438                context.slideshow.stop();
439              };
440              var left = context.thumbs_wrapper.scrollLeft() + 1;
441              if(direction == 'left') {
442                left = context.thumbs_wrapper.scrollLeft() - 1;
443              };
444              context.thumbs_wrapper.scrollLeft(left);
445            },
446            10
447          );
448          $(this).css('opacity', 1);
449        },
450        function() {
451          has_scrolled = 0;
452          clearInterval(thumbs_scroll_interval);
453          $(this).css('opacity', 0.6);
454        }
455      );
456    },
457    _afterShow: function() {
458      this.gallery_info.html((this.current_index + 1) +' / '+ this.images.length);
459      if(!this.settings.cycle) {
460        // Needed for IE
461        this.prev_link.show().css('height', this.image_wrapper_height);
462        this.next_link.show().css('height', this.image_wrapper_height);
463        if(this.current_index == (this.images.length - 1)) {
464          this.next_link.hide();
465        };
466        if(this.current_index == 0) {
467          this.prev_link.hide();
468        };
469      };
470      this.fireCallback(this.settings.callbacks.afterImageVisible);
471    },
472    /**
473     * Checks if the image is small enough to fit inside the container
474     * If it's not, shrink it proportionally
475     */
476    _getContainedImageSize: function(image_width, image_height) {
477      if(image_height > this.image_wrapper_height) {
478        var ratio = image_width / image_height;
479        image_height = this.image_wrapper_height;
480        image_width = this.image_wrapper_height * ratio;
481      };
482      if(image_width > this.image_wrapper_width) {
483  	    var ratio = image_height / image_width;
484  	    image_width = this.image_wrapper_width;
485  	    image_height = this.image_wrapper_width * ratio;
486  	  };
487      return {width: image_width, height: image_height};
488    },
489    /**
490     * If the image dimensions are smaller than the wrapper, we position
491     * it in the middle anyway
492     */
493    _centerImage: function(img_container, image_width, image_height) {
494      img_container.css('top', '0px');
495      if(image_height < this.image_wrapper_height) {
496        var dif = this.image_wrapper_height - image_height;
497        img_container.css('top', (dif / 2) +'px');
498      };
499      img_container.css('left', '0px');
500      if(image_width < this.image_wrapper_width) {
501        var dif = this.image_wrapper_width - image_width;
502        img_container.css('left', (dif / 2) +'px');
503      };
504    },
505    _getDescription: function(image) {
506      var desc = false;
507      if(image.desc.length || image.title.length) {
508        var title = '';
509        if(image.title.length) {
510          title = '<strong class="ad-description-title">
510'+ image.title +'</strong>';
511        };
512        var desc = '';
513        if(image.desc.length) {
514          desc = '<span>'+ image.desc +'</span>';
515        };
516        desc = $('<p class="ad-image-description">'+ title + desc +'</p>');
517      };
518      return desc;
519    },
520    /**
521     * @param function callback Gets fired when the image has loaded, is displaying
522     *                          and it's animation has finished
523     */
524    showImage: function(index, callback) {
525      if(this.images[index] && !this.in_transition) {
526        var context = this;
527        var image = this.images[index];
528        this.in_transition = true;
529        if(!image.preloaded) {
530          this.loading(true);
531          this.preloadImage(index, function() {
532            context.loading(false);
533            context._showWhenLoaded(index, callback);
534          });
535        } else {
536          this._showWhenLoaded(index, callback);
537        };
538      };
539    },
540    /**
541     * @param function callback Gets fired when the image has loaded, is displaying
542     *                          and it's animation has finished
543     */
544    _showWhenLoaded: function(index, callback) {
545      if(this.images[index]) {
546        var context = this;
547        var image = this.images[index];
548        var img_container = $(document.createElement('div')).addClass('ad-image');
549        var img = $(new Image()).attr('src', image.image);
550        if(image.link) {
551          var link = $('<a href="'+ image.link +'" target="_blank"></a>');
552          link.append(img);
553          img_container.append(link);
554        } else {
555          img_container.append(img);
556        }
557        this.image_wrapper.prepend(img_container);
558        var size = this._getContainedImageSize(image.size.width, image.size.height);
559        img.attr('width', size.width);
560        img.attr('height', size.height);
561        img_container.css({width: size.width +'px', height: size.height +'px'});
562        this._centerImage(img_container, size.width, size.height);
563        var desc = this._getDescription(image, img_container);
564        if(desc) {
565          if(!this.settings.description_wrapper) {
566            img_container.append(desc);
567            var width = size.width - parseInt(desc.css('padding-left'), 10) - parseInt(desc.css('padding-right'), 10);
568            desc.css('width', width +'px');
569          } else {
570            this.settings.description_wrapper.append(desc);
571          }
572        };
573        this.highLightThumb(this.nav.find('.ad-thumb'+ index));
574
575        var direction = 'right';
576        if(this.current_index < index) {
577          direction = 'left';
578        };
579        this.fireCallback(this.settings.callbacks.beforeImageVisible);
580        if(this.current_image || this.settings.animate_first_image) {
581          var animation_speed = this.settings.animation_speed;
582          var easing = 'swing';
583          var animation = this.animations[this.settings.effect].call(this, img_container, direction, desc);
584          if(typeof animation.speed != 'undefined') {
585            animation_speed = animation.speed;
586          };
587          if(typeof animation.easing != 'undefined') {
588            easing = animation.easing;
589          };
590          if(this.current_image) {
591            var old_image = this.current_image;
592            var old_description = this.current_description;
593            old_image.animate(animation.old_image, animation_speed, easing,
594              function() {
595                old_image.remove();
596                if(old_description) old_description.remove();
597              }
598            );
599          };
600          img_container.animate(animation.new_image, animation_speed, easing,
601            function() {
602              context.current_index = index;
603              context.current_image = img_container;
604              context.current_description = desc;
605              context.in_transition = false;
606              context._afterShow();
607              context.fireCallback(callback);
608            }
609          );
610        } else {
611          this.current_index = index;
612          this.current_image = img_container;
613          context.current_description = desc;
614          this.in_transition = false;
615          context._afterShow();
616          this.fireCallback(callback);
617        };
618      };
619    },
620    nextIndex: function() {
621      if(this.current_index == (this.images.length - 1)) {
622        if(!this.settings.cycle) {
623          return false;
624        };
625        var next = 0;
626      } else {
627        var next = this.current_index + 1;
628      };
629      return next;
630    },
631    nextImage: function(callback) {
632      var next = this.nextIndex();
633      if(next === false) return false;
634      this.preloadImage(next + 1);
635      this.showImage(next, callback);
636      return true;
637    },
638    prevIndex: function() {
639      if(this.current_index == 0) {
640        if(!this.settings.cycle) {
641          return false;
642        };
643        var prev = this.images.length - 1;
644      } else {
645        var prev = this.current_index - 1;
646      };
647      return prev;
648    },
649    prevImage: function(callback) {
650      var prev = this.prevIndex();
651      if(prev === false) return false;
652      this.preloadImage(prev - 1);
653      this.showImage(prev, callback);
654      return true;
655    },
656    preloadAll: function() {
657      var context = this;
658      var i = 0;
659      function preloadNext() {
660        if(i < context.images.length) {
661          i++;
662          context.preloadImage(i, preloadNext);
663        };
664      };
665      context.preloadImage(i, preloadNext);
666    },
667    preloadImage: function(index, callback) {
668      if(this.images[index]) {
669        var image = this.images[index];
670        if(!this.images[index].preloaded) {
671          var img = $(new Image());
672          img.attr('src', image.image);
673          if(!this.isImageLoaded(img[0])) {
674            this.preloads.append(img);
675            var context = this;
676            img.load(
677              function() {
678                image.preloaded = true;
679                image.size = { width: this.width, height: this.height };
680                context.fireCallback(callback);
681              }
682            ).error(
683              function() {
684                image.error = true;
685                image.preloaded = false;
686                image.size = false;
687              }
688            );
689          } else {
690            image.preloaded = true;
691            image.size = { width: img[0].width, height: img[0].height };
692            this.fireCallback(callback);
693          };
694        } else {
695          this.fireCallback(callback);
696        };
697      };
698    },
699    isImageLoaded: function(img) {
700      if(typeof img.complete != 'undefined' && !img.complete) {
701        return false;
702      };
703      if(typeof img.naturalWidth != 'undefined' && img.naturalWidth == 0) {
704        return false;
705      };
706      return true;
707    },
708    highLightThumb: function(thumb) {
709      this.thumbs_wrapper.find('.ad-active').removeClass('ad-active');
710      thumb.addClass('ad-active');
711      if(this.settings.thumb_opacity < 1) {
712        this.thumbs_wrapper.find('a:not(.ad-active) img').fadeTo(300, this.settings.thumb_opacity);
713        thumb.find('img').fadeTo(300, 1);
714      };
715      var left = thumb[0].parentNode.offsetLeft;
716      left -= (this.nav_display_width / 2) - (thumb[0].offsetWidth / 2);
717      this.thumbs_wrapper.animate({scrollLeft: left +'px'});
718    },
719    fireCallback: function(fn) {
720      if($.isFunction(fn)) {
721        fn.call(this);
722      };
723    }
724  };
725
726  function AdGallerySlideshow(nextimage_callback, settings) {
727    this.init(nextimage_callback, settings);
728  };
729  AdGallerySlideshow.prototype = {
730    start_link: false,
731    stop_link: false,
732    countdown: false,
733    controls: false,
734
735    settings: false,
736    nextimage_callback: false,
737    enabled: false,
738    running: false,
739    countdown_interval: false,
740    init: function(nextimage_callback, settings) {
741      var context = this;
742      this.nextimage_callback = nextimage_callback;
743      this.settings = settings;
744    },
745    create: function() {
746      this.start_link = $('<span class="ad-slideshow-start">'+ this.settings.start_label +'</span>');
747      this.stop_link = $('<span class="ad-slideshow-stop">'+ this.settings.stop_label +'</span>');
748      this.countdown = $('<span class="ad-slideshow-countdown"></span>');
749      this.controls = $('<div class="ad-slideshow-controls"></div>');
750      this.controls.append(this.start_link).append(this.stop_link).append(this.countdown);
751      this.countdown.hide();
752
753      var context = this;
754      this.start_link.click(
755        function() {
756          context.start();
757        }
758      );
759      this.stop_link.click(
760        function() {
761          context.stop();
762        }
763      );
764      $(document).keydown(
765        function(e) {
766          if(e.keyCode == 83) {
767            // 's'
768            if(context.running) {
769              context.stop();
770            } else {
771              context.start();
772            };
773          };
774        }
775      );
776      return this.controls;
777    },
778    disable: function() {
779      this.enabled = false;
780      this.stop();
781      this.controls.hide();
782    },
783    enable: function() {
784      this.enabled = true;
785      this.controls.show();
786    },
787    toggle: function() {
788      if(this.enabled) {
789        this.disable();
790      } else {
791        this.enable();
792      };
793    },
794    start: function() {
795      if(this.running || !this.enabled) return false;
796      var context = this;
797      this.running = true;
798      this.controls.addClass('ad-slideshow-running');
799      this._next();
800      this.fireCallback(this.settings.onStart);
801      return true;
802    },
803    stop: function() {
804      if(!this.running) return false;
805      this.running = false;
806      this.countdown.hide();
807      this.controls.removeClass('ad-slideshow-running');
808      clearInterval(this.countdown_interval);
809      this.fireCallback(this.settings.onStop);
810      return true;
811    },
812    _next: function() {
813      var context = this;
814      var pre = this.settings.countdown_prefix;
815      var su = this.settings.countdown_sufix;
816      clearInterval(context.countdown_interval);
817      this.countdown.show().html(pre + (this.settings.speed / 1000) + su);
818      var slide_timer = 0;
819      this.countdown_interval = setInterval(
820        function() {
821          slide_timer += 1000;
822          if(slide_timer >= context.settings.speed) {
823            var whenNextIsShown = function() {
824              // A check so the user hasn't stoped the slideshow during the
825              // animation
826              if(context.running) {
827                context._next();
828              };
829              slide_timer = 0;
830            };
831            if(!context.nextimage_callback(whenNextIsShown)) {
832              context.stop();
833            };
834            slide_timer = 0;
835          };
836          var sec = parseInt(context.countdown.text().replace(/[^0-9]/g, ''), 10);
837          sec--;
838          if(sec > 0) {
839            context.countdown.html(pre + sec + su);
840          };
841        },
842        1000
843      );
844    },
845    fireCallback: function(fn) {
846      if($.isFunction(fn)) {
847        fn.call(this);
848      };
849    }
850  };
851})(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.