// -----------------------------------------------------------------------------------
//
//	Commuun Photogallery v0.1
//	by Peter Duijnstee - http://www.commuun.nl
//
// Note this script is dependent on jquery
//
// The available options are documented at the top of the class definition.
// Options should be passed as a "hash" to the constructor.
//
// Example:
//
// $(document).ready( function() {
//   g = new Gallery({ title_label: 'My Gallery', image_transition_delay: 2500, animation_delay: 250, autoplay: false });
//   g.addImage('images/image1.jpg', 'images/thumbs/image1.jpg', 'First image', 'http://www.google.com');
//   g.addImage('images/image2.jpg', 'images/thumbs/image2.jpg', 'Second image');
//   g.addImage('images/image3.jpg', 'images/thumbs/image3.jpg', 'Third image');
//   g.init().prependTo( $('#container') );
// } );
//
//
// -----------------------------------------------------------------------------------

// Simple math extension
function sineInOut(t, b, e, d) {
  c = e - b;
  return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}
function cubicIn(t, b, e, d) {
  c = e - b;
  return c*(t/=d)*t*t + b;
}

function cubicOut(t, b, e, d) {
  c = e - b;
  return c*((t=t/d-1)*t*t + 1) + b;
} 

// Helper function to load the gallery
function load_gallery( basefile, basethumb, count ) {
  if( typeof(x) != 'undefined' ) {
    g.close();
    $('#my_gallery').remove();
  }
  g = new Gallery({ title_label: 'My Gallery', id: 'my_gallery', image_transition_delay: 1000, animation_delay: 250, autoplay: false });
  for( i = 1; i <= count; i++ ) {
    no = String( i < 10 ? '0' + i : i );
    g.addImage(basefile+no+'.jpg', basethumb+no+'.jpg');
  }
  g.init().prependTo( $('#beeld') );
}

