/* MacStyleDock.js - a function for creating a Mac-OSX-style dock
 *
 * The author of this program, Safalra (Stephen Morley), irrevocably releases
 * all rights to this program, with the intention of it becoming part of the
 * public domain. Because this program is released into the public domain, it
 * comes with no warranty either expressed or implied, to the extent permitted
 * by law.
 *
 * For more public domain JavaScript code by the same author, visit:
 *
 * http://www.safalra.com/web-design/javascript/
 */


/* Creates a MacStyleDock. A MacStyleDock is a row of images that expand as the
 * mouse pointer moves over them. The images are created as children of the
 * specified node with the specified minimum and maximum sizes. Two other
 * parameters specify the images to be used and the range of expansion. The
 * parameters are:
 *
 * node 		- the node at which to create the Mac-style 'dock
 * imageDetails - an array each of whose elements are objects with three
 *				  properties:
 *				  - name	  - the basename of the image
 *				  - sizes	  - an array of pizel sizes available
 *				  - extension - the image extension
 *				  - onclick   - the function to call when the image is clicked
 *				  Requested file names consist of the concatenation of the name
 *				  property, one of the values of the size property, the string
 *				  '-reflection' for reflections, the string '-full' for full
 *				  versions (so captions can be added), and the extension
 *				  property.
 * minimumSize	- the minimum size of icons in the dock
 * maximumSize	- the maximum size of icons in the dock
 * range		- the range of expansion, in icons. This must be an integer.
 */
