function slideshow(dataSource, containerId, playButtonId, positionDisplayId, autoplay) {
  this.container = document.getElementById(containerId);
  this.playButton = document.getElementById(playButtonId);
  this.positionDisplay = document.getElementById(positionDisplayId);

  this.slides = new Array();
  
  this.container.className = 'loading';
  
  ssRef = this;
  
  if (typeof(dataSource) == 'object') {
    this.init(data, autoplay);
  } else if (typeof(dataSource) == 'string') {
    var req = new XMLHttpRequest();
    req.open('GET', dataSource, true);
    req.onreadystatechange = function (evt) {
      if (req.readyState == 4) {
        if ((req.status == 200 || location.protocol == 'file:') && req.responseText) {
          var data = jsonParse(req.responseText);
          ssRef.init(data, autoplay);
        } else {
          alert('Kunde inte ladda bildspel!');
        }
      }
    }
    req.send();
  } else {
    alert('Kunde inte ladda bildspel!');
  }
}

slideshow.prototype.preload = function() {
  // preload up to n slides ahead
  for (var i = this.index; i < this.slideData.length && i <= (this.index + this.preloadSlides); i++) {
    this.loadSlide(i);
  }
}

slideshow.prototype.loadSlide = function(i) {
  if (this.slideData[i]) {
    var data = this.slideData[i];
    if (!this.slides[i]) {
      var slide = document.createElement('div');
      slide.className = 'slide';
      
      var heading = document.createElement('h1');
      heading.appendChild(document.createTextNode(data.title));
      slide.appendChild(heading);
      
      if (data.image) {
        var image = new Image();
        image.src = data.image;
        image.alt = '';
        slide.appendChild(image);
      }
      
      var caption = document.createElement('div');
      caption.className = 'caption';
      caption.innerHTML = data.caption;
      slide.appendChild(caption);
      
      this.slides[i] = slide;
      this.container.appendChild(slide);

      if (image) {
        if (image.complete) {
          caption.style.width = image.width + 'px';
        } else {
          image.onload = function() {
            caption.style.width = image.width + 'px';
          }
        }
      }
    }
  }
}

slideshow.prototype.slideLoaded = function(index) {
  if (this.slides[index]) {
    var images = this.slides[index].getElementsByTagName('img');
    for (var i = 0; i < images.length; i++) {
      if (!images[i].complete) {
        alert(images[i].src);
        return false;
      }
    }
    return true;
  }
  
  alert('not found');
  return false;
}

slideshow.prototype.init = function(data, autoplay) {
  this.slideData = data;
  
  if (!this.slideData) {
    alert('Invalid data!');
  }
  
  this.preloadSlides = 2;

  this.index = 0;
  this.toIndex = 1;
  
  this.autoplayDelay = 8000;
  this.autoplaying = false;
  this.autoplayTimeout = null;
  
  this.transitionTime = 400;
  this.transitionStart = null;
  this.transitionInterval = null;
  
  this.preload();
  
  this.container.className = '';
  this.slides[this.index].style.display = 'block';
  
  setText(this.positionDisplay, (this.index + 1) + ' / ' + this.slideData.length);
  
  if (autoplay) {
    this.autoplay(true);
  }
}

slideshow.prototype.change = function(toIndex) {
  if (this.transitionInterval || toIndex == this.index) {
    return;
  }
  
  this.toIndex = toIndex;
  
  for (var i = 0; i < this.slides.length; i++) {
    if (i != this.index && i != this.toIndex) {
      this.slides[this.toIndex].style.display = 'none';
    }
  }
  
  setOpacity(this.slides[this.toIndex], 0);
  setOpacity(this.slides[this.index], 100);
  this.slides[this.toIndex].style.zIndex = 1;
  this.slides[this.index].style.zIndex = 2;
  this.slides[this.toIndex].style.display = 'block';
  this.slides[this.index].style.display = 'block';
  
  var selfRef = this;
  var currentDate = new Date();
  this.transitionStart = currentDate.getTime();
  this.transitionInterval = setInterval(function() {selfRef.transition();}, 50);
}

slideshow.prototype.transition = function() {
  var currentDate = new Date();
  var elapsedTime = currentDate.getTime() - this.transitionStart;
  
  if (elapsedTime < this.transitionTime) {
    var progress = parseInt(elapsedTime / this.transitionTime * 100);
  } else {
    var progress = 100;
  }
  
  setOpacity(this.slides[this.toIndex], progress);
  setOpacity(this.slides[this.index], 100 - progress);
  
  if (progress == 100) {
    this.slides[this.index].style.display = 'none';
    this.index = this.toIndex;
    
    clearInterval(this.transitionInterval);
    this.transitionInterval = null;
    
    setText(this.positionDisplay, (this.index + 1) + ' / ' + this.slideData.length);
  
    if (this.autoplaying) {
      var selfRef = this;
      this.autoplayTimeout = setTimeout(function() {selfRef.next(true);}, this.autoplayDelay);
    }
    
    this.preload();
  }
}

slideshow.prototype.next = function(autoplay) {
  if (!autoplay && this.autoplaying) {
    this.autoplay(false);
  }
  
  if (this.index < (this.slideData.length - 1)) {
    var nextIndex = this.index + 1;
  } else {
    var nextIndex = 0;
  }

  if (autoplay && !this.slideLoaded(nextIndex)) {
    alert('not loaded');
    var selfRef = this;
    this.autoplayTimeout = setTimeout(function() {selfRef.next(true);}, 3000);
    return;
  }
  
  this.change(nextIndex);
}

slideshow.prototype.prev = function() {
  this.autoplay(false);

  if (this.index > 0) {
    var prevIndex = this.index - 1;
  } else {
    var prevIndex = this.slideData.length - 1;
  }
  
  this.loadSlide(prevIndex);

  this.change(prevIndex);
}


slideshow.prototype.autoplay = function(val) {
  this.autoplaying = val;
  
  if (this.autoplayTimeout) {
    clearTimeout(this.autoplayTimeout);
    this.autoplayTimeout = null;
  }
  
  if (this.autoplaying) {
    this.playButton.className = 'pause';
    var selfRef = this;
    this.autoplayTimeout = setTimeout(function() {selfRef.next(true);}, this.autoplayDelay);
  } else {
    this.playButton.className = 'play';
  }
}

slideshow.prototype.toggleAutoplay = function() {
  this.autoplay(!this.autoplaying);
}


function setText(element, text) {
  while(element.hasChildNodes()) {
    element.removeChild(element.firstChild);
  }
  
  element.appendChild(document.createTextNode(text));
}

function setOpacity(element, opacity) {
  // this should set opacity in every single browser that has any support for it
  if (element.style) {
    if (element.style.opacity!=null) {
      element.style.opacity = (opacity/100);
    } else if (element.style.MozOpacity!=null) {
      element.style.MozOpacity = (opacity/100);
    } else if (element.style.MozOpacity!=null) {
      element.style.KhtmlOpacity  = (opacity/100);
    } else if (element.style.filter!=null) {
      element.style.filter = "alpha(opacity="+opacity+")";
    }
  }
}
