function Fader( parent, loader, options )
{
  var options; 
  
  this.parent           = parent;
  this.loader           = loader;

  options               = options || {};
  this.context          = options.context || document;
  this.timer            = options.timer || new Timer();
  this.width            = options.width || 800;
  this.height           = options.height || 600;
  this.loaderImageUrl   = options.loaderImageUrl || "loader.gif";
  this.duration         = options.duration || 1000;
  this.fadeSpeed        = options.fadeSpeed || 1000;
  this.deferredLoaders  = options.deferredLoaders || [];

  this.pages                  = [];
  this.lastShowTime           = 0;
  this.currentPageIndex       = null;
  
  this.initializeFrame();
  this.initializeLoader();
  this.initializeContent();
  this.startLoader();
  this.startDeferredLoaders();
}

Fader.prototype =
{
  initializeFrame: function()
  {
    this.frameDiv = this.context.createElement( "div" );
    this.frameDiv.className = "fader_frame";
    this.frameDiv.style.width = this.width + "px";
    this.frameDiv.style.height = this.height + "px";
    this.parent.appendChild( this.frameDiv );
  },

  initializeLoader: function()
  {
    var image;

    this.loaderDiv = this.context.createElement( "div" );
    this.loaderDiv.className = "fader_loader";
    this.loaderDiv.style.position = "absolute";

    image = this.context.createElement( "img" );
    image.src = this.loaderImageUrl;

    this.loaderDiv.appendChild( image );
    this.frameDiv.appendChild( this.loaderDiv );
  },
  
  initializeContent: function()
  {
    this.contentDiv = this.context.createElement( "div" );
    this.contentDiv.className = "fader_content";
    this.frameDiv.appendChild( this.contentDiv );
    $( this.contentDiv ).hide();    
  },

  startLoader: function()
  {
    this.loader.loaded.attach( this.loaderLoaded.bind( this ) );
    this.loader.load();
  },
  
  startDeferredLoaders: function()
  {
    var i, loader;
    
    for ( i = 0; i < this.deferredLoaders.length; ++i )
    {
      loader = this.deferredLoaders[ i ];
      loader.loaded.attach( this.deferredLoaderLoaded.bind( this ) );
      loader.load();
    }
  },
  
  loaderLoaded: function()
  {
    this.createPagesFromLoader( loader );
    
    this.showFirstPage();
    this.hideLoader();

    this.timer.tick.attach( this.timerTick.bind( this ) );
    this.timer.start();
  },
  
  deferredLoaderLoaded: function( loader )
  {
    this.createPagesFromLoader( loader );
  },
  
  createPagesFromLoader: function( loader )
  {
    var page;
    var elements = loader.getElements();
    
    for ( i = 0; i < elements.length; ++i )
    {
      page = this.createPage( elements[ i ] );
      this.pages.push( page );
      this.contentDiv.appendChild( page )
      $( page ).hide();
    }
  },
  
  createPage: function( element )
  {
    var pageDiv = this.context.createElement( "div" );
    pageDiv.className = "fader_page";
    pageDiv.style.position = "absolute";
    pageDiv.appendChild( element );
    return pageDiv;
  },
  
  hideLoader: function()
  {
    $( this.loaderDiv ).hide(); 
  },
  
  showFirstPage: function()
  {
    if ( this.pages.length > 0 )
    {
      $( this.contentDiv ).show();
      this.showPage( 0, false );
    }
  },
  
  showNextPage: function()
  {
    if ( this.pages.length > 0 )
    {
      var j = ( this.currentPageIndex + 1 ) % this.pages.length;
      this.showPage( j, true );
    }
  },
  
  showPage: function( pageIndex, fade )
  {
    var currentPage;
    var nextPage;

    this.lastShowTime = new Date().getTime();
    
    if ( this.currentPageIndex !== null )
    {
      currentPage = this.pages[ this.currentPageIndex ];
      
      if ( fade ) $( currentPage ).fadeOut( this.fadeSpeed )
      else $( currentPage ).hide();
    }
    
    nextPage = this.pages[ pageIndex ];
    
    if ( fade ) $( nextPage ).fadeIn( this.fadeSpeed );
    else $( nextPage ).show();
    
    this.currentPageIndex = pageIndex;
  },
  
  timerTick: function()
  {
    this.update();
  },

  update: function()
  {
    if ( new Date().getTime() - this.lastShowTime > this.duration )
    {
      this.showNextPage();  
    }
  }
};