function MacStyleDock(node, imageDetails, minimumSize, maximumSize, range){
	
  // create a container for the icons and add it to the dock container
  var iconsNode = document.createElement('div');
  node.appendChild(iconsNode);

  // create a container for the reflected icons and add it to the dock container
  var reflectedIconsNode = document.createElement('div');
  node.appendChild(reflectedIconsNode);

  // set the icon containers to centre its contents
  iconsNode.style.textAlign 		 = 'center';
  reflectedIconsNode.style.textAlign = 'center';
  
  // set the height of the dock containers to equal that of the maximised icons
  iconsNode.style.height  = maximumSize + 'px';
  reflectedIconsNode.style.height  = maximumSize + 'px';

  // initialise the maximum width to 0
  var maximumWidth	= 0;

  // initialise the scale factor to 0
  var scale  = 0;
  
  // initialise the time-outs and intervals to 0
  var closeTimeout	= null;
  var closeInterval = null;
  var openInterval	= null;
  
  // create an array to store images
  var images = [];
  
  // create an array to store the DOM nodes of the icons
  var iconNodes = [];
  
  // create an array to store the DOM nodes of the bubbles
  var iconBulles = [];
  
  // create an array to store the DOM nodes of reflections of the icons
  var reflectedIconNodes = [];
  var reflectedSpanIconNodes = [];
  
  // create an array to store the sizes of the icons
  var iconSizes = [];
  
  // loop over the images
  for (var i = 0; i < imageDetails.length; i++){
  
	// create and store a node for the icon for this image
	iconNodes[i] = document.createElement('img');
	
	// position the icon for this image relatively
	iconNodes[i].style.position = 'relative';
	iconNodes[i].style.cursor="hand"
	
	// store the initial size of the icon for this image
	iconSizes[i] = minimumSize;
	
	// create and store a node for the reflected icon for this image
	reflectedSpanIconNodes[i]=document.createElement('div');
	reflectedSpanIconNodes[i].style.cssFloat="left";
	reflectedSpanIconNodes[i].style.position = 'relative';
	reflectedSpanIconNodes[i].style.display = 'inline';
	reflectedSpanIconNodes[i].style.margin='0 0 0 0';
	//reflectedSpanIconNodes[i].style.backgroundColor='white';
	
	reflectedIconsNode.appendChild(reflectedSpanIconNodes[i]);
	
	reflectedIconNodes[i] = document.createElement('img');
	
	//Bubble info
	iconBulles[i]=document.createElement('div');
	
	reflectedSpanIconNodes[i].appendChild(iconBulles[i]);
	reflectedSpanIconNodes[i].appendChild(reflectedIconNodes[i]);
	
	
	iconBulles[i].style.position = 'relative';
	//iconBulles[i].style.cssFloat="left";
	iconBulles[i].style.height='20px';
	//iconBulles[i].style.display = 'inline';
	//iconBulles[i].style.width='100%';
	//iconBulles[i].style.top=parseInt(iconNodes[i].getAttribute('height'))+parseInt(iconNodes[i].getAttribute('top'))+"px";
	iconBulles[i].style.color='white';
	iconBulles[i].style.textAlign='center';
	//iconBulles[i].oParent=reflectedSpanIconNodes[i];
	//iconBulles[i].style.backgroundColor='red';
	//iconBulles[i].innerHTML=imageDetails[i].title;
		
	
	
	
	// update the properties of the icon for this image
	updateIconProperties(i);
	
	// add the span for this image to the dock
	iconsNode.appendChild(iconNodes[i]);
	
	// add the appropriate event listeners to the icon for this image
	if (iconNodes[i].addEventListener){
	  iconNodes[i].addEventListener('mousemove', processMouseMove, false); 
	  iconNodes[i].addEventListener('mouseout', processMouseOut, false);
	  iconNodes[i].addEventListener('click', imageDetails[i].onclick, false);
	}else if (iconNodes[i].attachEvent){
	  iconNodes[i].attachEvent('onmousemove', processMouseMove);
	  iconNodes[i].attachEvent('onmouseout', processMouseOut);
	  iconNodes[i].attachEvent('onclick', imageDetails[i].onclick);
	}
	
   
	
	// loop over the sizes available for this image
	for (var j = 0; j < imageDetails[i].sizes.length; j++){
	
	  // create a DOM node containing this image at this size 
	  var image = document.createElement('img');
	  image.setAttribute(
		  'src',
		  imageDetails[i].name
			  + imageDetails[i].sizes[j]
			  + imageDetails[i].extension);
			  
			  
	  // add the DOM node to the array of stored images
	  images.push(image);
	  
	}
	
	  
  }
  

  /* Sets a toolbar image to the specified size. The parameter is:
   *
   * index - the 0-based index of the image to be sized
   */
  function updateIconProperties(index){
  
	// determine the size for the icon, taking into account the scale factor
	var size = minimumSize + scale * (iconSizes[index] - minimumSize);
	
	// find the index of the appropriate image size
	var sizeIndex = 0;
	while (imageDetails[index].sizes[sizeIndex] < size
		&& sizeIndex + 1 < imageDetails[index].sizes.length){
	  sizeIndex++;
	}

	// check whether the full icon with its caption should be displayed
	if (size == maximumSize){
	
	  // set the src attribute of the image for the icon
	  iconNodes[index].setAttribute('src',
		  imageDetails[index].name
			  + maximumSize
			  + '-full'
			  + imageDetails[index].extension);
			  
			  
			  iconBulles[index].style.visibility="visible";
			  iconBulles[index].style.display="block";
			  iconBulles[index].innerHTML=imageDetails[index].title;
	
	}else{
	
	  // set the src attribute of the image for the icon
	  iconNodes[index].setAttribute('src',
		  imageDetails[index].name
			  + imageDetails[index].sizes[sizeIndex]
			  + imageDetails[index].extension);
	  
			  iconBulles[index].style.visibility="hidden";
			  iconBulles[index].style.display="none";
			  iconBulles[index].innerHTML="";
	}
	
	// set the src attribute of the image for the icon's reflection
	reflectedIconNodes[index].setAttribute('src',
		imageDetails[index].name
			+ imageDetails[index].sizes[sizeIndex]
			+ '-reflection'
			+ imageDetails[index].extension);
			
	// set the width and height of the image for the icon and its reflection
	iconNodes[index].setAttribute('width',	size);
	iconNodes[index].setAttribute('height', size);
	reflectedIconNodes[index].setAttribute('width',  size);
	reflectedIconNodes[index].setAttribute('height', size);
	
	// set the top margin of the image for the icon
	iconNodes[index].style.marginTop = (maximumSize - size) + 'px';
	reflectedIconNodes[index].style.marginBottom = (maximumSize - size) + 'px';
	
  }

  /* Processes a mousemove event on an image in the 'dock'. The parameter is:
   *
   * e - the event object. window.event will be used if this is undefined.
   */
  function processMouseMove(e){
   
	// clear the closing interval and time-out
	window.clearTimeout(closeTimeout);
	closeTimeout = null;
	window.clearInterval(closeInterval);
	closeInterval = null;
	
	// check that the opening interval is required but does not yet exist
	if (scale != 1 && !openInterval){
	
	  // create the opening interval
	  openInterval = window.setInterval(
		  function(){
			if (scale < 1) scale += 0.125;
			if (scale >= 1){
			  scale = 1;
			  window.clearInterval(openInterval);
			  openInterval = null;
			}
			for (var i = 0; i < iconNodes.length; i++){
			  updateIconProperties(i);
			}
		  },
		  20);
		  
	}
	
	// set the event object if the browser does not supply it
	if (!e) e = window.event;
	
	// find the DOM node on which the mouseover event occured
	var target = e.target || e.srcElement;
	
	// obtain the index of the icon on which the mouseover event occured
	var index = 0;
	while (iconNodes[index] != target) index++;
	
	// obtain the fraction across the icon that the mouseover event occurred
	var across = (e.layerX || e.offsetX) / iconSizes[index];
	
	// check a distance across the icon was found (in some cases it will not be)
	if (across){
	
	  // initialise the current width to 0
	  var currentWidth = 0;
	
	  // loop over the icons
	  for (var i = 0; i < iconNodes.length; i++){
	  
		// check whether the icon is in the range to be resized
		if (i < index - range || i > index + range){
		
		  // set the icon size to the minimum size
		  iconSizes[i] = minimumSize;
		  
		}else if (i == index){
		
		  // set the icon size to be the maximum size
		  iconSizes[i] = maximumSize;
		
		}else if (i < index){
		
		  // set the icon size to the appropriate value
		  iconSizes[i] =
			  minimumSize
			  + Math.round(
				  (maximumSize - minimumSize - 1)
				  * (
					  Math.cos(
						  (i - index - across + 1) / range * Math.PI)
					  + 1)
				  / 2);
		  
		  // add the icon size to the current width
		  currentWidth += iconSizes[i];
		
		}else{
		
		  // set the icon size to the appropriate value
		  iconSizes[i] =
			  minimumSize
			  + Math.round(
				  (maximumSize - minimumSize - 1)
				  * (
					  Math.cos(
						  (i - index - across) / range * Math.PI)
					  + 1)
				  / 2);
		  
		  // add the icon size to the current width
		  currentWidth += iconSizes[i];
		
		}
		
	   
	  }
	  
	  // update the maximum width if necessary
	  if (currentWidth > maximumWidth) maximumWidth = currentWidth;
	  
	  // detect if the total size should be corrected
	  if (index >= range
		  && index < iconSizes.length - range
		  && currentWidth < maximumWidth){

		// correct the size of the smallest magnified icons
		iconSizes[index - range] += Math.floor((maximumWidth - currentWidth) / 2);
		iconSizes[index + range] += Math.ceil((maximumWidth - currentWidth) / 2);
	  
	  }
	  
	  // update the sizes of the images
	  for (var i = 0; i < iconNodes.length; i++) updateIconProperties(i);
	  
	}

	
  }

  // Processes a mouseout event on an image in the dock.
  function processMouseOut(){
  
	  infobulle.style.display='none';
	  
	// check that neither the closing interval nor time-out are set
	if (!closeTimeout && !closeInterval){
	
	  // create the closing time-out
	  closeTimeout = window.setTimeout(
		  function(){
			closeTimeout = null;
			if (openInterval){
			  window.clearInterval(openInterval);
			  openInterval = null;
			}
			closeInterval = window.setInterval(
				function(){
				  if (scale > 0) scale -= 0.125;
				  if (scale <= 0){
					scale = 0;
					window.clearInterval(closeInterval);
					closeInterval = null;
				  }
				  for (var i = 0; i < iconNodes.length; i++){
					updateIconProperties(i);
				  }
				},
				20);
		  },
		  100);
		  
	}
	
  }

}
