(function($){  
  $.fn.gallery = function(options) {  
 
    var defaults = {
      displayDuration:    3000,
      initialState:       'play',
      showControls:       2,
      showThumbnails:     2,
      transitionType:     1,
      transitionDuration: 1000,
      autoStart:          true
    };
    var options = $.extend(defaults, options);

    return this.each(function() {
      var firstImage = $(this).find('img:first');
      var h          = firstImage.height();
      var w          = firstImage.width();

      galleryTransition($(this), options, firstImage.height(), firstImage.width());
    });  

    //Petit bricolage pour que ça fonctionne aussi dans IE7
    function absIE7(src) {
      var a = $('<a href="' + src + '" />');
      return a.attr('href');
    }
    

    function galleryTransition(gallery, options, h, w) {
      var transitionType = options.transitionType;
      var t;
      //Création d'un objet Transition du bon type en fonction des options
      if (transitionType == 1)
        t = new TransitionUpDown(gallery, options, h, w);
      else if (transitionType == 2)
        t = new TransitionDownUp(gallery, options, h, w);
      else if (transitionType == 3)
        t = new TransitionFade(gallery, options, h, w);
      else if (transitionType == 4)
        t = new TransitionLeftRight(gallery, options, h, w);
      else if (transitionType == 5)
        t = new TransitionRightLeft(gallery, options, h, w);
      else if (transitionType == 6)
        t = new TransitionCrush(gallery, options, h, w);
      else if (transitionType == 7)
        t = new TransitionZoom(gallery, options, h, w);
      else
        t = new Transition(gallery, options, h, w);
    }

    function Transition(gallery, options, h, w) {
      this.gallery = gallery;
      this.displayDuration = options.displayDuration;
      this.transitionDuration = options.transitionDuration;
      this.state = options.initialState;
      this.timer = 0;

      this.currentSrc = ''; //src de l'image affichée pour le moment
      this.showControls = options.showControls;
      this.showThumbnails = options.showThumbnails;
      
      /*
       * Ajoute la barre de miniatures.
       * Ajoute le DIV avec les miniatures dedans, et met, en fonction du paramètre
       * display, les évènements corrects pour afficher ou non la barre de
       * miniatures au survol de la galerie par la souris.
       */
      this.addThumbnails = function(display) {
        var width  = this.gallery.width();
        var height = this.gallery.height();
        var thumbHeight = 60;
        var thumbWidth  = Math.round(thumbHeight/height * width)
        var thumbsHeight = thumbHeight;
        var thumbsWidth; //Calculé plus bas
        var thumbsMargin = 10;

        var that = this;

        //Ajout d'un DIV pour les miniatures
        this.gallery.append('<div class="gallery-thumbnails"><div class="gallery-thumbnails-mark" /></div>');
        var thumbsDiv = this.gallery.find('.gallery-thumbnails');

        //Ajout de miniatures dans ce DIV,
        //Redimensionnement du DIV et
        //Centrage du DIV dans la galerie.
        thumbsWidth = 0;
        this.gallery.find('.gallery-in img').each(function(){
          var src = $(this).attr('src');
          var thumb = '<img src="' + src + '" height="' + thumbHeight + '" width="' + thumbWidth + '"/>';
          thumbsDiv.append(thumb);
          thumbsWidth++;
        });
        thumbsWidth *= thumbWidth;
        thumbsDiv.css('width', thumbsWidth+'px');
        var left = Math.round(width/2 - thumbsWidth/2);
        thumbsDiv.css('left', left+'px');

        //Ajout d'un évènement pour afficher les miniatures au survol
        if (display == 2) {
          this.gallery.hoverDelay(function(){
            thumbsDiv.animate({
              bottom: thumbsMargin + 'px'
            }, 'slow');
          }, function() {
            thumbsDiv.animate({
              bottom: '-' + (thumbsHeight + thumbsMargin) + 'px'
            }, 'slow');
          }, {inDelay: 250, outDelay: 250});
          //Ajout d'un évènement pour aligner les miniatures
          if ((width + 2*thumbsMargin) < thumbsWidth) {
            this.gallery.mousemove(function(e) {
              var mouseX = Math.round(e.clientX - $(this).offset().left);
              var delta = (mouseX - width / 2) / width;
              var left = thumbsMargin + Math.round((width - (thumbsWidth + 2*thumbsMargin))/2 - delta*Math.abs((thumbsWidth + 2*thumbsMargin) - width));
              thumbsDiv.css('left', left+'px');
            });
          }
        } else if (display == 1) {
          if ((width + 2*thumbsMargin) < thumbsWidth) {
            thumbsDiv.css('left', thumbsMargin);
            thumbsDiv.css('width', width - 2*thumbsMargin);
            var nbThumbs = thumbsDiv.find('img').size();
            thumbWidth = (width - 2*thumbsMargin)/nbThumbs;
            thumbsDiv.find('img').css('width', thumbWidth);
            thumbHeight = (height/width)*thumbWidth;
            thumbsDiv.find('img').css('height', thumbHeight);
            thumbsDiv.css('height', thumbHeight);
          }
          thumbsDiv.animate({
            bottom: thumbsMargin + 'px'
          }, 'slow');
        }

        this.gallery.find('.gallery-thumbnails img').click(function() {
          var src = $(this).attr('src');
          that.goToImage(src);
        });
      }
      
      /*
       * Ajoute la barre de contrôle.
       * Ajoute le DIV avec les contrôles dedans, et met, en fonction du paramètre
       * display, les évènements corrects pour afficher ou non la barre de
       * contrôle au survol de la galerie par la souris.
       */
      this.addControls = function(display) {
        var controlsHeight = 20;
        var controlsMargin = 10;

        var that = this;

        //Ajout d'un DIV pour les contrôles
        this.gallery.append('<div class="gallery-controls">'
                          + '<span class="gallery-prev"></span>'
                          + '<span class="gallery-play-pause"></span>'
                          + '<span class="gallery-next"></span>'
                          + '</div>');
        var controlsDiv = this.gallery.find('.gallery-controls');

        //Ajout d'un évènement pour afficher les contrôles au survol
        if (display == 2) {
          this.gallery.hoverDelay(function(){
            controlsDiv.animate({
              top: controlsMargin + 'px'
            }, 'slow');
          }, function() {
            controlsDiv.animate({
              top: '-' + (controlsHeight + controlsMargin) + 'px'
            }, 'slow');
          }, {inDelay: 250, outDelay: 250});
        } else if (display == 1) {
          controlsDiv.animate({
            top: controlsMargin + 'px'
          }, 'slow');
        }

        //Évènements au clic sur les boutons
        this.gallery.find('.gallery-play-pause').click(function() {
          if (that.state == 'play') {
            that.pause();
          } else if (that.state == 'pause') {
            that.play();
          }
        });
        this.gallery.find('.gallery-prev').click(function() {
          var oldState = that.state;
          that.pause();
          that.prev();
          if (oldState == 'play')
            that.play();
        });
        this.gallery.find('.gallery-next').click(function() {
          var oldState = that.state;
          that.pause();
          that.next();
          if (oldState == 'play')
            that.play();
        });
      }
        
      /*
       * Démarrage du défilement automatique des images
       */
      this.play = function(){
        this.state = 'play';
        this.gallery.find('.gallery-play-pause').css('background-position', '-20px 0px');
        var that = this;
        this.timer = setInterval(function(){
          that.next();
        }, that.displayDuration);
      };
      
      /*
       * Arrêt du défilement automatique des images
       */
      this.pause = function(){
        this.state = 'pause';
        this.gallery.find('.gallery-play-pause').css('background-position', '0px 0px');
        clearInterval(this.timer);
      };
      
      /*
       * Va directement à une image déterminée par sa source
       */
      this.goToImage = function(src){
        var oldState = this.state;
        if (src != this.currentSrc) {
          this.pause();
          var galleryIn = this.gallery.find('.gallery-in');
          var first = galleryIn.find('img:first');
          var newFirst = first.clone();
          newFirst.css('width', first.width());
          newFirst.css('height', first.height());
          newFirst.css('position', 'absolute');
          newFirst.css('display', 'inline-block');
          newFirst.css('opacity', '1');
          newFirst.appendTo(galleryIn);
          var nbImages = galleryIn.find('img').size();
          var i=0;
          while((absIE7(first.attr('src')) != src) && i < nbImages) {
            var oldFirst = first;
            first = oldFirst.next();
            oldFirst.appendTo(galleryIn);
            oldFirst.css('display', 'none');
            i++;
          }
          if (i != nbImages) { //Peut-être que src ne fait pas partie des images
            this.change(newFirst, first, true);
            this.currentSrc = src;
            this.highLightThumb();
          } else {
            newFirst.remove();
          }
          if (oldState == 'play')
            this.play();
        }
      };
      
      this.next = function() {
        var first  = this.gallery.find('.gallery-in img:first');
        var second = this.gallery.find('.gallery-in img:first + img');
        this.change(first, second, false);
        this.currentSrc = second.attr('src');
        this.highLightThumb();
        first.appendTo(this.gallery.find('.gallery-in'));
      }
      
      this.prev = function() {options.autoStart
        var first  = this.gallery.find('.gallery-in img:first');
        var last = this.gallery.find('.gallery-in img:last');
        this.change(first, last, false);
        this.currentSrc = last.attr('src');
        this.highLightThumb();
        last.prependTo(this.gallery.find('.gallery-in'));
      }
      
      /*
       * Passe de la première image à la deuxième avec une transition.
       * C'est la fonction à surcharger dans les sous-classes.
       * On ne devrait normalement pas à avoir à passer les paramètres first et
       * second, mais on les utilise normalement déjà dans la fonction appelante
       * avant et après donc c'est plsu performant de faire comme ça.
       */
      this.change = function(first, second, remove) {
        //Transition par défaut: pas de transition
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('display', 'inline-block');
        second.css('display', 'inline-block');
        if (remove)
          first.remove();
      }

      /*
       * Met en valeur la miniature de l'image actuellement affichée
       */  
      this.highLightThumb = function() {
        if (this.showThumbnails) {
          var left = 0;
          var currentSrc = absIE7(this.currentSrc);
          this.gallery.find('.gallery-thumbnails img').each(function() {
            var src = $(this).attr('src');
            if (src == currentSrc) {
              $(this).addClass("active-thumb");
              left = $(this).position().left + $(this).width()/2 - 5;
            } else {
              $(this).removeClass("active-thumb");
            }
          });
          this.gallery.find('.gallery-thumbnails-mark').animate({
            left: left
          }, Number(this.transitionDuration));
        }
      }

      /*
       * Constructeur.
       * Placé à la fin pour que toutes les variables et méthodes aient bien été
       * définies avant.
       */  
      this.construct = function() {
        var that = this;
        
        if (this.showControls != '0')
          this.addControls(this.showControls);

        if (this.showThumbnails != '0')
          this.addThumbnails(this.showThumbnails);

        var firstImage = this.gallery.find('.gallery-in img:first');
        firstImage.css('display', 'inline-block');
        this.currentSrc = firstImage.attr('src');
        this.highLightThumb();
        if (this.state == 'play')
          this.play();
        else
          this.pause();
      };
      this.construct();
    }

    function TransitionFade(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        first.animate({
          opacity: 0
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          first.css('opacity', '1');
          if (remove)
            first.remove();
        });
      }
    }

    function TransitionUpDown(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        first.css('top', '0');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('top', '-' + h + 'px');
        first.animate({
          top: h
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          if (remove)
            first.remove();
        });
        second.animate({
          top: 0
        }, Number(this.transitionDuration));
      }
    }

    function TransitionDownUp(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        first.css('top', '0');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('top', h + 'px');
        first.animate({
          top: '-' + h
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          if (remove)
            first.remove();
        });
        second.animate({
          top: 0
        }, Number(this.transitionDuration));
      }
    }

    function TransitionLeftRight(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        first.css('left', '0');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('left', '-' + w + 'px');
        first.animate({
          left: w
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          if (remove)
            first.remove();
        });
        second.animate({
          left: 0
        }, Number(this.transitionDuration));
      }
    }

    function TransitionRightLeft(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        first.css('left', '0');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('left', w + 'px');
        first.animate({
          left: '-' + w
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          if (remove)
            first.remove();
        });
        second.animate({
          left: 0
        }, Number(this.transitionDuration));
      }
    }

    function TransitionCrush(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        first.css('top', 0);
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('top', 0);
        second.css('height', 0);
        first.animate({
          height: 0,
          top: h + 'px'
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          first.css('height', h + 'px');
          if (remove)
            first.remove();
        });
        second.animate({
          height: h + 'px'
        }, Number(this.transitionDuration));
      }
    }

    function TransitionZoom(gallery, options, h, w) {
      this.inheritFrom = Transition;
      this.inheritFrom(gallery, options, h, w);

      this.change = function(first, second, remove) {
        this.gallery.find('.gallery-in img').css('display', 'none');
        first.css('position', 'absolute');
        first.css('display', 'inline-block');
        second.css('position', 'absolute');
        second.css('display', 'inline-block');
        second.css('left', '-' + w/2 + 'px');
        second.css('top', '-' + h/2 + 'px');
        second.css('width', w*2 + 'px');
        second.css('height', h*2 + 'px');
        first.animate({
          opacity: 0,
          width: 2*w,
          height: 2*h,
          left: '-' + w/2 + 'px',
          top: '-' + h/2 + 'px'
        }, Number(this.transitionDuration), function(){
          first.css('display', 'none');
          first.css('opacity', '1');
          if (remove)
            first.remove();
        });
        second.animate({
          height: h + 'px',
          width: w + 'px',
          top: 0,
          left: 0
        }, Number(this.transitionDuration));
      }
    }    

  };  
})(jQuery); 


