PageSourceSearch

https://ajdovscina.si/js/jquery.ad-gallery.js?v2

js ajdovscina.si collected 2026-10-02 04:43:08 UTC 29,691 bytes, 861 lines download raw bytes

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