/*
   35mm Photo Viewer v4 | schillmania.com
   ---------------------------------------
   Photo control, thumbnail, search engine
   objects and event handlers
*/

var theme = null;
var themes = ['alpha','white','inverted'];

/*

 VALID THEMES:

 0 - alpha
 1 - white
 2 - inverted

 Set themeIndex to one of these values.

*/

var themeIndex = 0; // SET THIS VALUE TO ONE OF THE ABOVE (between 0 and 2)

// If you edit code past this bit, bad things may happen if you're not sure of what you're doing. ;)

theme = themes[themeIndex];

var tStart = new Date();

var controlBar = null;
var searchBar = null;
var photoBar = null;
var photoViewer = null;
var thumbnail = null;
var loader = null;
var searchEngine = null;
var soundManager = null; // defined externally

function PhotoViewer() {
  writeDebug('PhotoViewer()');
  var self = this;
  this.o = null;
  this.bookmarkContainer = null;
  this.bookmark = null;
  this.container = null;
  this.container = getElementsByClassName('container','div')[0];
  // this.status = null;
  this.lastPhoto = null;
  this.pages = [];
  this.pageTabs = null;
  this.pageIcons = [];
  this.page = -1;
  this.collections = null;
  this.caption = null;
  this.photo = null;
  this.loader = null;
  this.setPhoto = function(e) {
    var url = null;
    // URL via event, or object via call
    if ((e && !e.href)||(!e && event)) {
      url = e?e.target:event.srcElement;
      self.photo = url;
    } else if (e) {
      url = e.href||e;
      self.photo = e;
    } else {
      return false;
    }
    if (self.lastPhoto) {
      // reset last photo
      self.lastPhoto.className = self.lastPhoto.className.replace(/selected/gi,'');
    }
    self.lastPhoto = self.photo;
    self.photo.className += ' selected';
    self.setStatus(1,(pngHandler.isIE?event:e));
    self.o.onerror = self.onload; // in case the image fails to load (bad URL etc.)
    self.o.onload = self.onload;
    setTimeout("soundManager.play('type');photoViewer.o.src='"+url+"'",20);
    return false;
  }
  this.setVideo = function(e) {
    var url = null;
    // URL via event, or object via call
    if ((e && !e.href)||(!e && event)) {
      url = e?e.target:event.srcElement;
      // self.photo = url;
    } else if (e) {
      url = e.href||e;
      // self.photo = e;
    } else {
      return false;
    }
  }
  this.setPage = function(e) {
    // URL with int by event, or int via call
    if (typeof(e)=='object'||(typeof(e)=='undefined' && event)) {
      p = ((e?e.target:event.srcElement).hash.substr(5)); // set index according to substring of anchor hash (ie. #page2)
      writeDebug('setPage: p = '+(e?e.target:event.srcElement).hash.substr(5));
      writeDebug('setPage: p(2) =  '+(e?e.target:event.srcElement));
    } else {
      p = e;
    }
    writeDebug('PhotoViewer.setPage('+(typeof(p)=='undefined'?'null':p)+')');
    if (typeof(p) != 'undefined' && p == self.page) return false;
    if (self.page>=0) {
      self.pages[self.page].style.display = 'none';
      self.pageIcons[self.page].className = self.pageIcons[self.page].className.replace(/ selected/gi,'');
    }
    self.page = p;
    self.pages[self.page].style.display = 'block';
    try {
      this.blur();
    } catch(e) {
      // d'oh!
      writeDebug('PhotoViewer.setPage(): blur() failed.');
    }
    self.pageIcons[self.page].className += ' selected';
    writeDebug('PhotoViewer.setPage() - done');
    soundManager.play('page0');
    return false;
  }
  this.setStatus = function(s,e) {
    loader.setStatus(s,e);
    // self.status.className = typeof(s)!='undefined'&&s?'status active':'status';
  }
  this.mouseoverHandler = function() {
    soundManager.play('mouseover');
  }
  this.onload = function() {
    self.setStatus(0);
    // show description etc.
    self.caption[0].innerHTML = self.caption[1].innerHTML = self.caption[2].innerHTML = self.photo.getElementsByTagName('span')[0].innerHTML;
    if (self.photo.id) {
      var loc = window.location.toString();
      self.bookmark.href = (loc.indexOf('#')?loc.substr(0,loc.indexOf('#')):loc)+'#'+self.photo.id;
      self.bookmarkContainer.style.visibility = 'visible';
    } else {
      self.bookmarkContainer.style.visibility = 'hidden';
    }
  }

  this.createPageTabs = function(oParent) {
    writeDebug('PhotoViewer.createPageTabs()');
    var o = document.createElement('ul');
    var LIs = document.getElementById('pages').childNodes;
    var tmpPage = null;
    var tmp = [];
    var pageCount = 0;
    /*
     Structure as follows:
     <ul>
      <li><a href="#pageX"><span>(X+1)</span></a></li>
      <!-- eg. -->
      <li><a href="#page0"><span>1</span></a></li>
     </ul>
    */
    for (var i=0; i<LIs.length; i++) {
      if (LIs[i].nodeName.toLowerCase()=='li') {
        tmpPage = document.createElement('li');
        tmp = [document.createElement('a'),document.createElement('span'),document.createTextNode('page '+(pageCount+1))];
        tmp[0].href = '#page'+pageCount;
        tmp[1].appendChild(tmp[2]);
        tmp[0].appendChild(tmp[1]);
        tmpPage.appendChild(tmp[0]);
        o.appendChild(tmpPage);
        pageCount++;
      } else {
        // found node that was not an LI.
      }
    }
    writeDebug('PhotoViewer.createPageTabs(): found '+pageCount+' pages.');
    oParent.appendChild(o);
    self.pageTabs = oParent.getElementsByTagName('a');
  }

  this.init = function() {
    writeDebug('PhotoViewer.init() - start');
    // var header = document.getElementById('header'); // getElementsByClassName('header','div',document.getElementById('photoSelector'))[0];
    self.bookmarkContainer = document.getElementById('bookmark');
    self.bookmark = self.bookmarkContainer.getElementsByTagName('a')[0];
    self.caption = document.getElementById('caption').getElementsByTagName('div');

    self.createPageTabs(document.getElementById('pageTabs'));

//    self.pageTabs = document.getElementById('pageTabs').getElementsByTagName('a');

    self.collections = getElementsByClassName('collection','div',document.getElementById('photoSelector'));
    self.o = document.getElementById('photo');
    // Page toggle handlers
    writeDebug('PhotoViewer.init(): configuring pages');

    for (var i=0; i<self.pageTabs.length; i++) {
      self.pages[i] = document.getElementById('page'+i);
      // self.pages[i].style.display = 'none';
      self.pageIcons[i] = document.createElement('a');
      self.pageIcons[i].href = '#page'+i;
      self.pageIcons[i].className = 'pageLink';
      self.pageIcons[i].innerHTML = '&nbsp';
      self.pageIcons[i].onmouseover = self.mouseoverHandler;
      self.pageIcons[i].onclick = self.setPage;
    }

    // assign photo if applicable
    if (window.location.hash) {
      self.setPhoto(document.getElementById(window.location.hash.substr(1))||document.getElementById('default'));
      self.setPage(0);
      // set by index
    } else {
      self.setPage(0);
      // self.setPhoto(document.getElementById('default'));
      var photos = getElementsByClassName('default','a',document.getElementById('photoSelector'));
      self.setPhoto(document.getElementById((photos.length==1?photos[0]:photos[parseInt(Math.random()*photos.length)]).id));
    }

    writeDebug('PhotoViewer.init() - end');
  }
  writeDebug('PhotoViewer() - end');
}