// The actual gallery class
function Gallery(options) {
  this.options = {
    // Default DOM id for the gallery itself
    gallery_id: 'my_gallery',
  
    // Default title and optional text for the close button
    title_label: 'My Gallery',
    close_label: '',
  
    // Position of the title bar (top or bottom)
    title_position: 'top',
    
    // Whether or not to display the thumbs.
    // Possible values are 'yes', 'no' and 'auto' to display them on mouseover.
    hide_thumbs: 'no',
    // Position of the thumb bar (top or bottom indicates before or after viewport)
    thumbs_position: 'bottom',
    // Duration in # of frames of the scroll from one thumb "page" to the next
    thumbs_scroll_duration: 30,
    // Number of MS between frames
    thumbs_scroll_speed: 40,
    
    // What elements to display
    show_title: false,
    show_slideshow_controls: false,
    show_close_button: false,
    show_image_buttons: true,
    show_page_buttons: true,

    // Images per "page"
    per_page: 5,
    
    // Whether we want to allow an animated slideshow
    slideshow: true,
    // The delay in miliseconds between slides.
    // Making this faster (lower) than the animation speed may have undesirable effects.
    slideshow_delay: 500,
    // Whether the slideshow starts paused or playing
    autoplay: false,

    // Speed of the animation in miliseconds. Lower is faster.
    // N.B.: Applies to all transitions, other than the slideshow.
    animation_delay: 200,
    // Delay between slides, also applies when then "next image" button is clicked
    image_transition_delay: 500
  };

  //
  // Initialization functions
  //

  // Merge the given options with the defaults
  jQuery.extend( this.options, options );

  // Initialize the list of images
  this.image_list = new Array;

  // Setup the default configuration
  this.init = function(container) {
    this.current_image = 0;

    // Create the container div for the gallery
    this.gallery = $('<div id='+this.options.gallery_id+'>').prependTo($(container));

    // Add the close button
    if( this.options.show_close_button ) {
      this.close_button = $('<div class="close">').append(this.options.close_label).appendTo(this.gallery);
      // Set the mouse cursor to pointer on hover
      this.close_button.css("cursor","pointer");
      // Make sure the close button closes the dialog on click
      this.close_button.bind('click', {myself: this}, function(e){
        e.data.myself.close();
      });
    }

    this.initTitle();

    this.initViewport();
    
    this.initImageNavigation();

    this.current_page = 0;

    this.initThumbs();

    // Initialize the first image
    this.showImage( 0 );

    // Start the slideshow, if requested
    this.initSlideshow();

    return this.gallery;
  };
  
  this.initTitle = function() {
    this.title = $('<div class="title">').append(this.options.title_label);
    if( this.options.title_position == 'top' )
      this.title.prependTo(this.gallery);
    else
      this.title.appendTo(this.gallery);
    if( !this.options.show_title )
      this.title.hide();
  };

  this.initViewport = function() {
    // Add the main image div
    this.viewport = $('<div class="viewport" style="position: relative;">')
                      .append('<span></span>') // This is part of a css hack to allow vertical centering of images
                      .append('<img>') // Add an empty image to to initialize
                      .appendTo(this.gallery);
  };

  this.initImageNavigation = function() {
    // Next and previous image buttons
    if( this.options.show_image_buttons ) {
      this.next_image = $('<div class="next"></div>').appendTo(this.gallery);
      this.previous_image = $('<div class="previous"></div>').appendTo(this.gallery);
      this.next_image.bind( 'click', { myself: this }, function() { myself.nextImage(); } );
      this.next_image.css( 'cursor', 'pointer' );
      this.next_image.hide();
      this.previous_image.bind( 'click', { myself: this }, function() { myself.previousImage(); } );
      this.previous_image.css( 'cursor', 'pointer' );
      this.previous_image.hide();
    }
  };

  this.initSlideshow = function() {
    // Slideshow settings
    if( this.options.slideshow ) {
      // Add the play and pause buttons
      myself = this;
      this.slideshow_playing = false;
      if( this.options.show_slideshow_controls ) {
        this.play_button = $('<div class="play">').appendTo(this.gallery);
        this.play_button.bind( 'click', function() { myself.startSlideshow() } );
        this.play_button.css( 'cursor', 'pointer' );
        this.pause_button = $('<div class="pause">').appendTo(this.gallery);
        this.pause_button.bind( 'click', function() { myself.stopSlideshow() } );
        this.pause_button.css( 'cursor', 'pointer' );
      }
      if( this.options.autoplay ) {
        myself = this
        setTimeout( 'myself.startSlideshow()', this.options.slideshow_delay);
        if( this.options.show_slideshow_controls )
          this.play_button.hide();
      } else {
        if( this.options.show_slideshow_controls )
          this.pause_button.hide();
      }
    }
  };

  this.initThumbs = function() {
    if(this.options.hide_thumbs == 'yes')
      return false;

    // Add the thumbnail container div
    this.thumbnail_row = $('<div class="thumbnail_row">');
    this.thumbnail_container = $('<div class="thumbnail_container">').appendTo( this.thumbnail_row );
    this.thumbnails = $('<ul class="thumbnails">').appendTo( this.thumbnail_container );

    if( this.options.thumbs_position == 'top' )
      this.thumbnail_row.insertBefore(this.viewport);
    else
      this.thumbnail_row.insertAfter(this.viewport);

    // Set auto-hide for thumbnail list if requested
    myself = this;
    if( this.options.hide_thumbs == 'auto' ) {
      this.thumbnail_container.hide();
      this.gallery.hover(
        function() {
          myself.thumbnail_container.fadeIn( myself.options.animation_delay );
        },
        function() {
          myself.thumbnail_container.fadeOut( myself.options.animation_delay );
        }
      );
    };

    if( this.options.show_page_buttons ) {
      this.prev_page_button = $('<div class="page_button">').prependTo(this.thumbnail_row);
      this.next_page_button = $('<div class="page_button">').appendTo(this.thumbnail_row);
    }

    // Clear out the floating divs.
    this.thumbnail_row.after('<div style="clear:both;">');

    // Add the actual thumbnails
    myself = this;
    jQuery.each( this.image_list, function( index, image ) {
      thumb = $('<li class="thumbnail">');
      thumb.css( 'cursor', 'pointer' );
      table = $('<table width=100% height=100% cellpadding=0 cellspacing=0 border=0><tr><td align=center valign=center><img class="loading" src="/images/spinner.gif">');

      $('<img>')
        .hide()
        .load( function() {
          $(this).parent().find('img.loading').remove();
          $(this).show();
        } )
        .error( function() {
          $(this).attr('src','/images/error.gif');
        } )
        .appendTo( table.find('td') )
        .attr('src',image.thumb);

      thumb.append(table).appendTo(myself.thumbnails);
      thumb.bind( 'click', { s: myself, i: index }, function(e) {
        e.data.s.showImage( e.data.i );
      } );
    } );

    $('<li class="separator">').insertAfter( this.thumbnails.children('li.thumbnail') );

    this.thumbnails.css( 'width', this.pageWidth() * this.totalPages() );
    
    // At startup we're not scrolling anything.
    this.scrolling_page = false;

    this.showPageButtons();
  };
  
  this.addImage = function( image, thumb, title, url ) {
    this.image_list.push({ path: image, thumb: thumb, title: title, url: url });
    // Preload the image
    $('<img>').attr('src',image);
  };

  this.close = function() {
    myself = this;
    this.gallery.fadeOut( this.options.animation_delay * 2, function() {
      myself.gallery.remove();
    } );
  };

  //
  // Image loading functions
  //
  this.showImage = function( index ) {
    // Don't move to the next image if we're still loading, if we're
    // already at this image or if the index doesn't exist.
    if( this.loading || index >= this.image_list.length )
      return false;

    // Set the current_image to the selected index
    this.current_image = parseInt(index);

    // Set the loading boolean to indicate that we've started fading in the image
    this.loading = true;
    
    // Check if we're on the right page for this image.
    if( this.options.hide_thumbs != 'yes' ) { 
      page = parseInt( this.current_image / (this.options.per_page) );
      if( page != this.current_page ) {
        this.current_page = page;
        this.moveToPage();
      }
    }
    

    // We replace the entire viewport because just replacing the image results in
    // undesirable centering effects in IE6
    new_viewport = this.viewport.clone();
    // Start with an empty "loading" gif which will be replaced when the real image loads
    new_image = $(new_viewport).find('img')
                  .error( function() {
                    $(this).attr('src','/images/error.gif');
                  } )
                  .attr('src', this.image_list[this.current_image].path);

    // Check if we need to add a link to the image
    if( url = this.image_list[this.current_image].url ) {
      new_image.css( 'cursor', 'pointer' );
      new_image.attr( 'title', url );
      new_image.bind( 'click', function() { window.open( url ) } );
    }
    
    // Check if we need to update the title
    if( title = this.image_list[this.current_image].title ) {
      if( this.options.title_label )
        this.title.html(this.options.title_label + ': ' + title);
      else
        this.title.html(title);
    } else {
      this.title.html(this.options.title_label);
    }

    // Mark the selected image's thumbnail
    if( this.options.hide_thumbs != 'yes' ) {
      this.thumbnails.find('li.thumbnail.selected').removeClass('selected');
      this.thumbnails.find('li.thumbnail:eq('+ this.current_image +')').addClass('selected');
    }

    // Update the next/prev image buttons
    this.showImageButtons();

    // Finally, actually fade the image into view.
    new_viewport
      .hide()
      .css('position','absolute')
      .css('left', this.viewport.position().left )
      .css('top', this.viewport.position().top )
      .insertAfter( this.viewport )
      .fadeIn( this.options.image_transition_delay );
    // And fade out and remove the old image
    myself = this;
    this.viewport.fadeOut( this.options.image_transition_delay, function() {
      myself.viewport.remove();
      myself.viewport = new_viewport;
      myself.viewport
        .css('position','')
        .css('left', '' )
        .css('top', '' )
      // Indicate that we're done fading in when we finish
      myself.loading = false;
    } );
  };

  this.showImageButtons = function() {
    // See if we need to display the next/previous buttons
    if( this.options.show_image_buttons ) {
      if( this.current_image + 1 >= this.image_list.length  )
        this.next_image.fadeOut( this.options.animation_delay );
      else
        this.next_image.fadeIn( this.options.animation_delay );
      if( this.current_image > 0 )
        this.previous_image.fadeIn( this.options.animation_delay );
      else
        this.previous_image.fadeOut( this.options.animation_delay );
    } else {
      this.next_image.fadeOut( this.options.animation_delay );
      this.previous_image.fadeOut( this.options.animation_delay );
    };
  };

  //
  // Image navigation functions
  //
  this.nextImage = function() {
    next = (this.current_image + 1) % this.image_list.length;
    this.showImage( next );
  }
  this.previousImage = function() {
    prev = (this.current_image - 1);
    if( prev < 0 )
      prev = this.image_list.length;
    this.showImage( prev );
  }

  //
  // Page navigation functions
  //
  this.nextPage = function() {
    if( this.scrolling_page )
      return false;

    if( this.image_list.length > (this.current_page+1) * this.options.per_page ) {
      this.current_page++;
      this.moveToPage( this.current_page );
    }
  };
  
  this.previousPage = function() {
    if( this.scrolling_page )
      return false;

    if( this.current_page > 0 ) {
      this.current_page--;
      this.moveToPage( this.current_page );
    }
  };

  this.moveToPage = function() {
    this.scroll_source = this.thumbnail_container.scrollLeft();
    this.scroll_target = this.pageWidth() * this.current_page;
    this.scroll_timer = 0;
    this.hidePageButtons();
    this.scrolling_page = true;
    this.updatePagePosition();
  };

  this.updatePagePosition = function() {
    this.scroll_timer++;
    if( this.scroll_timer <= this.options.thumbs_scroll_duration ) {
      this.thumbnail_container.scrollLeft( cubicOut(this.scroll_timer, this.scroll_source, this.scroll_target, this.options.thumbs_scroll_duration) );
      myself = this;
      setTimeout( 'myself.updatePagePosition()', this.options.thumbs_scroll_speed );
    } else {
      this.scrolling_page = false;
      this.showPageButtons();
    }
  };

  this.hidePageButtons = function() {
    if( this.options.show_page_buttons ) {
      this.next_page_button.removeClass( 'next_page' );
      this.next_page_button.css( 'cursor', '' );
      this.next_page_button.unbind( 'click' );
      this.prev_page_button.removeClass( 'previous_page' );
      this.prev_page_button.css( 'cursor', '' );
      this.prev_page_button.unbind( 'click' );
    }
  }
  this.showPageButtons = function() {
    // Show the previous/next page buttons (or not)
    if( this.options.show_page_buttons ) {
      this.hidePageButtons();
      if( this.current_page + 1 < this.totalPages() ) {
        this.next_page_button.addClass( 'next_page' );
        this.next_page_button.css( 'cursor', 'pointer' );
        myself = this;
        this.next_page_button.bind( 'click', function() { myself.nextPage(); } );
      }
      if( this.current_page > 0 ) {
        this.prev_page_button.addClass( 'previous_page' );
        this.prev_page_button.css( 'cursor', 'pointer' );
        myself = this;
        this.prev_page_button.bind( 'click', function() { myself.previousPage(); } );
      }
    }
  }

  //
  // Slideshow functions
  //
  this.startSlideshow = function() {
    if( !this.options.slideshow || this.slideshow_playing )
      return false;

    if( this.options.show_slideshow_controls ) {
      this.play_button.fadeOut(this.options.animation_delay);
      this.pause_button.fadeIn(this.options.animation_delay);
    }

    this.slideshow_playing = true;

    this.slideshow_loop(this);
  };

  this.stopSlideshow = function() {
    if( !this.options.slideshow )
      return false;

    if( this.options.show_slideshow_controls ) {
      this.play_button.fadeIn(this.options.animation_delay);
      this.pause_button.fadeOut(this.options.animation_delay);
    }

    this.slideshow_playing = false;
  };

  this.slideshow_loop = function(myself) {
    if( myself.slideshow_playing ) {
      myself.nextImage();
      setTimeout( 'myself.slideshow_loop(myself)', myself.options.slideshow_delay );
    }
  };

  //
  // Queries
  //

  // The number of pages for the current image collection
  this.totalPages = function() {
    return Math.ceil( this.image_list.length / this.options.per_page );
  };

  // The width of one thumbnail plus one separator
  this.thumbWidth = function() {
    t = this.thumbnails.find('.thumbnail:first');
    s = this.thumbnails.find('.separator:first');
    w = t.width();
    w+= s.width();
    return 94;
  };

  // The total width of one "page" of thumbnails
  this.pageWidth = function() {
    return( this.thumbWidth() * this.options.per_page );
  }

};
