/**
 * Image fader
 *
 *
 * The script is freely distributable under the terms of an MIT license.
 *
 *
 * @author Hristo Drumev
 * @license MIT license
 * @copyright Hristo Drumev [www.hdrumev.com]
 * @version 0.03 / 22.07.2011
 *
 *
 * >>> Does not require any libraries or frameworks
 *
 * Usage : 1. Include this file in your (x)HTML page
 *         2. Setup configuration
 *
 */


;( function() {

// Configuration
var options =
{
  container   : 'slider',        // Id of the container
  navigator   : 'itemselector',  // Id of slider navigator
  prevArrow   : 'prevarrow',     // Id of arrow for previous slide
  nextArrow   : 'nextarrow',     // Id of arrow for next slide 
  hideArrows  : true,            // To hide arrows when mouse is not over slider
  slideClass  : 'slide',         // The className of each slide
  fadeTimeout : 500,             // Timeout for fading of images 
  holdTimeout : 5000,            // Timeout for showing of images
  randomSlide : false            // Show random image every time
};

// ----------------------------------------------------------------------------

/**
 * DOM ready event listener
 *
 * @param {Function} fn - listener
 */
var domReady = function( fn )
{
  document.addEventListener
  ? document.addEventListener( 'DOMContentLoaded', fn, false )
  : ( function() { document.body ? fn() : setTimeout( arguments.callee, 0 ); } )();
}

/**
 * Sets opacity of the element
 *
 * @param {HTMLElement} element - HTML element
 * @param {Number} value - Value of the opacity (0-1)
 */
var setOpacity = ( function() {
  var reOpacity = /alpha\([^)]*\)/;
  var IE6 = window.XMLHttpRequest ? false : true;
  return [,].length == 2
  ? function( element, value )
  {
    var style = element.style;
    if( element.currentStyle && !element.currentStyle.hasLayout )
      style.zoom = 1;
    if( reOpacity.test( style.filter ) )
    {
      value = value >= 1 && IE6 ? '' : ( 'alpha(opacity=' + ( value*100 ) + ')' );
      style.filter = style.filter.replace( reOpacity, value );
    }
    else
      style.filter += ' alpha(opacity=' + ( value*100 ) + ')';
  }
  : function( element, value ) { element.style.opacity = value; }
} )();

/**
 * Adds event listener to the HTML element
 *
 * @param {HTMLElement} element - HTML element
 * @param {String} type - Type of the event
 * @param {Function} fn - The event listener
 */
var addEvent = function( element, type, fn )
{
  if( element.addEventListener )
  {
    switch( type )
    {
      case 'mouseenter': element.addEventListener( 'mouseover', mouseInOut( fn ), false ); break;
      case 'mouseleave': element.addEventListener( 'mouseout',  mouseInOut( fn ), false ); break;
      default: element.addEventListener( type, fn, false ); break;
    }
  }
  else if( element.attachEvent )
    element.attachEvent( 'on' + type, function(){ fn.call( element, window.event ); } );
}

function mouseInOut( fn )
{
  return function( event )
  {
    var element = event.relatedTarget;
    while( element && element !== this )
      element = element.parentNode;
    if( element !== this )
      fn.call( this, event );
  };
}

/**
 * Creates HTML element
 *
 * @param {String} tag - The tag of the element
 * @return {HTMLElement} - The created element
 */
var createElement = ( function() {
  return document.createElementNS
  ? function( tag ) { return document.createElementNS( 'http:\/\/www.w3.org\/1999\/xhtml', tag ); }
  : function( tag ) { return document.createElement( tag ); }
} )();


// ----------------------------------------------------------------------------

/**
 * Image fader [constructor]
 *
 * @param {Object} options - configuration
 */
function Slider( options )
{
  this.options = options;
  this.container = document.getElementById( options.container );
  if( !this.container )
    return;
  var elements = this.container.getElementsByTagName( '*' );
  if( options.slideClass )
  {
    this.slides = [];
    var i = elements.length;
    while( i-- )
      if( elements[i].className == options.slideClass )
        this.slides.push( elements[i] );
  }
  else
    this.slides = elements;
  this.length = this.slides.length;
  if( this.length < 2 )
    return;
  if( options.randomSlide )
  {
    this.index = options.randomSlide ? ~~( Math.random()*this.length ) : 0;  
    while( ( this.next = ~~( Math.random()*this.length ) ) == this.index )  
      continue;
  }
  else
    this.next = ( this.index = 0 ) + 1;
  for( var i = 0; i < this.length; i++ )
  {
    if( i != this.index )
    {
      setOpacity( this.slides[i], 0 );
      this.slides[i].style.visibility = 'hidden';
    }
    this.slides[i].style.display = 'block';
  }
  this.navigator = document.getElementById( options.navigator );
  if( this.navigator )
  {
    this.navLinks = [];
    this.navigator.innerHTML = '';
    var ul = createElement( 'ul' );
    for( var i = 0; i < this.length; i++ )
    {
      var li = createElement( 'li' );
      var link = createElement( 'a' );
      link.setAttribute( 'href', '#' );
      link.innerHTML = i;
      link.index = i;
      link.slider = this;
      addEvent( link, 'click', this.navigatorClick );
      li.appendChild( link );
      ul.appendChild( li );
      this.navLinks.push( link );
    }
    this.navLinks[this.index].className = 'active';
    this.navigator.appendChild( ul );
  }
  this.prevArrow = document.getElementById( options.prevArrow );
  this.nextArrow = document.getElementById( options.nextArrow );
  if( this.prevArrow && this.nextArrow )
  {
    var arrow = createElement( 'a' );
    arrow.setAttribute( 'href', '#' );
    arrow.innerHTML = 'previous';
    arrow.slider = this;
    addEvent( arrow, 'click', this.arrowClick );
    this.prevArrow.innerHTML = '';
    this.prevArrow.appendChild( arrow );
    arrow = createElement( 'a' );
    arrow.setAttribute( 'href', '#' );
    arrow.innerHTML = 'next';
    arrow.slider = this;
    addEvent( arrow, 'click', this.arrowClick );
    this.nextArrow.innerHTML = '';
    this.nextArrow.appendChild( arrow );
    if( options.hideArrows )
    {
      this.inSlide = false;
      this.inArrow = false;
      this.arrowsShowed = false;
      this.showHideArrows( false );      
      for( var i = 0; i < this.length; i++ )
      {
        var slide = this.slides[i];
        slide.slider = this;
        addEvent( slide, 'mouseenter', function( event ) {
          this.slider.inSlide = true;
          this.slider.showHideArrows( true );
        } );
        addEvent( slide, 'mouseleave', function( event ) {
          this.slider.inSlide = false;
          this.slider.showHideArrows( false );
        } );
      }
      this.prevArrow.slider = this;
      this.nextArrow.slider = this;
      var enterFunct = function()
      {
        this.slider.inArrow = true;
        this.slider.showHideArrows( true );
      }
      var leaveFunct = function()
      {
        this.slider.inArrow = false;
        this.slider.showHideArrows( false );
      }
      addEvent( this.prevArrow, 'mouseenter', enterFunct );
      addEvent( this.prevArrow, 'mouseleave', leaveFunct );
      addEvent( this.nextArrow, 'mouseenter', enterFunct );
      addEvent( this.nextArrow, 'mouseleave', leaveFunct );
    }    
  }  
  this.timer = null;
  var self = this;
  this.fnFade = function() { self.fade.call( self ); };
  this.holdTimer = setTimeout( this.fnFade, this.options.holdTimeout );
}

/**
 * Timer manipulator
 *
 */
Slider.prototype.fade = function()
{
  if( !this.timer )
  {
    this.startTime = +new Date();
    if( this.navigator )
    {
      this.navLinks[this.index].className = '';
      this.navLinks[this.next].className = 'active';
    }
    this.slides[this.next].style.visibility = 'visible';
    return this.timer = setInterval( this.fnFade, 10 );
  }
  var time = +new Date() - this.startTime;
  if( time >= this.options.fadeTimeout )
  {
    setOpacity( this.slides[this.index], 0 );
    setOpacity( this.slides[this.next], 1 );
    this.slides[this.index].style.visibility = 'hidden';
    this.index = this.next;
    if( options.randomSlide )
      while( ( this.next = ~~( Math.random()*this.length ) ) == this.index )  
        continue;
    else
      this.next = ( ++this.next >= this.length ) ? 0 : this.next;
    this.timer = clearInterval( this.timer );
    this.holdTimer = clearTimeout( this.holdTimer );
    return this.holdTimer = setTimeout( this.fnFade, this.options.holdTimeout );
  }
  var opacity = ( -Math.cos( ( time / this.options.fadeTimeout )*Math.PI ) * 0.5 ) + 0.5;
  setOpacity( this.slides[this.index], 1 - opacity );
  setOpacity( this.slides[this.next], opacity );
}

/**
 * Click manipulator (links on navigator)
 *
 */
Slider.prototype.navigatorClick = function( event )
{
  var slider = this.slider;
  if( !slider.timer && this.index != slider.index )
  {
    clearTimeout( this.holdTimer );
    slider.next = this.index;
    slider.startTime = +new Date();
    slider.navLinks[slider.index].className = '';
    slider.navLinks[slider.next].className = 'active';
    slider.slides[slider.next].style.visibility = 'visible';
    slider.timer = setInterval( slider.fnFade, 10 );
  }
  event.preventDefault ? event.preventDefault() : event.returnValue = false;
}

/**
 * Click manipulator (arrows)
 *
 */
Slider.prototype.arrowClick = function( event )
{
  var slider = this.slider;
  if( !slider.timer )
  {
    clearTimeout( this.holdTimer );
    if( this.innerHTML == 'previous' )
      slider.next = slider.index > 0 ? slider.index - 1 : slider.length - 1;
    else
      slider.next = slider.index == ( slider.length - 1 ) ? 0 : slider.index + 1;
    if( slider.navLinks )
    {
      slider.navLinks[slider.index].className = '';
      slider.navLinks[slider.next].className = 'active';
    }
    slider.slides[slider.next].style.visibility = 'visible';
    slider.startTime = +new Date();
    slider.timer = setInterval( slider.fnFade, 10 );
  }
  event.preventDefault ? event.preventDefault() : event.returnValue = false;
}

/**
 * Shows or hides the arrows
 *
 * @param Bool - To show arrows
 */
Slider.prototype.showHideArrows = function( show )
{
  if( show && ( this.inSlide || this.inArrow ) )
  {
    this.prevArrow.style.display = 'block';    
    this.nextArrow.style.display = 'block';    
  }
  if( !show && ( !this.inSlide && !this.inArrow ) )
  {
    this.prevArrow.style.display = 'none';    
    this.nextArrow.style.display = 'none';    
  }
}

// ----------------------------------------------------------------------------

domReady( function() { new Slider( options ) } );

} )();