function Loader() {
  writeDebug('Loader()');
  var self = this;
  this.setStatus = function(s,e) {
    if (s) {
      // thumbnail.hide();
      // thumbnail.disabled = 1;
      self.show(e);
    } else {
      self.o.display = 'none';
      // thumbnail.disabled = 0;
      self.hide();
    }
  }
  this.show = function(e) {
    try {
      self.move(e); // need event for safari - remove try .. catch later
    } catch(e) {
      // d'oh - will fire on initial/scripted show() call
      return false;
    }
    self.o.style.display = 'block';
    addEventHandler(document,'mousemove',self.move);
  }
  this.hide = function() {
    self.o.style.display = 'none';
    removeEventHandler(document,'mousemove',self.move);
  }
  this.move = function(e) {
    e = e?e:event;
    if (e) {
      var thumbActive = (thumbnail.oContainer.style.display == 'block');
      var offX = thumbActive?45:0;
      var offY = thumbActive?-30:0;
      self.o.style.left = (e.clientX+2+offX)+'px';
      self.o.style.top = (e.clientY-22+offY)+(document.documentElement.scrollTop||document.body.scrollTop||0)+'px';
    }
  }
  this.init = function() {
    writeDebug('Loader.init()');
    document.body.appendChild(self.o);
  }
  this.o = document.createElement('div');
  this.o.id = 'loader';
  this.o.className = 'png';
  this.oFP = document.createElement('div');
  this.oFP.className = 'faceplate';
  this.oP = document.createElement('div');
  this.oP.className = 'progress';
  this.o.appendChild(this.oFP);
  this.o.appendChild(this.oP);
  writeDebug('Loader() - end');
}

