function PWZoomer() {
	// DIVs
	this.zoomImageThumbnail = null;
	this.thumbnailsDiv = null;
	this.nagnifier = null;
	this.zoomOverlay = null;
	this.zoomView = null;
	this.viewPort = null;	// viewPort div

	// Magnify can be 'auto' or number (2 means twice big as thumbnail)
	this.zoomMultiply = 'auto';
	this.zoomMultiplyOnClick = null;
	this.zoomViewLastSize = new Array(0,0);  // Original size of last opened image
	this.zoomClick = false;
	this.enableWheel = false;
	this._onWheel = Function();

	// Drag objects - subscribed to Drag script
	this.dragView = null;
	this.dragMagnifier = null;
	
  // Custom icons handling
  this.autoIconHandling = true;
  this._margins = new Array(10, 10);
  
	// Lens
	this.lensType = 'both';	// values= 'click', 'lens', 'both'
	this.lensZoomInImagePath = './img/zoom_in.png';
	this.lensZoomOutImagePath = './img/zoom_out.png';
	this.lensImageIn = null;
	this.lensImageOut = null;
  this._maxZoom = null; // Internal temporary value
  this._minZoom = null; // Internal temporary value
	
	// Arrows - prev, next
	this.arrowsShow = true;
	this.arrowPrevPath = './img/arrow_prev.png';
	this.arrowNextPath = './img/arrow_next.png';
	this.arrowPrev = null;
	this.arrowNext = null;
	
	// Images
	this.waitingImage = './img/loading.gif';
	this.loading = null;	// Image object
	
	// Load
	// Call post load when all these methods were called
	this.requiredLoad = new Array('preLoad', 'onArrowPrevLoad', 'onArrowNextLoad');
	
	this._customEvents = new Array(); // key : operation; value : function; allowed keys: 'postLoad', 'preLoad', 'onZoomIn', 'onZoomOut', 'onZoomPlus', 'onZoomMinus', 'onImageChange', 'onWheel'
	
	// Debugging	// Module: DEBUG
	this.debugEnabled = false;	// Module: DEBUG
	this.debugDiv = null;	// Module: DEBUG
	this.debugCallDiv = null;	// Module: DEBUG
	this._debugPause = false;	// Module: DEBUG

	//imagesArray : new Array(),

	this.init = function(magnifier, ZToverlay, ZTview, zoomTn)
	{
		this._debugCalls("init", magnifier, ZToverlay, ZTview, zoomTn);	// Module: DEBUG

		var instance = this;
		
    this.addEvent(window, 'load', function()
    {
  		instance.onload(magnifier, ZToverlay, ZTview, zoomTn);
    });
  }
  
  this.onload = function(magnifier, ZToverlay, ZTview, zoomTn)
  {
		this._debugCalls("onload", magnifier, ZToverlay, ZTview, zoomTn);	// Module: DEBUG

		var instance = this;

    // Setup the mouse wheel operation
		this._onWheel = function(e) { instance.onWheel(e); }
		
		// Get the elements by ID and store them in properties
		this.magnifier = this.getElement(magnifier);
		this.zoomOverlay = this.getElement(ZToverlay);
		this.zoomView = this.getElement(ZTview);
		this.thumbnailsDiv = this.getElement(zoomTn);
		this.zoomOverlay.style.visibility = "hidden";
		this.viewPort = this.zoomView.parentNode;

		// Move zoomView to new inner div in viewPort
		var div = document.createElement('div');
		div.setAttribute('class', 'view');
		div.style.position = 'relative';
		this.viewPort.appendChild(div);
		div.appendChild(this.zoomView);

    this.fixMedium();

		this.preLoad();
	},
	
	this.preLoad = function()
	{
		this._debugCalls("preLoad");	// Module: DEBUG
		
		var instance = this;

		// Preload 'Loading' icon, set the necesarry parameters
		var t = new Image();
		t.style.visibility = 'hidden';
		t.style.position = 'absolute';
		t.setAttribute('class','zoomerLoading');
		t.zindex = 100;
		t.src = this.waitingImage;
		this.loading = t;
		this.viewPort.appendChild(t);
		
		// Preload 'Zoom in' icon, set the necesarry parameters
		var t = new Image();
		t.onload = function(e)
			{
				instance.onZoomInLoad(e);
			}
		t.style.visibility = 'hidden';
		t.style.position = 'absolute';
		t.setAttribute('class','zoomIn');
		t.onclick = function()
			{
				instance.zoomView.onclick();
				return false;
			};
		t.zindex = 100;
		t.src = this.lensZoomInImagePath;
		this.viewPort.appendChild(t);
		this.lensImageIn = t;

		// Preload 'Zoom out' icon, set the necesarry parameters
		var t = new Image();
		t.onload = function(e)
			{
				instance.onZoomOutLoad(e);
			}
		t.style.visibility = 'hidden';
		t.style.position = 'absolute';
		t.setAttribute('class','zoomOut');
		t.onclick = function()
			{
				instance.zoomOut();
				//instance.disableZoom();
				return false;
			};
		t.zindex = 100;
		t.src = this.lensZoomOutImagePath;
		this.viewPort.appendChild(t);
		this.lensImageOut = t;

		// Preload 'Previous arrow' icon, set the necesarry parameters
		var t = new Image();
		this.arrowPrev = t;
		t.onload = function(e)
			{
				instance.onArrowPrevLoad(e);
			}
		t.style.visibility = 'hidden';
		t.style.position = 'absolute';
		t.setAttribute('class','arrowPrev');
		t.onclick = function()
			{
				instance.gotoPrev();
				return false;
			};
		t.zindex = 100;
		t.src = this.arrowPrevPath;
		this.viewPort.appendChild(t);

		// Preload 'Next arrow' icon, set the necesarry parameters
		var t = new Image();
		this.arrowNext = t;
		t.onload = function(e)
			{
				instance.onArrowNextLoad(e);
			}
		t.style.visibility = 'hidden';
		t.style.position = 'absolute';
		t.setAttribute('class','arrowNext');
		t.onclick = function()
			{
				instance.gotoNext();
				return false;
			};
		t.zindex = 100;
		t.src = this.arrowNextPath;
		this.viewPort.appendChild(t);
		
		this.raise('preLoad');
		
		// Remove preLoad from required operations
		this.removeLoadItems('preLoad');
	}
	
	this.postLoad = function()
	{
		var s = this.thumbnailsDiv.getElementsByTagName("a");
		var img = s[0].getElementsByTagName("img");
		this.zoomImageThumbnail = img[0];

		this.recreateEvents();

		var instance = this;
		this.zoomView.onclick = function()
			{
				instance.initNewImage(instance.zoomImageThumbnail, instance.zoomImageThumbnail.parentNode.href);
				instance.zoomView.onclick = Function();
			};
		this.showMagnifyLens(true, 'in');
		this.showArrows();
		//this.arrowPrev.style.visibility = 'hidden';

		this.raise('postLoad');
	}
	
	this.onArrowPrevLoad = function()
	{
		this._debugCalls("onArrowPrevLoad");	// Module: DEBUG
		
		try
		{
		  if(this.autoIconHandling)
		  {
        this.arrowPrev.style.left = this._margins[0] + "px";
        this.arrowPrev.style.top = ((this.viewPort.offsetHeight - this.arrowPrev.offsetHeight) / 2) + "px";
			}
		}
		catch(err)
		{
			this._debug("An exception occurred in the script. Error name: " + err.name + ". Error message: " + err.message);	// Module: DEBUG
		}
		
		this.removeLoadItems('onArrowPrevLoad');
	}
	
	this.onArrowNextLoad = function()
	{
		this._debugCalls("onArrowNextLoad");	// Module: DEBUG
		
		try
		{
		  if(this.autoIconHandling)
		  {
  			this.arrowNext.style.left = (this.viewPort.offsetWidth - this._margins[0] - this.arrowNext.offsetWidth) + "px";
  			this.arrowNext.style.top = ((this.viewPort.offsetHeight - this.arrowNext.offsetHeight) / 2) + "px";
  		}
		}
		catch(err)
		{
			this._debug("An exception occurred in the script. Error name: " + err.name + ". Error message: " + err.message);	// Module: DEBUG
		}

		this.removeLoadItems('onArrowNextLoad');
	}

	this.onZoomInLoad = function()
	{
		this._debugCalls("onZoomInLoad");	// Module: DEBUG
		
		try
		{
		  if(this.autoIconHandling)
		  {
  			this.lensImageIn.style.left = (this.viewPort.offsetWidth - this._margins[0] - this.lensImageIn.offsetWidth) + "px";
  			this.lensImageIn.style.bottom = (this._margins[1]) + "px";
			}
		}
		catch(err)
		{
			this._debug("An exception occurred in the script. Error name: " + err.name + ". Error message: " + err.message);	// Module: DEBUG
		}
		
		this.removeLoadItems('onZoomInLoad');
	}

	this.onZoomOutLoad = function()
	{
		this._debugCalls("onZoomOutLoad");	// Module: DEBUG
		
		try
		{
		  if(this.autoIconHandling)
		  {
  			this.lensImageOut.style.left = (this.viewPort.offsetWidth - this._margins[0] - this.lensImageIn.offsetWidth) + "px";
  			this.lensImageOut.style.bottom = (this._margins[1]) + "px";
			}
		}
		catch(err)
		{
			this._debug("An exception occurred in the script. Error name: " + err.name + ". Error message: " + err.message);	// Module: DEBUG
		}
		
		this.removeLoadItems('onZoomOutLoad');
	}
	
	this.showArrows = function()
	{
		this._debugCalls("showArrows");	// Module: DEBUG

		if(this.zoomImageThumbnail == null)
			return;
		
		// Check if next arrow should be visible
		var e2 = this.findNextSibling(this.zoomImageThumbnail.parentNode, 'a');
		if(e2 == null)
			this.arrowNext.style.visibility = 'hidden';
		else
			this.arrowNext.style.visibility = 'visible';

		// Check if previous arrow should be visible
		var e2 = this.findPreviousSibling(this.zoomImageThumbnail.parentNode, 'a');
		if(e2 == null)
			this.arrowPrev.style.visibility = 'hidden';
		else
			this.arrowPrev.style.visibility = 'visible';
	}

  ///
  // Can change zoom behaviour, where the zoom must be click to zoom in
	this.zoomType = function(type, lens)
	{
		this._debugCalls("zoomType");	// Module: DEBUG
		
		if(type == 'click' || type == 'lens' || type == 'both')
			this.lensType = type;
		
		this.lensImage = lens;
	}

  ///
  // recreate the events for thumbnails
	this.recreateEvents = function()
	{
		this._debugCalls("recreateEvents");	// Module: DEBUG
		
		var instance = this;
		
		var s = this.thumbnailsDiv.getElementsByTagName("a");
		this.zoomView.onclick = Function();
		for (var i=0;i<s.length;i++)  
		{
			var img = (s[i].getElementsByTagName("img"))[0];
			s[i].style.display = "block";
			s[i].style.position = "relative";
			s[i].style.width = img.offsetWidth;
			s[i].style.height = img.offsetHeight;
			s[i].onclick = Function("return false;");

			if(s[i].rel == null || s[i].rel == "" || s[i].rel == "undefined")
			{
				// No medium image
				img.onclick = function()
					{
						//instance.zoomImageThumbnail = this;
						//instance.recreateEvents();
						instance.initNewImage(this, instance.zoomImageThumbnail.parentNode.href);
						return false;
					};
			}
			else
			{
				// Medium image
				img.onclick = function()
					{
						instance.changeImage(this);
						//instance.zoomView.src = instance.zoomImageThumbnail.parentNode.rel;
						instance.zoomView.onclick = function()
							{
								instance.initNewImage(instance.zoomImageThumbnail, instance.zoomImageThumbnail.parentNode.href);
								instance.zoomView.onclick = Function();
							};
							return false;
					};
			}
		}
		
		//this.showArrows();	// Change visibility of the arrows
	}
	
	this.showMagnifyLens = function(show, type)
	{
		this._debugCalls("showMagnifyLens");	// Module: DEBUG

		if(this.zoomImageThumbnail != null)
		{
			var s = this.zoomImageThumbnail.parentNode;
			if(s == "undefined" || s.rel == null || s.rel == "" || s.rel == "undefined")
				show = false;
		}
		
		if(show == null || show == true)
		{
			//this.lensImageOut.style.left = (this.viewPort.offsetWidth - this.margins[0] - this.lensImageOut.offsetWidth) + "px";
			//this.lensImageOut.style.top = (this.viewPort.offsetHeight - this.margins[1] - this.lensImageOut.offsetHeight) + "px";
			//this.lensImageIn.style.left = (this.viewPort.offsetWidth - this.margins[0] - this.lensImageIn.offsetWidth) + "px";
			//this.lensImageIn.style.top = (this.viewPort.offsetHeight - this.margins[1] - this.lensImageIn.offsetHeight) + "px";

			if(type == 'in')
			{
				this.lensImageOut.style.visibility = "hidden";
				this.lensImageIn.style.visibility = "visible";
			}
			else
			{
				this.lensImageOut.style.visibility = "visible";
				this.lensImageIn.style.visibility = "hidden";
			}
		}
		else
		{
			this.lensImageOut.style.visibility = "hidden";
			this.lensImageIn.style.visibility = "hidden";
		}
	}
	
	this.gotoPrev = function()
	{
		this._debugCalls("gotoPrev");	// Module: DEBUG
		
		if((e2 = this.findPreviousSibling(this.zoomImageThumbnail.parentNode, 'a')) != null)
			(e2.getElementsByTagName("img"))[0].onclick();
	}
	
	this.gotoNext = function()
	{
		this._debugCalls("gotoNext");	// Module: DEBUG

		if((e2 = this.findNextSibling(this.zoomImageThumbnail.parentNode, 'a')) != null)
			(e2.getElementsByTagName("img"))[0].onclick();
	}
	
	this.zoomOut = function()
	{
		this._debugCalls("zoomOut");	// Module: DEBUG
		
		this.recreateEvents();
		this.changeImage(this.zoomImageThumbnail, false);
    this.disableZoom();
    this.fixMedium();
	
		this.raise('onZoomOut');
	}

	this.enableZoom = function()
	{
		this._debugCalls("enableZoom");	// Module: DEBUG

		//this.recreateEvents();
		this.zoomOverlay.style.visibility = "visible";
	}
	
	this.disableZoom = function()
	{
		this._debugCalls("disableZoom");	// Module: DEBUG

    var instance = this;

		this.zoomOverlay.style.visibility = "hidden";
		if(this.zoomImageThumbnail != null)
			this.setPosition(0, 0);

    this.unsubscribeDrag();

   	this.zoomView.style.width = "";
		this.zoomView.style.height = "";
		this.zoomView.removeAttribute('width');
		this.zoomView.removeAttribute('height');

		this.zoomView.onclick = function()
			{
				instance.initNewImage(instance.zoomImageThumbnail, instance.zoomImageThumbnail.parentNode.href);
				instance.zoomView.onclick = Function();
			};

		this.showMagnifyLens(true, 'in');
	}
	
	///
	// Set the zoom relatively to the size of thumbanil image
	// zoom 2x means that big image will be twice as big as thumbnail
	//
	this.setThumbnailMultiply = function(multiply, render)
	{
		this._debugCalls("setThumbnailMultiply", multiply, render);	// Module: DEBUG

		this.zoomMultiplyOnClick = null;
		this.setMultiply(multiply, render);
	}

	///
	// Set the zoom relatively to the size of view port
	// zoom 2x means that big image will be twice as big as view port
	//
	this.setZoomMultiply = function(multiply, render)
	{
		this._debugCalls("setZoomMultiply", multiply, render);	// Module: DEBUG

		this.zoomMultiplyOnClick = multiply;
		
		if(this.zoomImageThumbnail == null)
			return;
		
		if(typeof(multiply) == 'number')
		{
			multiply = (this.viewPort.offsetWidth / this.zoomImageThumbnail.offsetWidth) * multiply;
		}
		this.setMultiply(multiply, render);
	}
	
	this.setMultiply = function(multiply, render)
	{
		this._debugCalls("setMultiply", multiply, render);	// Module: DEBUG

		if(render != false)
			render = true;

		if(multiply == 'auto')
		{
			this.zoomMultiply = multiply;
		}
		else if(typeof(multiply) == 'number')
		{
			var max = this.getMaximumZoom();
			var min = this.getMinimumZoom();
			if(multiply > max)
				multiply = max;
			if(multiply < min)
				multiply = min;
			this._debugCalls('Force re-set multiply to', multiply);
			this.zoomMultiply = multiply;
		}
		else
		{
			this._debug("Function setZoomMultiply received wrong value. It can be number or 'auto'. Value was not changed.");
		}
		
		if(render == true && this.zoomImageThumbnail != null)
		{
		  // TODO
			//this.processNewImage();
		}
		this.fixZoomBorders();
	}
	
	this.zoomTo = function(d, x, y)
	{
    //var max = this._maxZoom ? this._maxZoom : this.getMaximumZoom();
    //var min = this._minZoom ? this._minZoom : this.getMinimumZoom();
    
    //if(d < min) d = min;
    //if(d > max) d = max;
    
    //this.zoomMultiply = d;

		if(d == 'auto')
		{
			this.zoomMultiply = d;
		}
		else if(typeof(d) == 'number')
		{
			var max = this.getMaximumZoom();
			var min = this.getMinimumZoom();

			if(d > max)
			{
				d = max;
  			this._debugCalls('Force re-set multiply to', d); // Module: DEBUG
			}
			if(d < min)
			{
				d = min;
  			this._debugCalls('Force re-set multiply to', d); // Module: DEBUG
			}

			this.zoomMultiply = d;


			this.zoomView.style.width = (this.viewPort.offsetWidth * d) + "px";
			this.zoomView.style.height = (this.viewPort.offsetHeight * d) + "px";
		}
		else
		{
			this._debug("Function setZoomMultiply received wrong value. It can be number or 'auto'. Value was not changed.");
		}

/*
		if(d == 'auto')
		{
			this.zoomView.style.width = "";
			this.zoomView.style.height = "";
		}
		else
		{
			this.zoomView.style.width = (this.zoomImageThumbnail.offsetWidth * d) + "px";
			this.zoomView.style.height = (this.zoomImageThumbnail.offsetHeight * d) + "px";
		}
*/    
    this.fixZoomBorders();
	}

	this.renderMagnifier = function()
	{
		this._debugCalls("renderMagnifier");	// Module: DEBUG

		this.magnifier.style.width = ((this.viewPort.offsetWidth / this.zoomViewLastSize[0]) * this.zoomImageThumbnail.offsetWidth) + "px";
		this.magnifier.style.height = ((this.viewPort.offsetHeight / this.zoomViewLastSize[1]) * this.zoomImageThumbnail.offsetHeight) + "px";
//		this.magnifier.style.width = ((this.viewPort.offsetWidth / this.zoomView.offsetWidth) * this.zoomImageThumbnail.offsetWidth) + "px";
//		this.magnifier.style.height = ((this.viewPort.offsetHeight / this.zoomView.offsetHeight) * this.zoomImageThumbnail.offsetHeight) + "px";
			
		this.fixZoomBorders();
	}
	
	///
	// Fix the borders of the image
	// There shouldn't be any white borders in view port or on thumbnail
	//
	this.fixZoomBorders = function()
	{
		this._debugCalls("fixZoomBorders");	// Module: DEBUG

		if(parseInt(this.zoomOverlay.style.left) + this.magnifier.offsetWidth > this.zoomImageThumbnail.offsetWidth)
		{
			this.zoomOverlay.style.left = (this.zoomImageThumbnail.offsetWidth - this.magnifier.offsetWidth) + "px";
			this.zoomView.style.left = (-(this.zoomView.offsetWidth - this.viewPort.offsetWidth)) + "px";
		}
		if(parseInt(this.zoomOverlay.style.top) + this.magnifier.offsetHeight > this.zoomImageThumbnail.offsetHeight)
		{
			this.zoomOverlay.style.top = (this.zoomImageThumbnail.offsetHeight - this.magnifier.offsetHeight) + "px";
			this.zoomView.style.top = (-(this.zoomView.offsetHeight - this.viewPort.offsetHeight)) + "px";
		}

		if(parseInt(this.zoomOverlay.style.left) < 0)
		{
			this.zoomOverlay.style.left = "0px";
			this.zoomView.style.left = "0px";
		}
		if(parseInt(this.zoomOverlay.style.top) < 0)
		{
			this.zoomOverlay.style.top = "0px";
			this.zoomView.style.top = "0px";
		}
	}

	this.getMinimumZoom = function()
	{
		this._debugCalls("getMinimumZoom");	// Module: DEBUG
		return 1;
/*
		var widthMin = this.zoomViewLastSize[0] / this.viewPort.offsetWidth;
		var heightMin = this.zoomViewLastSize[1] / this.viewPort.offsetHeight;
		
		var ret = ((widthMin > heightMin) ? heightMin : widthMin);
		return ret;
*/
	}
	
	this.getMaximumZoom = function()
	{
		this._debugCalls("getMaximumZoom");	// Module: DEBUG

    // @TODO Remove try
    try
    {
  		var widthMin = this.zoomViewLastSize[0] / this.viewPort.offsetWidth;
  		var heightMin = this.zoomViewLastSize[1] / this.viewPort.offsetHeight;
  		
  		var ret = ((widthMin < heightMin) ? heightMin : widthMin);
  	}
  	catch(err)
  	{
  	}
		return ret;
	}

	this.getActualZoom = function()
	{
		this._debugCalls("getActualZoom");	// Module: DEBUG

    return this.zoomView.offsetWidth / this.viewPort.offsetWidth
	}

	this.renderZoomedImage = function()
	{
		this._debugCalls("renderZoomedImage");	// Module: DEBUG

		if(typeof(this.zoomMultiplyOnClick) == 'number')
		{
			this.setZoomMultiply(this.zoomMultiplyOnClick, false);
		}
		
		if(this.zoomMultiply == 'auto')
		{
			this.zoomView.style.width = "";
			this.zoomView.style.height = "";
		}
		else
		{
			this.zoomView.style.width = (this.zoomImageThumbnail.offsetWidth * this.zoomMultiply) + "px";
			this.zoomView.style.height = (this.zoomImageThumbnail.offsetHeight * this.zoomMultiply) + "px";
		}
	}
	
	this.unsubscribeDrag = function()
	{
		this._debugCalls("unsubscribeDrag");	// Module: DEBUG

		var instance = this;

		try
		{
			var f = new Function();
			
			this.magnifier.onDragStart = f;
			this.magnifier.onDragEnd = f;
			this.magnifier.onDrag = f;
			this.magnifier.onmousedown = f;

			this.zoomView.onDragStart = f;
			this.zoomView.onDragEnd = f;
			this.zoomView.onDrag = f;
			this.zoomView.onmousedown = f;

			if(this.enableWheel == true)
			{
        this.removeEvent(instance.zoomView, 'mousewheel', this._onWheel );
			}
		}
		catch(err) {}
	}

	this.subscribeDrag = function()
	{
		this._debugCalls("subscribeDrag");	// Module: DEBUG

		var instance = this;

		// Find position of the thumnail so we can restrict the area for the magnifier
		var tnPosition = this.findPos(this.zoomImageThumbnail);

		// Moving with the small area
		this.dragMagnifier = Drag.init(
				this.zoomOverlay, null,
				0, this.zoomImageThumbnail.offsetWidth - this.magnifier.offsetWidth,
				0, this.zoomImageThumbnail.offsetHeight - this.magnifier.offsetHeight
			);

		// Moving on big image
		this.dragView = Drag.init(
				this.zoomView, null,
				-(this.zoomView.offsetWidth - this.viewPort.offsetWidth), 0,
				-(this.zoomView.offsetHeight - this.viewPort.offsetHeight), 0
			);	// sirka velkeho + sirka magnifier

		if(this.enableWheel == true)
		{
      this.removeEvent(instance.zoomView, 'mousewheel', this._onWheel );
      this.addEvent(instance.zoomView, 'mousewheel',  this._onWheel );
		}
	}

  this.changeImage = function(ZoomImageTn, isChange)
  {
    this._debugCalls("changeImage", ZoomImageTn);	// Module: DEBUG

    var instance = this;
    
    this.zoomImageThumbnail = ZoomImageTn;
    this.zoomView.onload = function() { instance.onMediumLoad(); };
    this.zoomView.src = ZoomImageTn.parentNode.rel;
    
    if(isChange == null || isChange == true)
    {
      this.onImageChange();
      this.raise('onImageChange');
    }
  }

	this.initNewImage = function(ZoomImageTn, bigImage)
	{
		this._debugCalls("initNewImage", ZoomImageTn, bigImage);	// Module: DEBUG

    //this.onImageChange();

    // TODO
    //this.zoomView.src = ZoomImageTn.parentNode.rel;

    this.zoomImageThumbnail = this.getElement(ZoomImageTn);

		if(this.zoomImageThumbnail == null || typeof(this.zoomImageThumbnail) != 'object')
		{
			this._debug("Thumbnail image was not found. Cannot initialize zoom.");	// Module: DEBUG
			return;
		}
		
		//this.disableZoom();	// Disable the zoomer before new image will be loaded
		
		// Move the overlay element to new DOM parent
		ZoomImageTn.parentNode.appendChild(this.zoomOverlay);
		
		// Set the position for the <img src="pic/medium/img2.jpg" alt="img2.jpg, 61kB" class="" title="Img2" height="300" width="400" />magnifier and viewPort to the left top corner of the thumnail
		this.zoomOverlay.style.left = "0px";
		this.zoomOverlay.style.top = "0px";

		this.zoomView.style.left = "0px";
		this.zoomView.style.top = "0px";

		this.loadingIcon(true);

		var instance = this;

		// Change the image
		this.zoomView.style.visibility = 'hidden';
    this.zoomView.onload = function()
			{
				instance.processNewImage();
			}
		this.zoomViewLastSize = null;
		this.zoomView.src = bigImage;
	}
	
	this.processNewImage = function()
	{
		this._debugCalls("processNewImage");	// Module: DEBUG

		this.zoomView.style.visibility = 'visible';
		this.loadingIcon(false);

		this.zoomView.onload = Function();
		if(this.zoomViewLastSize == null)
		{
			this.zoomViewLastSize = new Array(this.zoomView.offsetWidth, this.zoomView.offsetHeight);
		}

		this.renderZoomedImage();
		
		this.enableZoom();	// Enable zoom - clicking etc.
		this.renderMagnifier();	// Render the magnify rectangle

		// Find position of the thumnail so we can restrict the area for the magnifier
		//var tnPosition = this.findPos(this.zoomImageThumbnail);
		
		this.subscribeDrag();
		
		var instance = this;
		
		// OnClick on thumbnail
		if(this.zoomClick)
		{
  		this.zoomImageThumbnail.onclick = function(pos)
  			{
          if(pos == null) pos = event;
  				var ePos = instance.findPos(this);
  				instance.setPosition(
  					pos.clientX - ePos[0] - (instance.magnifier.offsetWidth / 2),
  					pos.clientY - ePos[1] - (instance.magnifier.offsetHeight / 2)
  					);
  				return false;
  			}
  	}
  	else
  	{
      this.zoomImageThumbnail.onclick = Function();
  	}
		
		this.zoomOverlay.onDrag = function(x, y)
			{
				var tnPosition = instance.findPos(instance.zoomImageThumbnail);
				
				var xFactor = instance.zoomImageThumbnail.offsetWidth / instance.zoomView.offsetWidth;
				var yFactor = instance.zoomImageThumbnail.offsetHeight / instance.zoomView.offsetHeight;
				curr_x = x / xFactor;
				curr_y = y / yFactor;
	
				instance.zoomView.style.left = -curr_x + "px";
				instance.zoomView.style.top = -curr_y + "px";
			}

		this.zoomView.onDrag = function(x, y)
			{
				var tnPosition = instance.findPos(instance.zoomImageThumbnail);
	
				var xFactor = instance.zoomView.offsetWidth / instance.zoomImageThumbnail.offsetWidth;
				var yFactor = instance.zoomView.offsetHeight / instance.zoomImageThumbnail.offsetHeight;
				curr_x = x / xFactor;
				curr_y = y / yFactor;
	
				//Large image moves in the opposite direction to the magnifier.
				instance.zoomOverlay.style.left = -curr_x + "px";
				instance.zoomOverlay.style.top = -curr_y + "px";
			}
		
		this.zoomView.style.visibility = 'visible';
		this.loadingIcon(false);
		this.showMagnifyLens(true, 'out');
		//this.showArrows();	// Change visibility of the arrows
	}

  // Positions
  
  ///
  // Convert viewPort position to image
  this.v2i = function(x, y)
  {
    
  }
  
	this.setPosition = function(x, y)
	{
		this._debugCalls("setPosition", x, y);	// Module: DEBUG

		try
		{
			var tnPos = this.findPos(this.zoomImageThumbnail);
			var bigPos = this.findPos(this.zoomView);
		
			// Move thumnail		
			this.zoomOverlay.style.left = x + "px";
			this.zoomOverlay.style.top = y + "px";
			
			// Move big image
			var xFactor = this.zoomImageThumbnail.offsetWidth / this.zoomView.offsetWidth;
			var yFactor = this.zoomImageThumbnail.offsetHeight / this.zoomView.offsetHeight;
			curr_x = (x)/xFactor;
			curr_y = (y)/yFactor;
			this.zoomView.style.left = (-curr_x) + "px";
			this.zoomView.style.top = (-curr_y) + "px";
		}
		catch(err)
		{
			this._debug("An exception occurred in the script. Error name: " + err.name + ". Error message: " + err.message);	// Module: DEBUG
		}
		
		// Fix borders
		this.fixZoomBorders();
	}

	this.findPos = function(obj)
	{
		//this._debugCalls("findPos", obj);	// Module: DEBUG

		if(obj == null || typeof(obj) != 'object')
		{
			alert("Unknown object passed to findPos method");	// Module: DEBUG
			return;
		}

		var curleft = curtop = 0;
		if (obj.offsetParent) {
			do {
						curleft += obj.offsetLeft;
						curtop += obj.offsetTop;
			} while (obj = obj.offsetParent);
		}
		return [curleft,curtop];
	}
	
	this.getElement = function(e)
	{
		this._debugCalls("getElement", e);	// Module: DEBUG

		if(typeof(e) == 'object')
		{
			return e;
		}
		else
		{
			return document.getElementById(e);
		}
	}

	this.loadingIcon = function(show)
	{
		this._debugCalls("loadingIcon", show);	// Module: DEBUG
		
		if(this.loading == null)
			return;

		if(show == true)
		{
			this.loading.style.visibility = 'visible';
			this.loading.style.left = ((this.viewPort.offsetWidth - this.loading.offsetWidth) / 2) + "px";
			this.loading.style.top = ((this.viewPort.offsetHeight - this.loading.offsetHeight) / 2) + "px";
		}
		else
		{
			this.loading.style.visibility = 'hidden';
		}
	}

	this.onWheel = function(e)
	{
		this.cancelEvent(e);
		this._debugCalls("onWheel", e);	// Module: DEBUG
		
		// If the thumnail is visible, disable the wheel
		if(this.lensImageOut.style.visibility != 'visible')
		{
  		this._debugCalls("force-exit", 'onWheel');	// Module: DEBUG
		  return;
		}

		e = e ? e : window.event;
		var raw = e.detail ? e.detail : e.wheelDelta;
		
		if(raw < 0)	// Zoom in		
		{
		  this.zoomTo(this.getActualZoom() + 0.5, 10, 10);
		}
		else	// Zoom out
		{
		  this.zoomTo(this.getActualZoom() - 0.5, 10, 10);
		}
		
		this.unsubscribeDrag();
		this.subscribeDrag();
		
		this.raise('onWheel', e);
	}
  
  this.onMediumLoad = function()
  {
		this._debugCalls("onMediumLoad");	// Module: DEBUG

    this.fixMedium();
  }

  this.onImageChange = function()
  {
    //this.zoomImageThumbnail = this;
    this.recreateEvents();
    this.disableZoom();
    this.showArrows();

    var instance = this;
		this.zoomView.onclick = function()
			{
				instance.initNewImage(instance.zoomImageThumbnail, instance.zoomImageThumbnail.parentNode.href);
				instance.zoomView.onclick = Function();
			};
    //instance.unsubscribeDrag();
    //instance.zoomView.src = instance.zoomImageThumbnail.parentNode.rel;
  }
  
  this.fixMedium = function()
  {
    var z = this.zoomView;
    var p = this.viewPort;
    var w = this.zoomView.offsetWidth / this.viewPort.offsetWidth;
    var h = this.zoomView.offsetHeight / this.viewPort.offsetHeight;

    // proportion 
    if(w > 1 || h > 1)
    {
      if(w > h)
      {
        z.style.width = p.offsetWidth + "px";
      }
      else
      {
        z.style.height = p.offsetHeight + "px";
      }
    }
    
    // Center image
    var w = p.offsetWidth - z.offsetWidth;
    if(w > 0)
      z.style.left = w / 2 + "px";
    var h = p.offsetHeight - z.offsetHeight;
    if(h > 0)
      z.style.top = h / 2 + "px";
  }



  ///
  // Add custom handler for internal actions
  this.handle = function(action, fn)
  {
    this._customEvents[action] = fn;
  }

  ///
  // remove custom handler for internal actions
  this.unhandle = function(action)
  {
    this._customEvents[action] = null;
  }

  ///
  // Raise internal action and call custom handler
  this.raise = function(action)
  {
    var e = this._customEvents[action];
    if(e != null)
      e();
  }





	this.addEvent = function(obj, eventName, fn)
	{
		this._debugCalls("addEvent", obj, eventName, fn);	// Module: DEBUG

		if (obj.addEventListener)
		{
	    if(eventName == 'mousewheel')
	    {
	      obj.addEventListener('DOMMouseScroll', fn, false);
	    }
			obj.addEventListener(eventName, fn, false);
			return true;
		}
		else if (obj.attachEvent)
		{
			var r = obj.attachEvent("on" + eventName, fn);
			return r;
		}
		else
		{
			return false;
		}
	}
	
	this.removeEvent = function(obj, eventName, fn)
	{
		this._debugCalls("removeEvent", obj, eventName, fn);	// Module: DEBUG

		fn = Function();
		if(obj.removeEventListener)
		{
			if(eventName == 'mousewheel')
				obj.removeEventListener('DOMMouseScroll', fn, false);  
			obj.removeEventListener(eventName, fn, false);
		}
		else if(obj.detachEvent)
			obj.detachEvent("on" + eventName, fn);
	}

	this.cancelEvent = function(e)
	{
		e = e ? e : window.event;
		if(e.stopPropagation)
			e.stopPropagation();
		if(e.preventDefault)
			e.preventDefault();
		e.cancelBubble = false;
		e.cancel = true;
		e.returnValue = false;
		return false;
	}
  
  ///
  // Find next sibling fo the type in the same parent as elem
	this.findNextSibling = function(elem, type)
	{
		this._debugCalls("findNextSibling", elem, type);	// Module: DEBUG

		var e2 = elem.nextSibling;
		
		while (e2 != null && e2.nodeType != 1 && e2.nodeName != type)
		{
			e2 = e2.nextSibling;
		}
		return e2;
	}

  ///
  // Find previous sibling for the type in the same parent as elem
	this.findPreviousSibling = function(elem, type)
	{
		this._debugCalls("findPreviousSibling", elem, type);	// Module: DEBUG

		var e2 = elem.previousSibling;
		
		while (e2 != null && e2.nodeType != 1 && e2.nodeName != type)
		{
			e2 = e2.previousSibling;
		}
		return e2;
	}

  ///
  // Remove required event, when last event was removed, call postLoad method
	this.removeLoadItems = function(item)
	{
		this._debugCalls("removeLoadItems", item);	// Module: DEBUG

		var i = 0;
		while (i < this.requiredLoad.length) {
			if (this.requiredLoad[i] == item) {
				this.requiredLoad.splice(i, 1);
			} else {
				i++;
			}
		}

		if(this.requiredLoad.length == 0)
			this.postLoad();
	}
	
	///
	// Here starts debug, can be removed for live site
	// if so, you need to remove all live marked with '// Module: DEBUG' at the end
	// Module: DEBUG
	//
	this.initDebug = function(div, callDiv)
	{
		this.debugDiv = this.getElement(div);
		this.debugCallDiv = this.getElement(callDiv);
		this.debugEnabled = true;
		
		var instance = this;
		if(this.debugDiv)
		  setTimeout(function() { instance._debugRepeat(); }, 2000);
	}
	
	// Module: DEBUG
	this._debugRepeat = function()
	{
		if(this.debugEnabled != true || typeof(this.debugDiv) != 'object')
			return;

		try
		{
			this.debugDiv.innerHTML = "";

			this.debugDiv.innerHTML = 
				"<b>ViewPort</b><br />" +
				"<ul>" +
				"<li>Src: " + this.zoomView.src + "</li>" +
				"<li>OnClick: " + this.zoomView.onclick + "</li>" +
				"<li>OnLoad: " + this.zoomView.onload + "</li>" +
				"<li><i>zoomView</i><ul>" + 
					"<li>onclick: " + this.zoomView.onclick + "</li>" +
					"</ul></li>" +
				"<li><i>prevArrow</i><ul>" + 
					"<li>position: " + this.arrowPrev.style.left + ", " + this.arrowPrev.style.top + "</li>" +
					"<li>visible: " + this.arrowPrev.style.visibility + "</li>" +
					"</ul></li>" +
				"<li><i>nextArrow</i><ul>" + 
					"<li>position: " + this.arrowNext.style.left + ", " + this.arrowNext.style.top + "</li>" +
					"<li>visible: " + this.arrowNext.style.visibility + "</li>" +
					"</ul></li>" +
				"<li><i>zoomIn</i><ul>" + 
					"<li>position: " + this.lensImageIn.style.left + ", " + this.lensImageIn.style.top + "; visible: " + this.lensImageIn.style.visibility + "</li>" +
					"<li>visible: " + this.lensImageIn.onclick + "</li>" +
					"</ul></li>" +
				"<li><i>zoomOut</i><ul>" + 
					"<li>position: " + this.lensImageOut.style.left + ", " + this.lensImageOut.style.top + "; visible: " + this.lensImageOut.style.visibility + "</li>" +
					"<li>visible: " + this.lensImageOut.onclick + "</li>" +
					"</ul></li>" +
				"</ul></li>" +
				"</ul>";

			this.debugDiv.innerHTML += 
				"<b>Gallery</b><br />" +
				"<ul>";
			var s = this.thumbnailsDiv.getElementsByTagName("a");
			for (var i=0;i<s.length;i++)  
			{
				var tImg = s[i].getElementsByTagName("img");
				var img = tImg[0];
				this.debugDiv.innerHTML += "<li>Image " + (i + 1);
				this.debugDiv.innerHTML += "<ul>" + 
					"<li>Thumbnail: " + img.src + "</li>" +
					"<li>Medium: " + s[i].rel + "</li>" +
					"<li>Big: " + s[i].href + "</li>" +
					"<li>Img onclick: " + img.onclick + "</li>" +
					"<li>Anchor onclick: " + s[i].onclick + "</li>" +
					"</ul>";
				this.debugDiv.innerHTML += "</li>"
			}
	
			this.debugDiv.innerHTML += "</ul>";
		}
		catch(err)
		{}

    if(this.debugDiv)
    {
  		var instance = this;
		  setTimeout(function() { instance._debugRepeat(); }, 2000);
		}
	}

	// Module: DEBUG
	this._debug = function(message)
	{
		if(this.debugEnabled != true)
			return;
		alert(message);
	}
	
	// Module: DEBUG
	this._debugCalls = function()
	{
		if(this.debugEnabled != true || this.debugCallDiv == null || typeof(this.debugCallDiv) != 'object')
			return;
			
		if(this._debugPause == true)
      alert("pause");
    
		var argv = this._debugCalls.arguments;
		var argc = argv.length;
		this.debugCallDiv.innerHTML += "<b>Call:</b> <i>" + argv[0] + "</i> (";
		for (var i = 1; i < argc; i++)
		{
			if(i != 1)
				this.debugCallDiv.innerHTML += ", ";
			this.debugCallDiv.innerHTML += argv[i];
		}
		this.debugCallDiv.innerHTML += ")<br />";
	}

	// Module: DEBUG
	this._dubugGetActualMultiply = function()
	{
		return this.zoomMultiply;
	}
	
	// Module: DEBUG
	this._debugPrintStackTrace = function()
	{
		var callstack = [];
		var isCallstackPopulated = false;
		try {
			i.dont.exist+=0; //doesn't exist- that's the point
		} catch(e) {
			if (e.stack) //Firefox
			{
				var lines = e.stack.split('\n');
				for (var i=0, len=lines.length; i<len; i++) {
					if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
						callstack.push(lines[i]);
					}
				}
				//Remove call to printStackTrace()
				callstack.shift();
				isCallstackPopulated = true;
			}
			else if (window.opera && e.message) { //Opera
				var lines = e.message.split('\n');
				for (var i=0, len=lines.length; i<len; i++) {
					if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
						var entry = lines[i];
						//Append next line also since it has the file info
						if (lines[i+1]) {
							entry += " at " + lines[i+1];
							i++;
						}
						callstack.push(entry);
					}
				}
				//Remove call to printStackTrace()
				callstack.shift();
				isCallstackPopulated = true;
			}
		}
		if (!isCallstackPopulated) { //IE and Safari
			var currentFunction = arguments.callee.caller;
			while (currentFunction) {
				var fn = currentFunction.toString();
				var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
				callstack.push(fname);
				currentFunction = currentFunction.caller;
			}
		}
		output(callstack);
	}
	
  function output(arr) {
    alert(arr.join('\n\n'));
  }

}


