// Change the specified image
function setImage(elem, img) {
	elem.setAttribute('src', img);
}

// Array to store preloaded images.
var preload_images_arr = new Array();

// Preload images so that there was no flickering when showing previously invisible
// (and not loaded by the browser) picture.
function preload_images(image_names) {
	for (var i=0; i<image_names.length; ++i) {
		preload_images_arr[i] = new Image;
		preload_images_arr[i].src = image_names[i];
	}
}

// Calculates absolute position of the element on the page
function getAbsolutePosition(elem) {
	var left = 0;
	var top = 0;
	if (!elem.offsetParent)
		return {x: elem.offsetLeft, y: elem.offsetTop};
	else {
		do {
			left += elem.offsetLeft;
			top += elem.offsetTop;
		} while (elem = elem.offsetParent);
		return {x: left, y: top};
	}
}

// Performs addition of each field of the two rectangles
function rectangleAdd(rect1, rect2) {
	return {
		left:   rect1.left   + rect2.left,
		top:    rect1.top    + rect2.top,
		width:  rect1.width  + rect2.width,
		height: rect1.height + rect2.height
	};
}

// Performs subtraction of each field of the two rectangles
function rectangleSub(rect1, rect2) {
	return {
		left:   rect1.left   - rect2.left,
		top:    rect1.top    - rect2.top,
		width:  rect1.width  - rect2.width,
		height: rect1.height - rect2.height
	};
}

// Returns negative of each field of the rectangle
function rectangleNeg(rect1) {
	return {
		left:   -rect1.left,
		top:    -rect1.top,
		width:  -rect1.width,
		height: -rect1.height
	};
}

// Performs division of each field of the rectangle by a number
function rectangleDiv(rect1, num) {
	return {
		left:   rect1.left   / num,
		top:    rect1.top    / num,
		width:  rect1.width  / num,
		height: rect1.height / num
	};
}

// expandingImage objects
var expanding_div;             // For expanding
var collapsing_div;            // For collapsing