function ControlBar() {
  writeDebug('ControlBar()');
  var self = this;
  this.o = null;
  this.oParentID = 'controls';
  this.oParent = null;
  this.tabs = null;
  this.activeTab = null;
  this.tabRelatedContent = null; // how ugly. ah well.
  this.tabMainContent = null;

  this.setTab = function(e) { // event object or integer
    o = (typeof(e)=='object'||(typeof(e)=='undefined' && event))?(e?e.target:event.srcElement).index:e;
    var regExp = new RegExp(' active'+(pngHandler.isIE?'IE':''),'g');
    // writeDebug('ControlBar.setTab()');
    if (self.activeTab != null) {
      self.tabs[self.activeTab].className = self.tabs[self.activeTab].className.replace(regExp,'');
      self.tabContent[self.activeTab].style.display = 'none';
      self.tabRelatedContent[self.activeTab].style.display = 'none';
    }
    self.tabs[o].className += ' active'+(pngHandler.isIEWin?'IE':'');
    self.tabContent[o].style.display = 'block';
    self.tabRelatedContent[o].style.display = 'block';

    if (pngHandler.isIEWin) {
      if (self.activeTab != null) pngHandler.refresh(self.tabs[self.activeTab],self.tabs[self.activeTab].srcInactive);
      pngHandler.refresh(self.tabs[o],self.tabs[o].srcActive);
    }

    self.activeTab = o;
    if (o==1) {
      try {
        searchBar.oInput.focus();
      } catch(e) {
        // oh well
      }
    }

    soundManager.play('select');

  }

  this.toggleMinimize = function() {
    var result = self.tabRelatedContent[self.activeTab].style.display!='block'?'block':'none';
    self.tabRelatedContent[self.activeTab].style.display = result;
    soundManager.play((result=='block'?'out':'in'));
  }

  this.mousedownHandler = function(e) {
    mouseHandler.startDrag(document.getElementById('container'),e);
  }

  this.init = function() {
    writeDebug('ControlBar().init()');
    self.o = document.createElement('div');
    self.o.className = 'bar png';
    self.tabs = [document.createElement('div'),document.createElement('div'),document.createElement('div')];
    self.tabs[0].className = 'tab photos png';
    self.tabs[0].title = 'View photos by collection';
    self.tabs[0].index = 0;
    self.tabs[1].className = 'tab search png';
    self.tabs[1].title = 'Search for photos';
    self.tabs[1].index = 1;
    self.tabs[2].className = 'tab about png';
    self.tabs[2].title = 'About this app';
    self.tabs[2].index = 2;

    self.mouseoverIE = function() { if (this.className.indexOf('active')==-1) pngHandler.refresh(this,this.srcActive); }
    self.mouseoutIE = function () { if (this.className.indexOf('active')==-1) pngHandler.refresh(this,this.srcInactive); }

    if (pngHandler.isIEWin) {
      self.tabs[0].className = 'tab photosIE png';
      self.tabs[1].className = 'tab searchIE png';
      self.tabs[2].className = 'tab aboutIE png';

      var fileType = (pngHandler.supported?'.png':'.gif');

      self.tabs[0].srcActive = 'image/'+theme+'tab_photos_active'+fileType;
      self.tabs[0].srcInactive = 'image/'+theme+'tab_photos'+fileType;

      self.tabs[1].srcActive = 'image/'+theme+'tab_search_active'+fileType;
      self.tabs[1].srcInactive = 'image/'+theme+'tab_search'+fileType;

      self.tabs[2].srcActive = 'image/'+theme+'tab_about_active'+fileType;
      self.tabs[2].srcInactive = 'image/'+theme+'tab_about'+fileType;

      for (var i=0; i<self.tabs.length; i++) {
        self.tabs[i].onmouseover = self.mouseoverIE;
        self.tabs[i].onmouseout = self.mouseoutIE;
      }
    }

    self.oParent = document.getElementById(self.oParentID);

    // apply tabs under bar
    for (var i=0; i<self.tabs.length; i++) {
      self.tabs[i].onclick = self.setTab;
      self.oParent.appendChild(self.tabs[i]);
    }
    self.oParent.appendChild(self.o); // append bar

    writeDebug('ControlBar(): Creating child objects');
    // create child objects
    photoBar = new PhotoBar();
    searchBar = new SearchBar();

    searchEngine = new SearchEngine();
    searchEngine.init();

    self.tabContent = [photoBar.o,searchBar.o,document.getElementById('about-descriptor')];
    self.tabRelatedContent = [photoViewer.container,searchEngine.oResults,document.getElementById('about-content')];
    writeDebug('ControlBar(): Hiding tab related content');
    for (i=0; i<self.tabRelatedContent.length; i++) {
      self.tabRelatedContent[i].style.display = 'none';
    }
    // append drag handler to "about" content - excluding ie:mac in this special case because it's crap
    if (!pngHandler.isIEMac) document.getElementById('about-descriptor').onmousedown = controlBar.mousedownHandler;

  }

}

