	  // Copyright (c) 2011 Willem Beutels
	  
	  var debug = 0;
	  var logMsg = '';
	  var _matrix;
	  var _rotatingObjects = [];
	  var _collectionImagesObject = null;

	  function initMatrix(columnCount, rowCount, cellWidth, cellHeight, containerId) {
	    _matrix = _createMatrixObject(rowCount * columnCount, columnCount, containerId);
	    _matrix.initCells(0, 0, cellWidth, cellHeight, 0, 0);
	  }
	  
	  function showImageViewer() {
	    if (_collectionImagesObject.collectionImageIndex != -1) {
		  _collectionImagesObject.clear();
		}
		
		_collectionImagesObject.init();
	  }
	  
	  function initImageViewer(imageArrayObjects, collectionImageArrayStartCellIndex, backCellIndex, forwardCellIndex, backImagePath, forwardImagePath) {
	  	_collectionImagesObject = new Object();
						
		_collectionImagesObject.collectionImageArray = imageArrayObjects;
		_collectionImagesObject.collectionImageArrayStartCellIndex = collectionImageArrayStartCellIndex; 
		_collectionImagesObject.navigationImagesObject = createImageArrayObject('', [backImagePath, forwardImagePath], ['javascript:_collectionImagesObject.prevImage();', 'javascript:_collectionImagesObject.nextImage();']);
		_collectionImagesObject.collectionImageIndex = -1;

		_collectionImagesObject.init = function() {
		  _collectionImagesObject.collectionImageIndex = 0;
		  
		  if (imageArrayObjects.length > 1) {
		    _collectionImagesObject.navigationImagesObject.setImage(forwardCellIndex, 1);
		  }

		  _collectionImagesObject.collectionImageArray[0].setAllSequence(_collectionImagesObject.collectionImageArrayStartCellIndex[0]);		  
		}
		
		_collectionImagesObject.clear = function() {
		  if (_collectionImagesObject.collectionImageIndex != -1) {
		    _collectionImagesObject.collectionImageArray[_collectionImagesObject.collectionImageIndex].clearAllCells();
		  }

          _collectionImagesObject.navigationImagesObject.clearAllCells();
		  _collectionImagesObject.collectionImageIndex = -1;		  
		}        
		
		_collectionImagesObject.nextImage = function() {
		  if (_collectionImagesObject.collectionImageIndex != -1) {
		    _collectionImagesObject.collectionImageArray[_collectionImagesObject.collectionImageIndex].clearAllCells();

            if (_collectionImagesObject.collectionImageIndex == 0) {
              _collectionImagesObject.navigationImagesObject.setImage(backCellIndex, 0);
	        } 

		    _collectionImagesObject.collectionImageIndex++;
		    _collectionImagesObject.collectionImageArray[_collectionImagesObject.collectionImageIndex].setAllSequence(_collectionImagesObject.collectionImageArrayStartCellIndex[_collectionImagesObject.collectionImageIndex]);

		    if (_collectionImagesObject.collectionImageIndex == _collectionImagesObject.collectionImageArray.length - 1) {
		      _collectionImagesObject.navigationImagesObject.clearImage(1);
		    }
		  }
		}
		
		_collectionImagesObject.prevImage = function() {
		  if (_collectionImagesObject.collectionImageIndex >= 0) {
		    _collectionImagesObject.collectionImageArray[_collectionImagesObject.collectionImageIndex].clearAllCells();
		    _collectionImagesObject.collectionImageIndex--;
		    _collectionImagesObject.collectionImageArray[_collectionImagesObject.collectionImageIndex].setAllSequence(_collectionImagesObject.collectionImageArrayStartCellIndex[_collectionImagesObject.collectionImageIndex]);
				  
		    if (_collectionImagesObject.collectionImageIndex == 0) {
		      _collectionImagesObject.navigationImagesObject.clearImage(0);
		    }
		  
		    if (_collectionImagesObject.collectionImageIndex == (_collectionImagesObject.collectionImageArray.length - 2)) {
		      _collectionImagesObject.navigationImagesObject.setImage(forwardCellIndex, 1);
		    }
		  }
		}
	  }
	  
	  function clearMatrix() {
		_matrix.clearAllCells();
	  }
	  
	  function stopContinuousRandomRotateAll() {
	    var rotatingObjects = [];
		
	    for (var i=0; i<_rotatingObjects.length; i++) {
		  rotatingObjects.push(_rotatingObjects[i]);
		}
		
		for (var i=0; i<rotatingObjects.length; i++) {
		  rotatingObjects[i].stopContinuousRandomRotate();
		}
	  }
	  
	  function setKeepEmptyCellsOnRotate(cellCount) {
	    _matrix.rotateKeepEmptyCells = cellCount;
	  }
	  
	  /**
	  * Create a matrix object
	  **/
	  function _createMatrixObject(cellSize, columnSize, containerId) {
	    //var _body = document.getElementsByTagName('body') [0];
		var container = document.getElementById(containerId);
		container.style.height = '100%';
		
		container.style.textAlign = 'center';
		var logDiv = document.createElement('div');
		logDiv.setAttribute('id', '_log');
		logDiv.style.position = 'fixed';
		logDiv.style.top = '0px';
		logDiv.style.right = '0px';
		container.appendChild(logDiv);
		
	    var matrixObject = new Object();
	  	matrixObject.cellSize = cellSize;
	    matrixObject.columnSize = columnSize;
		matrixObject.cellOwner = [];
        matrixObject.rotateKeepEmptyCells = 0;
		
		for (var i=0; i<cellSize; i++) {
		  matrixObject.cellOwner.push(null);
		}

		matrixObject.initCells = function(offsetLeft, offsetTop, cellWidth, cellHeight, cellSpacing) {
		  var matrixHeight = matrixObject.cellSize / matrixObject.columnSize * cellHeight;
		  var matrixWidth = matrixObject.columnSize * cellWidth; 
		  var vert = document.createElement('div');
		  vert.style.cssFloat = 'left';
		  
		  //for IE:
		  vert.style.styleFloat = 'left';
		  
		  vert.style.height = '50%';
		  vert.style.width = '100%';
	      vert.style.marginTop = (matrixHeight / 2 * -1) + 'px';
		  var hor = document.createElement('div');
		  hor.style.marginLeft = 'auto';
	      hor.style.marginRight = 'auto';
	      hor.style.textAlign = 'left';
	      hor.style.clear = 'both';
		  hor.style.position = 'relative';
		  hor.style.width = matrixWidth + 'px';
		  hor.style.height = matrixHeight + 'px';
		  container.appendChild(vert);
		  container.appendChild(hor);
		  
		  var top = -1;
		  var left = offsetLeft;

		  for (var i=0;i<matrixObject.cellSize;i++) {		
		    if (top == -1) {
		      top = offsetTop;
		    } else {
		      if ((i % matrixObject.columnSize) == 0) {
		        left = offsetLeft;
			    top += (cellHeight + cellSpacing);
		      } else {
		        left += (cellWidth + cellSpacing);
		      }
		    }
			
			var cell = document.createElement('div');
			cell.setAttribute('id','_cell' + i);
			cell.style.position = 'absolute';
		    cell.style.top = top + 'px';
  		    cell.style.left = left + 'px';
			hor.appendChild(cell);
		  }
	    }
		
		matrixObject.clearAllCells = function() {
	      for (var i=0; i<matrixObject.cellOwner.length; i++) {
		    if (matrixObject.cellOwner[i] != null) {
		      matrixObject.cellOwner[i].clearCell(i);
		    }
		  }
	    }
		
		matrixObject.getEmptyCellCount = function() {
		  var emptyCellCount = 0;
		  
		  for (var i=0; i<matrixObject.cellSize; i++) {
		    if (matrixObject.cellOwner[i] == null) {
              emptyCellCount++;
			}
		  }
		  
		  return emptyCellCount;
		}
		
		return matrixObject;
	  }

	  /**
	  * Create an image array object
	  **/
	  function createImageArrayObject(imageBaseDir, imageFilenameArray, imageHrefArray, columnSize) {
		var arrayObject = new Object();
		arrayObject.baseDir = imageBaseDir;
		arrayObject.filenames = imageFilenameArray;
		arrayObject.href = imageHrefArray;
		arrayObject.assignedToCellIndex = [];
		arrayObject.columnSize = columnSize;
		arrayObject.rotateInterval = -1;
		arrayObject.rotateTimer;
		
		for (var i=0; i<imageFilenameArray.length; i++) {
		  arrayObject.assignedToCellIndex.push(-1);
		}

		arrayObject.startContinuousRandomRotate = function(intervalMilliSeconds) {
		  if (intervalMilliSeconds != undefined) {
		    if (arrayObject.rotateInterval != -1) {
			  return;
			}
			
		    arrayObject.rotateInterval = intervalMilliSeconds;
			_rotatingObjects.push(arrayObject);
		  } else {
		    arrayObject.doRandomRotate();
		  }

		  arrayObject.rotateTimer = setTimeout(function(){arrayObject.startContinuousRandomRotate()}, arrayObject.rotateInterval);
		}

		arrayObject.stopContinuousRandomRotate = function() {
		  if (arrayObject.rotateInterval != -1) {
		    clearTimeout(arrayObject.rotateTimer);
			arrayObject.rotateInterval = -1;
			_rotatingObjects.splice(_rotatingObjects.indexOf(arrayObject), 1);
		  }
		}
		
		arrayObject.cellInUse = function(cellIndex) {
		  return (arrayObject.assignedToCellIndex.indexOf(cellIndex) != -1);
		}
		
		arrayObject.clearCell = function(cellIndex) {
		  if (arrayObject.cellInUse(cellIndex)) {
		    var el = document.getElementById('_cell' + cellIndex);
			el.innerHTML = '';
		    _matrix.cellOwner[cellIndex] = null;
			var imgIndex = arrayObject.assignedToCellIndex.indexOf(cellIndex);

			if (imgIndex != -1) {
			  arrayObject.assignedToCellIndex[imgIndex] = -1;
			}
		  }
		}
		
		arrayObject.clearImage = function(imgIndex) {
		  var cellIndex = arrayObject.assignedToCellIndex[imgIndex];
		  
		  if (cellIndex != -1) {
		    arrayObject.clearCell(cellIndex);
		  }
		}
		
		arrayObject.clearRandomImage = function() {
		  var assignedImages = [];
		  
		  for (var i=0; i<arrayObject.assignedToCellIndex.length; i++) {
		    if (arrayObject.assignedToCellIndex[i] != -1) {
			  assignedImages.push(i);
			}
		  }
		  
		  var imgIndex = assignedImages[Math.floor(Math.random()*assignedImages.length)];
		  arrayObject.clearCell(arrayObject.assignedToCellIndex[imgIndex]);
		}
		
		arrayObject.clearAllCells = function() {
		  for (var i=0; i<arrayObject.assignedToCellIndex.length; i++) {
		    if (arrayObject.assignedToCellIndex[i] != -1) {
			  arrayObject.clearCell(arrayObject.assignedToCellIndex[i]);
			}
		  }
		}
		
		arrayObject.setAllSequence = function(startCellIndex) {
		  if (arrayObject.columnSize != undefined) {
		    var availableColumnSize = _matrix.columnSize - (startCellIndex % _matrix.columnSize);
			var cellIndex = startCellIndex;
			var columnCount = 1;
			
			for (var i=0; i<arrayObject.filenames.length; i++) {
			  if (cellIndex >= _matrix.cellSize) {
			    break;
			  }

			  arrayObject.setImage(cellIndex, i);
			  
			  if (columnCount >= arrayObject.columnSize) {
                columnCount = 1;
				cellIndex += _matrix.columnSize - arrayObject.columnSize + 1;  
			  } else if (columnCount >= availableColumnSize) {
			    columnCount = 1;
			    i+= (arrayObject.columnSize - availableColumnSize);
				cellIndex += _matrix.columnSize - availableColumnSize + 1;
			  } else {
			    columnCount++;
			    cellIndex++;
			  }
			}
		  }
		}

		arrayObject.setAllRandom = function() {
		  for (var i=0; i<arrayObject.filenames.length; i++) {
            arrayObject.doRandomRotate();		    
		  }
		}
		
		arrayObject.doRandomRotate = function() {
          var cellIndex = getRandomUnreservedCell();

	      if (cellIndex != -1) {
			var cellInUse = arrayObject.cellInUse(cellIndex);		    
		    var imgIndex = getRandomNonAssignedImgIndex();
			
			if (cellInUse) {
			  if (imgIndex != -1) {
			    arrayObject.setImage(cellIndex, imgIndex);  
			  } else {
			    var img1Index = Math.floor(Math.random()*arrayObject.assignedToCellIndex.length);
			    var cell1Index = arrayObject.assignedToCellIndex[img1Index];
				var img2Index = Math.floor(Math.random()*arrayObject.assignedToCellIndex.length);
			    var cell2Index = arrayObject.assignedToCellIndex[img2Index];
				arrayObject.setImage(cell2Index, img1Index);
			    arrayObject.setImage(cell1Index, img2Index)
			  }
			} else {
			  if (imgIndex != -1) {
			    if (_matrix.getEmptyCellCount() <= _matrix.rotateKeepEmptyCells) {
				  var img1Index = Math.floor(Math.random()*arrayObject.assignedToCellIndex.length);
				  arrayObject.clearRandomImage();
				}
				arrayObject.setImage(cellIndex, imgIndex);
			  } else {  
                var img1Index = Math.floor(Math.random()*arrayObject.assignedToCellIndex.length);
			    var cell1Index = arrayObject.assignedToCellIndex[img1Index];
			    arrayObject.clearCell(cell1Index);
				arrayObject.setImage(cellIndex, img1Index);
			  }
			}
		  }
        }
		
		arrayObject.setImage = function(cellIndex, imgIndex) {
		  var currentImgIndex = arrayObject.assignedToCellIndex.indexOf(cellIndex);

		  if (currentImgIndex != -1) {
		    arrayObject.assignedToCellIndex[currentImgIndex] = -1;
		  } else if (_matrix.cellOwner[cellIndex] != null) {
		    _matrix.cellOwner[cellIndex].clearCell(cellIndex);
		  }

		  var el = document.getElementById('_cell' + cellIndex);
		  var href;

		  if (arrayObject.href != undefined && arrayObject.href.length > 0) {
		    var hrefIndex = 0;
			
			if (arrayObject.href[imgIndex] != undefined) {
			  hrefIndex = imgIndex;
			}
		  
		    if (arrayObject.href[hrefIndex] != undefined && arrayObject.href[hrefIndex] != '') {
			  href = arrayObject.href[hrefIndex];
			}
		  }
		  
		  var imgContent = '<img src="' + arrayObject.baseDir + arrayObject.filenames[imgIndex] + '" alt="" border="0" />';
		  var content; 
		  
		  if (href != undefined) {
			content = '<a href=' + href + '>' + imgContent + '</a>';
		  } else {
		    content = imgContent;
		  }

		  //set opacity to 0 before setting content to avoid flicker effect
		  el.style.opacity = 0;
		  el.style.filter='alpha(opacity=0)'; 
          el.innerHTML = content;
		  Effect.Fade('_cell' + cellIndex, { duration: 2.0, from: 0, to: 1 });
		  
          arrayObject.assignedToCellIndex[imgIndex] = cellIndex;
		  _matrix.cellOwner[cellIndex] = arrayObject;
		}

		function getRandomUnreservedCell() {
		  var returnVal = -1;
	      var empty = [];
	      var owned = [];
			
		  for (var i=0; i<_matrix.cellSize; i++) {
		    if (_matrix.cellOwner[i] == null) {
              empty.push(i);
			} else if (_matrix.cellOwner[i] == arrayObject) {
			  owned.push(i);			  
			}
		  }

		  if (empty.length > 0) {
			returnVal = empty[Math.floor(Math.random()*empty.length)];
	      } else if (owned.length > 0) {			
		    returnVal = owned[Math.floor(Math.random()*owned.length)];
		  }

		  return returnVal;
		}		
		
		function getRandomNonAssignedImgIndex() {
		  var returnVal = -1;
		  var unAssigned = [];
		
          for (var i=0; i<arrayObject.assignedToCellIndex.length; i++) {
		    if (arrayObject.assignedToCellIndex[i] == -1) {
		      unAssigned.push(i);
		    }
		  }
		
		  if (unAssigned.length > 0) {
		    returnVal = unAssigned[Math.floor(Math.random()*unAssigned.length)];
		  }
		
		  return returnVal;
		}
		
		return arrayObject;
	  }
	  	  
	  function log(msg) {
	    if (debug == 1) {
		  logMsg += msg + '<br />';
		  var logDiv = document.getElementById('_log');
		  
		  if (logDiv) {
		    logDiv.innerHTML = logMsg;
		  }
		}
	  }