// Class providing expandable image
// elem:       thumbnail IMG element, the source for expanding
// obj_name:   name of the object implementing this class
// img_file:   new image file to show in the expanded DIV
// img_width:  width of the expanded image
// img_height: height of the expanded image
function expandingImage(elem, obj_name, img_file, img_width, img_height) {
	// Remember input parameters
	this.thumb_elem = elem;
	this.obj_name = obj_name;
	this.img_file = img_file;
	this.img_width = img_width;
	this.img_height = img_height;

	// Find significant HTML DOM elements
	this.img_div = document.getElementById('show-image-div');
	this.img_div_img = document.getElementById('show-image-img');
	this.img_div_loading = document.getElementById('show-image-loading');

	// Preload the image while expanding
	this.img_load = new Image;
	this.img_load.src = img_file;

	// Current status
	this.expanded = false;      // image is expanded (yes/no)
	this.loading = false;       // image is expanded, but the browser did not load the file yet

	// Parameters for expanding/collapsing
	this.steps = 15;            // Number of steps
	this.delta_time = 15;       // Time in milliseconds between steps

	// Fixed positions
	this.thumb_pos = {left: 0, top: 0, width: 0, height: 0};    // Position of the source thumbnail
	this.large_pos = {left: 0, top: 0, width: 0, height: 0};    // Target position of the expanded image
	this.d_img_pos = {left: 0, top: 0, width: 0, height: 0};    // One-step delta from thumbnail to expanded image
	this.d_cnv_pos = {left: 0, top: 0, width: 0, height: 0};    // One-step delta for DIV

	// Variable positions
	this.img_cur_pos = {left: 0, top: 0, width: 0, height: 0};  // Current image position (start + delta * steps)
	this.div_cur_pos = {left: 0, top: 0, width: 0, height: 0};  // Current DIV position
	this.img_end_pos = {left: 0, top: 0, width: 0, height: 0};  // End image position (large for expanding, thumbnail for collapsing)
	this.div_end_pos = {left: 0, top: 0, width: 0, height: 0};  // End DIV position

	// Canvas parameters
	this.div_canvas = {left: -15, top: -15, width: 30, height: 30};     // Margin size (canvas around the image)
	this.div_border = 5;                                                // Expanded border width

	// Re-calculates the positions of the HTML elements and delta
	this.prepare = function() {
		// Determine and save the thumbnail position
		var pos = getAbsolutePosition(elem);
		var scrollX;
		var scrollY;
		if (typeof(window.pageXOffset) != 'undefined')
			scrollX = window.pageXOffset;
		else
			scrollX = document.body.parentElement.scrollLeft;
		if (typeof(window.pageYOffset) != 'undefined')
			scrollY = window.pageYOffset;
		else
			scrollY = document.body.parentElement.scrollTop;
		this.thumb_pos.left = pos.x - scrollX;
		this.thumb_pos.top = pos.y - scrollY;
		this.thumb_pos.width = elem.offsetWidth;
		this.thumb_pos.height = elem.offsetHeight;

		// Determine and save the large image position
		// Do not re-calculate if we're going to collapse: position of the large image could not change
		if (this.obj_name != 'collapsing_div') {
			this.large_pos.width = this.img_width;
			this.large_pos.height = this.img_height;
			if (typeof(window.innerWidth) != 'undefined')
				this.large_pos.left = Math.round((window.innerWidth - this.large_pos.width) / 2);
			else
				this.large_pos.left = Math.round((document.body.offsetWidth - this.large_pos.width) / 2);
			if (typeof(window.innerHeight) != 'undefined')
				this.large_pos.top = Math.round((window.innerHeight - this.large_pos.height) / 2);
			else
				this.large_pos.top = Math.round((document.body.offsetHeight - this.large_pos.height) / 2);
		}

		// Single step difference (thumbnail -> large image)
		var max_delta = rectangleSub(this.large_pos, this.thumb_pos);
		this.d_img_pos = rectangleDiv(max_delta, this.steps);
		this.d_cnv_pos = rectangleDiv(rectangleAdd(max_delta, this.div_canvas), this.steps);
	}

	// Starts the expanding process
	this.expand = function() {
		// Calculate positions
		this.prepare();

		// Begin from thumbnail, end with large image
		this.img_end_pos = this.large_pos;
		this.div_end_pos = rectangleAdd(this.img_end_pos, this.div_canvas);
		this.img_cur_pos = this.thumb_pos;
		this.div_cur_pos = this.thumb_pos;

		// Set starting position and show DIV
		this.img_div.style.left = this.thumb_pos.left + 'px';
		this.img_div.style.top = this.thumb_pos.top + 'px';
		this.img_div.style.width = this.thumb_pos.width + 'px';
		this.img_div.style.height = this.thumb_pos.height + 'px';
		this.img_div_img.style.left = this.thumb_pos.left + 'px';
		this.img_div_img.style.top = this.thumb_pos.top + 'px';
		this.img_div_img.style.width = this.thumb_pos.width + 'px';
		this.img_div_img.style.height = this.thumb_pos.height + 'px';
		this.img_div_img.setAttribute('src', this.thumb_elem.getAttribute('src'));
		this.img_div.style.display = '';

		// Go!
		setTimeout(this.obj_name + '.nextStep(' + this.steps + ')', 0);
	};

	// Starts the collapsing process
	// obj_name: new object name
	this.collapse = function(obj_name) {
		this.obj_name = obj_name;
		if (!this.expanded)
			return;
		this.img_div_loading.style.display = 'none';

		// Calculate positions
		this.prepare();

		// Begin from large image, end with thumbnail
		// cur_pos is already set to the large image position
		this.img_end_pos = this.thumb_pos;
		this.div_end_pos = this.thumb_pos;

		// Reverse the delta (this.prepare() sets it for expanding process)
		this.d_img_pos = rectangleNeg(this.d_img_pos);
		this.d_cnv_pos = rectangleNeg(this.d_cnv_pos);
		this.img_div.style.borderWidth = '1px';

		// Go!
		setTimeout(this.obj_name + '.nextStep(' + this.steps + ')', 0);
	};

	// Performs the next step of the expanding/collapsing
	this.nextStep = function(steps_left) {
		// Add delta to the current position
		this.img_cur_pos = rectangleAdd(this.img_cur_pos, this.d_img_pos);
		this.div_cur_pos = rectangleAdd(this.div_cur_pos, this.d_cnv_pos);

		// Change DIV and internal IMG sizes
		this.img_div.style.left = Math.floor(this.div_cur_pos.left) + 'px';
		this.img_div.style.top = Math.floor(this.div_cur_pos.top) + 'px';
		this.img_div.style.width = Math.floor(this.div_cur_pos.width) + 'px';
		this.img_div.style.height = Math.floor(this.div_cur_pos.height) + 'px';
		this.img_div_img.style.left = Math.floor(this.img_cur_pos.left) + 'px';
		this.img_div_img.style.top = Math.floor(this.img_cur_pos.top) + 'px';
		this.img_div_img.style.width = Math.floor(this.img_cur_pos.width) + 'px';
		this.img_div_img.style.height = Math.floor(this.img_cur_pos.height) + 'px';

		// If not last step, repeat
		--steps_left;
		if (steps_left)
			setTimeout(this.obj_name + '.nextStep(' + steps_left + ')', this.delta_time);
		else {
			// Finalizing: set the DIV/IMG positions exactly to the desired (to avoid rounding errors)
			this.img_cur_pos = this.img_end_pos;
			this.div_cur_pos = this.div_end_pos;
			this.img_div.style.left       = this.div_end_pos.left   + 'px';
			this.img_div.style.top        = this.div_end_pos.top    + 'px';
			this.img_div.style.width      = this.div_end_pos.width  + 'px';
			this.img_div.style.height     = this.div_end_pos.height + 'px';
			this.img_div_img.style.left   = this.img_end_pos.left   + 'px';
			this.img_div_img.style.top    = this.img_end_pos.top    + 'px';
			this.img_div_img.style.width  = this.img_end_pos.width  + 'px';
			this.img_div_img.style.height = this.img_end_pos.height + 'px';
			this.expanded = true;

			// There's still some work to do...
			setTimeout(this.obj_name + '.end()', 0);
		}
	};

	// Performs final steps
	this.end = function() {
		if (this.obj_name == 'collapsing_div') {
			// For collapsing hide the DIV, so that old thumbnail was visible
			this.img_div.style.display = 'none';
			collapsing_div = null;

			// There is a new image expander is waiting, start it
			if (expanding_div)
				setTimeout('expanding_div.expand()', 0);
		}
		else {
			this.img_div.style.borderWidth = this.div_border + 'px';
			// For expanding check whether the image file is loaded by browser
			if (!this.img_load.complete) {
				// Nope...
				if (this.expanded) {
					// Show the additional info about the loading process, if necessary...
					if (!this.loading) {
						this.img_div_loading.style.left   = this.img_end_pos.left   + 'px';
						this.img_div_loading.style.top    = this.img_end_pos.top    + 'px';
						this.img_div_loading.style.width  = this.img_end_pos.width  + 'px';
						this.img_div_loading.style.height = this.img_end_pos.height + 'px';
						this.img_div_loading.style.display = '';
						this.loading = true;
					}

					// ...and will check again a little bit later.
					setTimeout(this.obj_name + '.end()', 100);
				}
			}
			else {
				// Yep.
				if (this.loading) {
					// The "loading" info was shown, hide it.
					this.img_div_loading.style.display = 'none';
					this.loading = false;
				}
				// Show the real large image instead of expanded thumbnail
				this.img_div_img.setAttribute('src', this.img_load.src);
			}
		}
	};
}