function PhotoBar() {
  writeDebug('PhotoBar()');
  var self = this;
  var tmp;
  this.oParent = null;
  this.oParentID = 'controls';
  this.o = document.createElement('div');
  this.o.className = 'photoControls';
  this.o.onmousedown = controlBar.mousedownHandler;
  this.o.innerHTML = '<h2 style="width:auto">Photos</h2><div style="float:left;display:inline;line-height:17px">&nbsp; | &nbsp;Pages: &nbsp;</div>';
  for (var i=0; i<photoViewer.pageIcons.length; i++) {
    this.o.appendChild(photoViewer.pageIcons[i]);
  }
  this.oParent = document.getElementById(this.oParentID);
  this.oParent.appendChild(this.o);
  this.oMinimize = document.createElement('a');
  this.oMinimize.className = 'minimize';
  this.oMinimize.title = 'Hide/show content';
  if (pngHandler.isIE) {
    this.oMinimize.onmouseover = function() {this.className+=' minActive';}
    this.oMinimize.onmouseout = function() {this.className=this.className.replace(/ minActive/g,'');}
  }
  this.oMinimize.onclick = controlBar.toggleMinimize;
  this.o.appendChild(this.oMinimize);

  writeDebug('PhotoBar() - end');
}

function SearchBar() {
  writeDebug('SearchBar()');
  var self = this;
  this.oParent = null;
  this.oParentID = 'controls';
  this.o = document.createElement('form');

  this.updateInput = function(val) {
    writeDebug('SearchBar.updateInput('+(val?val:'null')+')');
    if (!val) return false;
    self.oInput.value = val.replace(/\++/g,' ');
  }

  this.onchangeHandler = function() {
    if (self.oInput.value.length) {
      if (self.oButton.className.indexOf('enabled')<0) {
        self.oButton.className += ' enabled';
        if (pngHandler.isIE) {
          addEventHandler(self.oButton,'mouseover',self.ieButtonEnableOver);
          addEventHandler(self.oButton,'mouseout',self.ieButtonEnableOut);
        }
      }
    } else {
      if (self.oButton.className.indexOf('enabled')+1) {
        self.oButton.className = self.oButton.className.replace(/ enabled/g,'');
        if (pngHandler.isIE) {
          self.oButton.className = 'go'; // forced reset
        }
      }
    }
  }

  this.keyDownHandler = function() {
    // soundManager.play('type');  // too noisy.
  }

  this.onfocusHandler = function() {
    if (!self.oInput.className) self.oInput.className = 'focus';
  }
  this.onblurHandler = function() {
    if (self.oInput.className) self.oInput.className = '';
  }
  this.ieButtonEnableOver = function() {
    self.oButton.className = 'go enabled hover';
  }
  this.ieButtonEnableOut = function() {
    self.oButton.className = 'go enabled';
  }
  this.submitHandler = function() {
    if (self.oButton.className.indexOf('enabled')<0) return false;
    try {
      searchEngine.doSearch(self.oInput.value);
    } catch(e) {
      writeDebug('<b>Warning</b>: submit exception.');
      return false;
    }
    return false;
  }

  this.o.onmousedown = controlBar.mousedownHandler;
  this.o.innerHTML = '<h2>Search</h2>';
  this.oInput = document.createElement('input');
  this.oButton = document.createElement('a');
  this.oButton.className = 'go';
  this.o.appendChild(this.oInput);
  this.o.appendChild(this.oButton);
  this.oMinimize = document.createElement('a');
  this.oMinimize.className = 'minimize';
  if (pngHandler.isIE) {
    this.oMinimize.onmouseover = function() {this.className+=' minActive';}
    this.oMinimize.onmouseout = function() {this.className=this.className.replace(/ minActive/g,'');}
  }
  this.oMinimize.onclick = controlBar.toggleMinimize;
  this.o.appendChild(this.oMinimize);

  this.oInput.onkeydown = this.keyDownHandler;

  if (pngHandler.isIE) {
    this.oInput.onkeyup = this.onchangeHandler;
    this.oInput.onfocus = this.onfocusHandler;
    this.oInput.onblur = this.onblurHandler;
  } else {
    this.oInput.onkeypress = this.onchangeHandler;
    this.oInput.onmouseover = this.onchangeHandler;
    this.oInput.onmouseout = this.onchangeHandler;
  }
  this.oButton.onclick = this.submitHandler;
  this.o.onsubmit = this.submitHandler;

  this.oParent = document.getElementById(this.oParentID);
  this.oParent.appendChild(this.o);
  writeDebug('SearchBar() - end');
}

