function PhotoLoader (options){
  this.persons = options.persons;
  this.personIds = null;
  this.smallImagePrefix = '/image.php?width=64&height=64&img=';
  this.mediumImagePrefix = '/image.php?width=128&height=128&img=';
  
  /**
    Start loading all (thumbnail) photos. 2 at once.
  */
  this.start = function (){
    this.index = -1;
    this.personIds = [];
    for (var id in this.persons){
      if (this.persons[id].photo != ''){
        this.personIds.push(id);
      }
    }
    this.personIds = randomizeArray(this.personIds);
    this.loadNextPhoto();
    this.loadNextPhoto();
  }
  
  /**
    Load the (thumbnail) photo for the given person.
    
    @param loadNext if true, the next photo will be loaded after this one's 
      complete, or if this one doesn't have a photo, or if it was already loaded.
  */
  this.loadPhoto = function (id, loadNext){
    var person = this.persons[id];
    if (person.photo == ''){
      if (loadNext){ this.loadNextPhoto(); }
      return;
    }
    var td = $('#person-' + id);
    if (td.hasClass('loaded')){
      if (loadNext){ this.loadNextPhoto(); }
      return;
    }
    td.addClass(person.gender + '-loading');
    var img = td.find('img');
    img.attr('src', this.smallImagePrefix + encodeURIComponent(person.photo));
    img.load(
      jQuery.proxy(
        function (){
          // Show the photo and load the next one.
          this.onPhotoLoaded(id);
          if (loadNext){ this.loadNextPhoto(); }
        },
        this
      )
    );
  }
  
  this.loadNextPhoto = function (){
    this.index++;
    if (this.index >= this.personIds.length){ return; }
    var id = this.personIds[this.index];
    this.loadPhoto(id, true);
  }

  this.onPhotoLoaded = function (id){
    var person = this.persons[id];
    var td = $('#person-' + id);
    var img = td.find('img');
    // Show the photo.
    img.fadeIn(
      'slow',
      function (){
        // Indicate that this photo has been loaded (its css prevents the background 
        // image from showing through on mouseover).
        td.removeClass(person.gender + '-loading');
        td.addClass('loaded');
      }
    );
  }
}
function randomizeArray(array){
  var result = [];
	var highest = array.length -1;
	for (var i=0; i<=highest; i++){
		var index = Math.round(Math.random()*highest);
		while (result[index] != null){
			index = Math.round(Math.random()*highest);
		}
		result[index] = array[i];
	}
  return result;
}

var personDetailsBaloon = null;
var personDetailsHideTimeout = null;
function getPersonDetailsBaloon (){
  if (personDetailsBaloon == null){
    var el = $('#persoon-preview');
    personDetailsBaloon = {
      personId:     null,
      element:      el,
      name:         el.find('.name'),
      photo:        el.find('.photo'),
      organization: el.find('.organisatie'),
      age:          el.find('.age'),
      quote:        el.find('.quote'),
      linkedIn:     el.find('.linkedIn'),
      twitter:      el.find('.twitter'),
      video:        el.find('.video'),
      blog:         el.find('.blog')
    };
  }
  return personDetailsBaloon;
}
function showPersonDetails (event){
  // Prevent any pending calls to 'hidePersonDetails' from hiding the baloon.
  clearTimeout(personDetailsHideTimeout);
  
  // Get the baloon div and the element that contains the id of the person to be shown.
  var baloon = getPersonDetailsBaloon();
  var td = $(event.currentTarget).parent('td').andSelf();
  var id = td.attr('id').replace(/person-/, '');
  var person = persons[id];
  
  baloon.personId = id;
  // Fill the baloon content.
  baloon.name.text(person.name);
  baloon.photo.attr('alt', person.name);
  baloon.organization.text(person.org);
  // The 'lowsrc' of the photo.
  baloon.element.removeClass();
  baloon.element.addClass(person.gender);
  if (td.hasClass('loaded')){
    baloon.photo.show();
    baloon.photo.attr('src', td.find('img').attr('src'));
  } else {
    baloon.photo.hide();
    HRTop100.photoLoader.loadPhoto(id);
  }
  if (person.photo != ''){
    baloon.photo.show();
    baloon.photo.attr('src', '/image.php?width=128&height=128&img=' + person.photo);
// Use loadImage to load the image in the background and fade it in after loading.
// This sometimes flickers... (maybe if the lowsrc is already there)?
// Sometimes the photo of someone else fades in (because the mouse is already over someone new).
//    loadImage(
//      '/image.php?width=128&height=128&img=' + person.photo,
///      function (photoSrc){
//        if (id != getPersonDetailsBaloon().personId){ return; }
//        baloon.photo.attr('src', photoSrc);
//        if (baloon.photo.is(':hidden')){ baloon.photo.fadeIn('slow'); } // Prevent flicker?
//      }
//    );
  }
  // The actual photo.
  if (person.age == ''){ baloon.age.text(''); }
  else { baloon.age.text(' (' + person.age + ')'); }
  if (person.quote == ''){ baloon.quote.text(''); }
  else { baloon.quote.text('"' + person.quote + '"'); }
  baloon.linkedIn.toggle( person.linkedInUrl != '' );
  baloon.twitter.toggle( person.twitterUrl != '' );
  baloon.video.toggle( person.videoUrl != '' );
  baloon.blog.toggle( person.blogUrl != '' );
  
  // Show and position the baloon.
  positionPersonDetails(event);
  baloon.element.show();
}
function hidePersonDetails (){
  clearTimeout(personDetailsHideTimeout);
  personDetailsHideTimeout = setTimeout(function (){$('#persoon-preview').hide();}, 100);
}
function positionPersonDetails (event){
  var baloon = getPersonDetailsBaloon();
  var top = event.pageY;
  var left = event.pageX;
  var viewport = $(window);
  if (left + baloon.element.outerWidth() + 10 > viewport.scrollLeft() + viewport.width()){
    left = viewport.scrollLeft() + viewport.width() - baloon.element.outerWidth() - 10;
  }else{
    left += 10;
  }
  if (top + baloon.element.outerHeight() + 20 > viewport.scrollTop() + viewport.height()){
    top = event.pageY - baloon.element.outerHeight() - 10;
  }else{
    top += 10;
  }
  baloon.element.offset({top: top, left: left});
}

function loadImage(src, callback){
  var img = $('<img style="display: none;" />').appendTo('body');
  img.attr('src', src);
  img.load(function (){
    img.remove();
    callback(src);
  });
}
function submitForm(id, validator, elements){
  var form = document.forms[id];
  for (var name in elements){
    if (form.elements[name]){
      form.elements[name].value = elements[name];
    }
  }
  if (validator && !validator(form)){
    return;
  }
  form.submit();
}