// Hi-level function to expand image.
// elem:       thumbnail IMG element, the source for expanding
// img_file:   new image file to show in the expanded DIV
// img_width:  width of the expanded image
// img_height: height of the expanded image
function expandImage(elem, img, img_width, img_height) {
	// Check if we need to collapse the previous image first
	var need_collapse = (expanding_div && expanding_div.expanded);
	if (need_collapse) {
		// If yes, then start collapsing
		collapsing_div = expanding_div;
		collapsing_div.collapse('collapsing_div');
	}
	// If the same thumbnail was clicked whose image is now shown, do not expand it after collapsing!
	if (!expanding_div || expanding_div.img_file != img) {
		expanding_div = new expandingImage(elem, 'expanding_div', img, img_width, img_height);
		// If collapsing is in progress, we cannot start expanding. Damn asynchronism!
		// In this case image collapser has to take care of the expander.
		if (!need_collapse)
			expanding_div.expand();
	}
	else
		expanding_div = null;
}

// Hi-level function to collapse currently expanded image, if any.
function collapseImage() {
	if (expanding_div) {
		// Just start collapsing
		collapsing_div = expanding_div;
		collapsing_div.collapse('collapsing_div');
		expanding_div = null;
	}
}

// Toggles table lines containing older versions
function show_hide_versions(img, id) {
	var tds = document.getElementsByTagName('td');
	var first_idx = 0;
	id += '-';
	for (var i = 0; i < tds.length; ++i)
		if (tds[i].id.indexOf(id) == 0) {
			first_idx = i;
			break;
		}
	if (i == tds.length)
		return;
	var show = (tds[first_idx].style.display == 'none');
	if (show) {
		img.setAttribute('src', img.getAttribute('src').replace(/plus\.png/, 'minus.png'));
		img.setAttribute('alt', '[-]');
		for (var i = first_idx; i < tds.length; ++i)
			if (tds[i].id.indexOf(id) == 0)
				tds[i].style.display = '';
	}
	else {
		img.setAttribute('src', img.getAttribute('src').replace(/minus\.png/, 'plus.png'));
		img.setAttribute('alt', '[+]');
		for (var i = first_idx; i < tds.length; ++i)
			if (tds[i].id.indexOf(id) == 0)
				tds[i].style.display = 'none';
	}
}