var PWBrowser = 
{
	_browser : null,
	_version : null,
	
	init : function()
	{
		//test for MSIE x.x;
		if (/MSIE (\d+\.\d+);/.test(navigator.userAgent))
		{
			PWBrowser._browser = 'msie';
			PWBrowser._version = new Number(RegExp.$1)
		}
		//test for Firefox/x.x or Firefox x.x (ignoring remaining digits);
		else if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent))
		{
			PWBrowser._browser = 'firefox';
			PWBrowser._version = new Number(RegExp.$1)
		}
		else if (/Opera[\/\s](\d+\.\d+)/.test(navigator.userAgent)){ //test for Opera/x.x or Opera x.x (ignoring remaining decimal places);
			PWBrowser._browser = 'opera';
			PWBrowser._version = new Number(RegExp.$1) // capture x.x portion and store as a number
		}
		else
		{
			PWBrowser._browser = '';
			PWBrowser._version = 0;
		}
			//PWBrowser._browser = 'msie';
			//PWBrowser._version = 6;
	},
	
	browser : function()
	{
		if(PWBrowser._browser == null) PWBrowser.init();
		return PWBrowser._browser;
	},
	
	version : function()
	{
		if(PWBrowser._version == null) PWBrowser.init();
		return PWBrowser._version;
	}
}
