/**
 * JS Viewer.
 * 
 * Simple javascript image viewer. Attaches self to click event of all elements with
 * class 'zebooka_jsviewer' and existing href. Title and description for an image 
 * are taken from title attribute of a link.
 * 
 * Requires prototype.js 1.6.1
 * 
 * @author Zebooka (Anton Bondar)
 * @copyright (c) 2009-2010 Zebooka (Anton Bondar)
 * @link http://www.zebooka.com/soft/zebooka-jsviewer/
 * @license http://www.zebooka.com/soft/LICENSE/
 * @package Zebooka
 * @subpackage JSViewer
 * @version 0.4.5
 * @todo Fix bug with scales when situation is opposite.
 */

var Zebooka = Zebooka || {};

Zebooka.JSViewer = {
	
	VERSION: '0.4.5',
	
	i18n: {
		PREVIOUS: 'Previous image',
		NEXT: 'Next image',
		CLOSE: 'Close viewer',
		LOADING: 'Loading image. Please wait.',
		ERROR: 'Error occured while loading image "#{title}". \nRequested URL "#{href}" is either unaccessable, or something gone wrong. \n\nRetry image loading?'
	},
	
	options: {
		upScale: false,
		downScale: true,
		overlayOpacity: 0.85,
		titleDescriptionSeparator: '|',
		hideSelectsEmbedsObjects: true,
		debug: false,
		prevNextOpacity: 1.0,
		closeOpacity: 1.0
	},
	
	images: [],
	currentImage: undefined,
	currentData: [0, 0, 0, 0],
	
	ieVersion: function ()
	{
		return parseInt(navigator.userAgent.match(/MSIE ([^;]*);/)[1] || 6.0);
	},
	
	keypressHandler: function (event)
	{
		var key = event.keyCode;
		var escapeKey = event.DOM_VK_ESCAPE || 27;
		
		switch (key)
		{
			case escapeKey: // ESCAPE
				event.stop();
				this.closeViewer();
				break;
			case 37: // LEFT
			case 8:  // BACKSPACE (does not work in Opera)
				event.stop();
				this.previousImage();
				break;
			case 39: // RIGHT
			case 32: // SPACE (does not work in Opera)
				event.stop();
				this.nextImage();
				break;
			default:
				break;
		}
	},
	
	clickHandler: function (event)
	{
		var target = event.findElement('.zebooka_jsviewer[href]');
		if (target && event.button == 0)
		{
			event.stop();
			this.openViewer(target);
		}
	},
	
	previousImage: function ()
	{
		if (typeof this.currentImage == 'undefined' || this.currentImage <= 0)
			return;
		this.currentImage--;
		this.showImage(this.currentImage);
	},
	
	nextImage: function ()
	{
		if (typeof this.currentImage == 'undefined' || this.currentImage + 1 >= this.images.length)
			return;
		this.currentImage++;
		this.showImage(this.currentImage);
	},
	
	initialize: function ()
	{
		if (this.options.debug && document.compatMode == 'BackCompat')
			alert('Your browser is in quirks mode. Zebooka-JSViewer script correctly works only when ' +
				'browser is in standart complitant mode. Add DOCTYPE tag to your html code. See ' +
				'http://en.wikipedia.org/wiki/Quirks_mode');
	
		this.clickHandler = (this.clickHandler).bind(this);
		this.keypressHandler = (this.keypressHandler).bind(this);
		this.resizeHandler = (this.resizeHandler).bind(this);
		this.ieScrollHandler = (this.ieScrollHandler).bind(this);
		this.previousImage = (this.previousImage).bind(this);
		this.nextImage = (this.nextImage).bind(this);
		
		div_overlay = document.createElement('div');
		div_overlay.id = 'zebooka_jsviewer_overlay';
		document.body.appendChild(div_overlay);
		div_overlay = $('zebooka_jsviewer_overlay');
		div_overlay.hide().observe('click', (function () { this.closeViewer(); }).bind(this));
		div_window = document.createElement('div');
		div_window.id = 'zebooka_jsviewer_window';
		document.body.appendChild(div_window);
		div_window = $('zebooka_jsviewer_window');
		
		if (Prototype.Browser.IE && this.ieVersion() < 7.0)
		{
			div_overlay.setStyle({ position: 'absolute' });
			div_window.setStyle({ position: 'absolute' });
		}
		else
		{
			div_overlay.setStyle({ position: 'fixed' });
			div_window.setStyle({ position: 'fixed' });
		}
		
		div_window.hide().innerHTML = '<div id="zebooka_jsviewer_title">&#160;</div>' +
			'<img id="zebooka_jsviewer_image" src="" />' +
			'<div id="zebooka_jsviewer_description">&#160;</div>' +
			'<div id="zebooka_jsviewer_close">&#160;</div>' +
			'<div id="zebooka_jsviewer_previous">&#160;</div>' +
			'<div id="zebooka_jsviewer_next">&#160;</div>';
		$('zebooka_jsviewer_close').hide().setStyle({ opacity: this.options.closeOpacity || 0.001 }).observe('click', this.closeViewer).writeAttribute('title', this.i18n.CLOSE || '');
		$('zebooka_jsviewer_previous').hide().setStyle({ opacity: this.options.prevNextOpacity || 0.001 }).observe('click', this.previousImage).writeAttribute('title', this.i18n.PREVIOUS || '');
		$('zebooka_jsviewer_next').hide().setStyle({ opacity: this.options.prevNextOpacity || 0.001 }).observe('click', this.nextImage).writeAttribute('title', this.i18n.NEXT || '');
		
		document.observe('click', this.clickHandler);
	},
	
	openViewer: function (clickedLink)
	{	
		if (this.options.hideSelectsEmbedsObjects)
			$$('select', 'object', 'embed').each(function (node) { node.style.visibility = 'hidden'; });
		$('zebooka_jsviewer_overlay').setOpacity(0.001).show();

		this.images = $$('.zebooka_jsviewer[href]').collect(function (a) { return [a.href, a.title]; }).uniq();
		var i = 0;
		while (this.images[i][0] != clickedLink.href) { i++; }
		this.showImage(i);
		this.resizeHandler(null);
		$('zebooka_jsviewer_overlay').setOpacity(this.options.overlayOpacity || 0.90);
		document.observe('keydown', this.keypressHandler);
		Event.observe(window, 'resize', this.resizeHandler);
		if (Prototype.Browser.IE && this.ieVersion() < 7.0)
			Event.observe(window, 'scroll', this.ieScrollHandler);
	},
	
	showImage: function (i)
	{
		var div_window = $('zebooka_jsviewer_window');
		$('zebooka_jsviewer_overlay').addClassName('zebooka_jsviewer_loading').writeAttribute('title', this.i18n.LOADING || '');
		div_window.hide();
		this.currentImage = i;
		var href = this.images[i][0];
		var title = (this.images[i][1]).split(this.options.titleDescriptionSeparator, 2);
		$('zebooka_jsviewer_title').innerHTML = '&#160;';
		$('zebooka_jsviewer_description').innerHTML = '&#160;';
		var tempImage = new Image();
		// tempImage.observe('load',  // BUG IN IE - Image is not extended by prototype
		tempImage.onerror = (function (e) {
			if (confirm((new Template(this.i18n.ERROR)).evaluate({ title: title[0] || '', href: href || '' })))
				this.showImage(this.currentImage);
			else
				this.closeViewer();
		}).bind(this);
		tempImage.onload = (function (e) {
			if (typeof this.currentImage == 'undefined')
				return;
			document.stopObserving('keydown', this.keypressHandler);
			document.stopObserving('click', this.clickHandler);
			div_window.show().setOpacity(0.001);
			var img = $('zebooka_jsviewer_image');
			if (img.src == href)
				div_window.setOpacity(1.0);
			else
				img.observe('load', function (e) {
					div_window.setOpacity(1.0);
				});
			img.src = href;
			this.currentData = [tempImage.width, tempImage.height, 0, 0];
			$('zebooka_jsviewer_title').innerHTML = title[0] || '&#160;';
			$('zebooka_jsviewer_description').innerHTML = title[1] || '&#160;';
			$('zebooka_jsviewer_overlay').removeClassName('zebooka_jsviewer_loading').writeAttribute('title', this.i18n.CLOSE || '');
			this.resizeHandler(null);
			document.observe('keydown', this.keypressHandler);
			document.observe('click', this.clickHandler);
		}).bind(this);
		tempImage.src = href;
	},
	
	resizeHandler: function (event)
	{
		if (typeof this.currentImage == 'undefined')
			return;
		
		var body = $$('body')[0];
		var div_window = $('zebooka_jsviewer_window');
		var img = $('zebooka_jsviewer_image');
		var div_title = $('zebooka_jsviewer_title');
		var div_desc = $('zebooka_jsviewer_description');
		var div_close = $('zebooka_jsviewer_close');
		var div_prev = $('zebooka_jsviewer_previous');
		var div_next = $('zebooka_jsviewer_next');
		
		if (Prototype.Browser.IE && this.ieVersion() < 7.0)
		{
			var mw = parseInt(body.getStyle('marginLeft')) + parseInt(body.getStyle('marginRight'));
			var mh = parseInt(body.getStyle('marginTop')) + parseInt(body.getStyle('marginBottom'));
			var vw = Math.max(body.getWidth() + mw, document.documentElement.getWidth() - mw - 2);
			var vh = Math.max(body.getHeight() + mh, document.documentElement.getHeight() - 4);
		}
		else
		{
			var vw = document.viewport.getWidth();
			var vh = document.viewport.getHeight();
		}
		$('zebooka_jsviewer_overlay').setStyle({ left: '0px', top: '0px', width: vw + 'px', height: vh + 'px' });
		div_close.hide();
		div_prev.hide();
		div_next.hide();
		if (this.currentData[2] == 0 && this.currentData[3] == 0)
		{
			div_title.hide();
			div_desc.hide();
			img.width = this.currentData[0];
			img.height = this.currentData[1];
			div_window.setStyle({ width: 'auto' });
			div_window.setStyle({ 
				width: (img.getWidth() + parseInt(img.getStyle('marginLeft')) + parseInt(img.getStyle('marginRight'))) + 'px', 
				height: 'auto' 
			});
			div_title.show();
			div_desc.show();
			this.currentData[2] = div_window.getWidth() + parseInt(div_window.getStyle('marginLeft')) + parseInt(div_window.getStyle('marginRight')) - this.currentData[0];
			this.currentData[3] = div_window.getHeight() + parseInt(div_window.getStyle('marginTop')) + parseInt(div_window.getStyle('marginBottom')) - this.currentData[1];
		}
		
		var iwm = document.viewport.getWidth() - this.currentData[2];
		var ihm = document.viewport.getHeight() - this.currentData[3];
		
		var coffee = 1;
		if (iwm > this.currentData[0] && ihm > this.currentData[1])
		{
			if (this.options.upScale)
				coffee = Math.min(iwm / this.currentData[0], ihm / this.currentData[1]);
			else
				coffee = 1;
		}
		else if (iwm < this.currentData[0] || ihm < this.currentData[1])
		{
			if (this.options.downScale)
				coffee = Math.min(iwm / this.currentData[0], ihm / this.currentData[1]);
			else
				coffee = 1;
		}

		div_title.hide();
		div_desc.hide();
		div_close.hide();
		div_prev.hide();
		div_next.hide();
		img.width = this.currentData[0] * coffee;
		img.height = this.currentData[1] * coffee;
		div_window.setStyle({ width: 'auto' });
		div_window.setStyle({ 
			width: (img.getWidth() + parseInt(img.getStyle('marginLeft')) + parseInt(img.getStyle('marginRight'))) + 'px', 
			height: 'auto' 
		});
		
		div_title.show();
		div_desc.show();
		div_close.setStyle({
			right: (parseInt(div_window.style.width) - img.offsetLeft - img.getWidth()) + 'px',
			top: img.offsetTop + 'px',
		});
		div_prev.setStyle({
			left: img.offsetLeft + 'px',
			top: img.offsetTop + 'px',
			height: img.getHeight() + 'px'
		});
		div_next.setStyle({
			right: (parseInt(div_window.style.width) - img.offsetLeft - img.getWidth()) + 'px',
			top: img.offsetTop + 'px',
			height: img.getHeight() + 'px'
		});
		div_close.show();
		if (this.currentImage > 0)
			div_prev.show();
		if (this.currentImage + 1 < this.images.length)
			div_next.show();
		
		if (!Prototype.Browser.IE || this.ieVersion() >= 7.0)
		{
			div_window.setStyle({
				top: ((document.viewport.getHeight() - div_window.getHeight()) / 2 - parseInt(div_window.getStyle('marginTop'))) + 'px',
				left: ((document.viewport.getWidth() - div_window.getWidth()) / 2 - parseInt(div_window.getStyle('marginLeft'))) + 'px' 
			});
		}
		else
			this.ieScrollHandler(null);
	},
	
	ieScrollTimeoutFunction: undefined,
	
	ieScrollHandler: function (event)
	{

		if (typeof this.currentImage == 'undefined' || !Prototype.Browser.IE || this.ieVersion() >= 7.0)
			return;
		if (typeof this.ieScrollTimeoutFunction != 'undefined')
			window.clearTimeout(this.ieScrollTimeoutFunction);
		var fun = (function () {
			var so = document.viewport.getScrollOffsets();
			var div_window = $('zebooka_jsviewer_window');
			div_window.setStyle({ 
				top: (so[1] + (document.viewport.getHeight() - div_window.getHeight()) / 2 - parseInt(div_window.getStyle('marginTop'))) + 'px',
				left: (so[0] + (document.viewport.getWidth() - div_window.getWidth()) / 2 - parseInt(div_window.getStyle('marginLeft'))) + 'px' 
			});
			this.ieScrollTimeoutFunction = undefined;
		}).bind(this);
		
		if (event == null)
			fun();
		else
			this.ieScrollTimeoutFunction = window.setTimeout(fun, 100);
	},
	
	closeViewer: function ()
	{
		this.currentImage = undefined;
		document.stopObserving('keydown', this.keypressHandler);
		Event.stopObserving(window, 'resize', this.resizeHandler);
		if (Prototype.Browser.IE)
			Event.stopObserving(window, 'scroll', this.ieScrollHandler);
		$('zebooka_jsviewer_window').hide();
		$('zebooka_jsviewer_overlay').hide();

		if (this.options.hideSelectsEmbedsObjects)
			$$('select', 'object', 'embed').each(function (node) { node.style.visibility = 'visible'; });
	},
	
	end: ''
};

document.observe('dom:loaded', function (event) { Zebooka.JSViewer.initialize(); });