function SearchEngine() {
  writeDebug('SearchEngine()');
  // grab data elements
  // strip keywords etc. from referrer / URL
  // eg. http://www.google.ca/search?hl=en&ie=UTF-8&q=recordland+calgary&meta=
  // search for keywords
  // display results

  var self = this;
  var t = this;
  this.o = null;
  this.descriptions = null;
  this.query = null;
  this.keywords = [];
  this.matches = [];
  this.matchesMax = 6;
  this.results = [];
  this.regExp = null;
  this.regExpStr = '';
  this.searchCount = 0;
  this.oResults = null;
  this.oID = 'search';

  this.queryParams = ['q','p','ask','k','key','keyword','keywords','kwd','qq','qry_str','qu','qry','query','recherche','s','search','searchitem','searchfor','search_text','search_term','t','term','terms','va']; // search engine query parameters (e.g. google uses q=..) - this list unscrupulously borrowed from the guys at SEHL (thanks) - http://suda.co.uk/projects/SEHL

  this.init = function() {
    writeDebug('SearchEngine.init()');
    // self.o.innerHTML = 'search';
    // self.o.appendChild(searchBar.oForm);
    self.o = document.getElementById(self.oID);
    self.oResults = document.createElement('div');
    self.oResults.className = 'results';
    self.o.appendChild(self.oResults);
    writeDebug('SearchEngine.init(): getting descriptions');
    self.descriptions = document.getElementById('pages').getElementsByTagName('span');
    writeDebug('SearchEngine.init(): got descriptions ('+self.descriptions.length+')');
    self.query = self.getQuery();
    searchBar.updateInput(self.query);
  }

  this.getQuery = function() {
    writeDebug('SearchEngine.getQuery()');
    var qResult = null;
    var dR = document.referrer;
    var loc = window.location.toString();
    if (loc) { // check location first, then referrer
      qResult = self.containsQuery(loc);
    }
    if (!qResult && dR) {
      qResult = self.containsQuery(dR);
    }
    return qResult;
  }

  this.containsQuery = function(s) {
    if (s.indexOf('?')==-1) {
      writeDebug('SearchEngine.containsQuery(): no arguments');
      return false; // exit if no arguments
    }
    var urlParams = s.substr(s.indexOf('?')+1).split('&'); // split parameters into array
    var q;
    for (var i=0; i<urlParams.length; i++) {
      for (var j=0; j<self.queryParams.length; j++) {
        if (urlParams[i].toLowerCase().indexOf((self.queryParams[j]+'=').toLowerCase())==0) { // match parameter pattern
          writeDebug('SearchEngine.containsQuery(): found param '+self.queryParams[j]);
          q = unescape(urlParams[i].substr(self.queryParams[j].length)).replace(/([\-().,-=@!"])/gi,'');
          writeDebug('SearchEngine.containsQuery(): query='+q);
          return q;
        }
      }
    }
    writeDebug('SearchEngine.containsQuery(): no match');
    return false;
  }

  this.doSearch = function(q,hideOnFail) {
    writeDebug('SearchEngine.doSearch('+(q?q:'null')+')');
    self.oResults.innerHTML = '';
    self.matches = [];
    if (!q) return false;
    q = q.replace(/\s+/g,'+');
    try {
      self.regExp = /([\-().,-=@!"])|\++(and|or|with|the|of|not|on|in|to|a|i|photo|pictures)\++/gi; // stuff to filter
      q = (q+'\+').replace(self.regExp,'\+'); // add leading/terminating + to catch any last filtered words
    } catch(e) {
      // invalid regular expression syntax
      writeDebug('SearchEngine.doSearch(): <b>Warning:</b> invalid RegExp syntax.');
    }
    q = q.replace(/\++/g,' '); // replace plus signs with spaces
    q = q.replace(/\s+$/g,''); // eliminate trailing whitespace
    writeDebug('SearchEngine.doSearch(): keywords='+q+'<br />');
    self.keywords = q.split(' ');
    self.regExpStr = '';
    for (i=0; i<self.keywords.length; i++) {
      self.regExpStr += '('+self.keywords[i]+')'+(i<self.keywords.length-1?'|':'');
    }
    try {
      self.regExp = new RegExp(self.regExpStr,'gi');
    } catch(e) {
      writeDebug('SearchEngine.doSearch(): <b>Warning:</b> invalid RegExp syntax.');
      // invalid regular expression syntax
    }
    writeDebug('SearchEngine.doSearch(): searching descriptions');
    for (i=0; i<self.descriptions.length; i++) {
      if (self.descriptions[i].innerHTML.match(self.regExp)) {
        var tmpMatch = new Object(self.descriptions[i].innerHTML.match(self.regExp).length);
        tmpMatch.relatedObject = self.descriptions[i].parentNode;
        self.matches[self.matches.length] = tmpMatch;
      }
    }
    writeDebug('SearchEngine.doSearch(): '+self.matches.length+' match'+(self.matches.length>1||!self.matches.length?'es':''));
    if (!self.matches.length) {
      self.oResults.innerHTML = '<span class="error">No matches found.</span>';
      if (hideOnFail) {
        writeDebug('SearchEngine.doSearch(): reverting to photo tab');
        photoViewer.setPage(0);
      }
      return false;
    }
    self.matches = self.matches.sort().reverse(); // arrange by keyword match count, highest to lowest.
    self.showResults();
  }

  this.showResults = function(noTransform) {
    writeDebug('SearchEngine.showResults()');
    self.results = [];
    var max = self.matches.length<self.matchesMax?self.matches.length:self.matchesMax;
    for (var i=0; i<max; i++) {
      self.results[i] = new Result(self.matches[i].relatedObject);
      self.oResults.appendChild(self.results[i].o);
      // if (document.body) self.results[i].transform(); // transform if after load()
      self.results[i].transform();
    }
    writeDebug('SearchEngine.doSearch() - end');
  }
  writeDebug('SearchEngine() - end');
}

function Result(oPhotoAnchor) {
  var self = this;
  this.oItem = oPhotoAnchor;
  this.o = document.createElement('div');
  this.o.className = 'result png';
  this.oDetails = document.createElement('div');
  this.oDetails.className = 'details';
  this.oThumbnailContainer = document.createElement('div');
  this.oThumbnailContainer.className = 'thumbnailContainer png';
  this.oThumbnail = document.createElement('div');
  this.oThumbnail.className = 'thumbnail';
  this.oDescription = document.createElement('div');
  this.oDescription.className = 'description png';

  if (this.oItem.className.indexOf('video')+1) {
    this.oThumbnail.style.backgroundImage = 'url("'+this.oItem.href.replace(this.oItem.href.substr(this.oItem.href.lastIndexOf('.')),'_thumb.gif')+'")';
    this.oThumbnail.style.backgroundPosition = '0px 0px';
  } else {
    this.oThumbnail.style.backgroundImage = 'url("'+thumbnail.URLs[this.oItem.thumbIndex]+'")';
    this.oThumbnail.style.backgroundPosition = '0px '+(this.oItem.thumbOffset?(-48*this.oItem.thumbOffset):0)+'px';
  }
  var tmpDescription = oPhotoAnchor.childNodes[0].innerHTML;
  // highlight keywords
  var matches = tmpDescription.match(searchEngine.regExp); // (return type: array)
  for (var i=0; i<matches.length; i++) {
    tmpDescription = tmpDescription.replace(new RegExp(matches[i],'gi'),'<b>'+matches[i]+'</b>');
  }
  this.oDescription.innerHTML = tmpDescription;

  this.o.appendChild(this.oDetails);
  this.oDetails.appendChild(this.oThumbnailContainer);
  this.oDetails.appendChild(this.oDescription);
  this.oThumbnailContainer.appendChild(this.oThumbnail);

  if (pngHandler.transform) {
    this.transform = function() {
      pngHandler.transform(self.o);
      pngHandler.transform(self.oThumbnailContainer);
      pngHandler.transform(self.oDescription);
    }
  } else {
    this.transform = function() {}
  }

  this.showItem = function(e) {
    var c = self.oItem.className;
    if (c.indexOf('video')+1) {
      photoViewer.setVideo(self.oItem);
    } else if (c.indexOf('related')+1) {
      window.open(self.oItem.href,'schillmaniaPopup');
    } else {
      photoViewer.setPhoto(self.oItem,(e?e:event));
    }
  }
  this.o.onclick = this.showItem;
  if (pngHandler.isIE) {
    this.o.onmouseover = function(){self.o.className+=' hover';}
    this.o.onmouseout = function(){self.o.className=self.o.className.replace(/ hover/gi,'');}
  }

}

var oDebug = null;

function writeDebug(x) {
  if (!oDebug) {
    oDebug = document.getElementsByTagName('p')[document.getElementsByTagName('p').length-1];
    oDebug.style.display = 'none';
  }
    var o = document.createElement('span'); // can't put block-level elements under P tags
    o.style.display = 'block';
    o.innerHTML = (new Date()-tStart)+': '+x;
  try {
    oDebug.appendChild(o);
  } catch(e) {
    alert('writeDebug failed.');
  }
}

function debugErrorHandler(eMsg,eURL,eLine) {
  try {
    writeDebug('<b>Error</b>: '+eMsg+' | '+eURL+', line '+eLine);
  } catch(e) {
    // oh well
    return false;
  }
}

function enableDebugMode() {
  window.onerror = debugErrorHandler;
  writeDebug('Debug mode + error handling enabled');
  oDebug.style.display = 'block';
}

function Thumbnail() {
  writeDebug('Thumbnail()');
  var self = this;
  this.o = null;
  this.descriptions = [];
  this.disabled = 1;
  this.lastIndex = -1;
  this.URLs = [];
  this.x = 64; // thumbnail width
  this.y = 48; // thumbnail height
  this.offY = parseInt(this.y*0.75);
  this.show = function(e) {
    // scope: this == anchor mouseover()
    if (self.disabled) return false;
    if (this.className.indexOf('video')+1) {
      self.lastIndex = -1; // change index with different URL
      self.o.style.backgroundImage = 'url("'+this.href.replace(this.href.substr(this.href.lastIndexOf('.')),'_thumb.gif')+'")';
      self.o.style.backgroundPosition = '0px 0px';
    } else {
      if (self.lastIndex != this.thumbIndex) {
        self.lastIndex = this.thumbIndex;
        self.o.style.backgroundImage = 'url("'+self.URLs[this.thumbIndex]+'")';
      }
      self.o.style.backgroundPosition = '0px '+(this.thumbOffset?(-self.y*this.thumbOffset):0)+'px';
    }
    try {
      self.move(e); // need event for safari - remove try.. catch later
    } catch(e) {
      // d'oh
      return false;
    }
    self.oContainer.style.display = 'block';
    soundManager.play('mouseover');
    addEventHandler(document,'mousemove',self.move);
  }
  this.hide = function() {
    if (self.disabled) return false;
    self.oContainer.style.display = 'none';
    removeEventHandler(document,'mousemove',self.move);
  }
  this.move = function(e) {
    e = e?e:event;
    if (e) {
      self.oContainer.style.left = (e.clientX+12)+'px';
      self.oContainer.style.top = (e.clientY-self.offY)+(document.documentElement.scrollTop||document.body.scrollTop||0)+'px';
    }
    // mouseHandler.stopBubbleHandler();
  }
  this.blur = function(e) {
    try {
      (e?e.target:(event?event.srcElement:document.body)).blur();
    } catch(e) {
      // oh well
      return false;
    }
  }
  this.init = function() {
    writeDebug('Thumbnail.init()');
    // grab thumbnail URLs, preload thumbnails, assign event handlers
    // var collections = getElementsByClassName('collection','div',document.getElementById('photoSelector'));
    var collections = photoViewer.collections;
    var preload = [];
    var items = [];
    var i,j,tmp,isVideo;
    writeDebug('Thumbnail.init(): getting collections');
    for (i=0; i<collections.length; i++) {
      if ((i+1)%2==0) collections[i].className = collections[i].className + ' alt';
      url = collections[i].getElementsByTagName('a')[0].href.toString();
      self.URLs[self.URLs.length] = (url.lastIndexOf('/')<url.length?url.substr(0,url.lastIndexOf('/')+1):'')+'thumbnails.jpg';
      // preload[preload.length] = new Image();
      // preload[preload.length-1].src = self.URLs[i];
      items = collections[i].getElementsByTagName('a');
      self.descriptions = collections[i].getElementsByTagName('span');
      for (j=0; j<items.length; j++) {
        isVideo = items[j].className?(items[j].className.indexOf('video')+1?true:false):false;
        items[j].title = self.descriptions[j].innerHTML;
        items[j].thumbIndex = i;
        items[j].thumbOffset = j;
        items[j].onmouseover = this.show;
        items[j].onmouseout = this.hide;
        items[j].onfocus = this.blur;
        if (!items[j].onclick) {
          items[j].onclick = isVideo?photoViewer.setVideo:photoViewer.setPhoto;
        }
      }
    }
    writeDebug('Thumbnail.init(): got collections ('+collections.length+')');
    self.oContainer = document.createElement('div');
    self.oContainer.id = 'thumbnailContainer';
    self.oContainer.className = 'png';
    this.o = document.createElement('div');
    this.o.className = 'thumbnail';
    self.oContainer.appendChild(self.o);
    document.body.appendChild(self.oContainer);
    // pngHandler.transform(self.oContainer);
    self.disabled = 0;
  }
  writeDebug('Thumbnail() - end');
}

function deconstructor() {
/*
  photoViewer.collections = null;
  photoViewer = null;
  thumbnail = null;
*/
}

function setActiveStyleSheet(title) {
  var a;
  for (var i=0; (a=document.getElementsByTagName('link')[i]); i++) {
    if (a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title')) {
      a.disabled = true;
      if (a.getAttribute('title') == title || (a.getAttribute('title').indexOf('default')!=-1)) a.disabled = false;
    }
  }
}

function preInit() {
  if (window.location.toString().toLowerCase().indexOf('debug=')+1) enableDebugMode();
  pngHandler.supportTest(); // writeDebug() dependency
  // set theme (alpha, inverted or white)
  theme += '/';
  themeString = theme.charAt(0).toUpperCase()+theme.substr(1,theme.length-2); // eg. "alpha/" becomes "Alpha"
  setActiveStyleSheet(themeString);
  if (pngHandler.isMac) {
    soundManager = new SoundManagerNull();
  } else {
    soundManager = new SoundManager();
  }
  photoViewer = new PhotoViewer();
  controlBar = new ControlBar();
  thumbnail = new Thumbnail();
  loader = new Loader();
  addEventHandler(window,'load',init);
}

function init() {
  writeDebug('init(): start');
  photoViewer.init();
  thumbnail.init();
  loader.init();
  controlBar.init();
  pngHandler.init();
  controlBar.setTab(searchEngine.query?1:0); // set after PNG transform
  searchEngine.doSearch(searchEngine.query,true);
  writeDebug('init(): end');
}

if (pngHandler.isIE) addEventHandler(window,'unload',deconstructor);
