
	function initImageZoom(_options) {
		var options = $extend({
			rel: 'imagezoom'
		}, _options || {});
		var elements = $$(document.links).filter(function(el) {
			if ((el.rel) && (el.rel.indexOf(options.rel) != -1)) 
				return true;
			else
				return false;
		});
		for (var i = 0; i < elements.length; i++) {
			var el = elements[i];
			el.addEvent("click", function() {
				this.blur();
				var sEl = this;
				var imgCap = "";
				if (this.getElements("img").length > 0)
					sEl = this.getElements("img")[0];
				if ((sEl.alt) && (sEl.alt != ""))
					imgCap = sEl.alt;
				else if (sEl.title)
					imgCap = sEl.title;
				else if (sEl.parentNode.title)
					imgCap = sEl.parentNode.title;
				var _options = $extend({
					image: this.href,
					caption: imgCap,
					startElement: sEl
				}, options || {});
				_options.image = this.href;
				_options.caption = imgCap;
				var imagezoom = new Imagezoom(_options);
				imagezoom.preloadImage();
				imagezoom.show();
				return false;
			});
		}
	}
	
	var Imagezoom = function(_options) {		
		var options = $extend({
			image: false,
			caption: "",
			enableCaptions: true,
			startElement: false,
			x: 10,
			y: 10,
			initWidth: 50,
			initHeight: 50,
			draggable: true,
			loadImage: "/js/imagezoom/images/loading.gif",
			loadDelay: 150,
			duration: 800,
			closeDuration: 500,
			transition: Fx.Transitions.Cubic.easeOut,
			startOpacity: 0.6,
			closeText: 'Close',
			rel: 'imagezoom',
			showCaptionBar: true,
			overlay: false,
			overlayColor: "#000",
			overlayOpacity: .75
		}, _options || {});
		
		var box = document.createElement("div");		
		var instance = this;
		
		/* shadow divs */
		var tl = document.createElement("div");
		tl.className = "s s_tl";
		var tr = document.createElement("div");
		tr.className = "s s_tr";
		var bl = document.createElement("div");
		bl.className = "s s_bl";
		var br = document.createElement("div");
		br.className = "s s_br";
		var top = document.createElement("div");
		top.className = "s s_top";
		var bottom = document.createElement("div");
		bottom.className = "s s_bottom";
		var left = document.createElement("div");
		left.className = "s s_left";
		var right = document.createElement("div");
		right.className = "s s_right";
		
		this.preloadImage = function() {
			if (options.image != false) {
				var img = new Image();
				img.src = options.image;
				img.style.visibility = "hidden";
				img.style.position = "absolute";
				img.style.top = "-9999999999px";
				img.setAttribute("id", "imagezoom-" + options.image);
				$$('body')[0].appendChild(img);
			}	
		}
		
		this.getImage = function() {
			if (($('imagezoom-' + options.image)) && ($('imagezoom-' + options.image).width != "0")) {
				var img = $('imagezoom-' + options.image).clone();
				img.setAttribute("id", "");
				img.style.position = "relative";
				img.style.top = "0px";
				img.style.visibility = "visible";
			} else {
				instance.preloadImage();
				window.setTimeout(function() {
					instance.getImage();
				}, 50);
			}
			return img;
		}		
		
		this.show = function() {
			if (options.image != false) {
				box.style.position = "absolute";
				box.style.overflow = "hidden";
				box.setAttribute("id", "imagezoom-open-" + options.image);
				
				if (options.startElement != false)
					options.startElement.blur();
				
				var x = options.x;
				var y = options.y;
				var boxWidth = options.initWidth;
				var boxHeight = options.initHeight;
				if (options.startElement != false) {
					x = options.startElement.getPosition().x;
					y = options.startElement.getPosition().y;
					boxWidth = options.startElement.offsetWidth;
					boxHeight = options.startElement.offsetHeight;
				}
				box.style.left = x + "px";
				box.style.top = y + "px";
				box.style.width = boxWidth + "px";
				box.style.height = boxHeight + "px";
				
				var fx = new Fx.Morph(box);
				fx.set({opacity: options.startOpacity});
				
				box.className = "imagezoom";
				$$('body')[0].appendChild(box);
				box.style.cursor = "pointer";
				box.addEvent("click", function() {
					var fx = new Fx.Morph(box, {duration: 200});
					fx.start({opacity: 0}).chain(function() {
						$$('body')[0].removeChild(box);
					});
				});

				this.loadImage();
			}
		}
		
		this.loadImage = function() {
			if (box.getElements(".loading").length == 0) {
				var loading = new Image();
				loading.src = options.loadImage;
				loading.className = "loading";
				box.appendChild(loading);
			}	
			if ($('imagezoom-' + options.image)) {
				var el = $('imagezoom-' + options.image);
				if (el.width != "0") {
					var newEl = new Image();
					newEl.src = options.image;
					window.setTimeout(function() { instance.insertImage(newEl) }, options.loadDelay);
				} else {
					window.setTimeout(function() { instance.loadImage(); }, 50);
				}
			} else {
				instance.preloadImage();
				window.setTimeout(function() { instance.loadImage(); }, 50);
			}
		}
		
		this.insertImage = function(img) {
			box.removeEvents("click");
			box.style.cursor = "default";
			box.style.overflow = "visible";
			var w = img.width;
			var h = img.height;
			img.style.width = w + "px";
			img.style.height = h + "px";
			img.className = 'image';
			var ptop = (window.getSize().y / 2) + window.getScroll().y - (h/2);
			var pleft = (window.getSize().x / 2) + window.getScroll().x - (w/2);
			var fx = new Fx.Morph(box, {duration: options.duration, transition: options.transition});
			fx.start({
				top: ptop,
				left: pleft,
				width: w,
				height: h,
				opacity: 1
			}).chain(function() {
				if (options.overlay == true) {
					if (!$('imagezoom_overlay')) {
						var overlay = $(document.createElement("div"));
						overlay.setAttribute("id", "imagezoom_overlay");
						overlay.style.backgroundColor = options.overlayColor;
						overlay.setOpacity(0);
						$$('body')[0].appendChild(overlay);
					} else {
						var overlay = $('imagezoom_overlay');
					}
					overlay.style.width = window.getScrollSize().x + "px";
					overlay.style.height = window.getScrollSize().y + "px";
					var overlayfx = new Fx.Morph(overlay, {duration: 600});
					overlayfx.start({
						opacity: options.overlayOpacity
					});
				}
				var close = $(document.createElement("div"));
				close.innerHTML = "<span>" + options.closeText + "</span>";
				close.className = "close";
				close.addEvent("click", function() {
					instance.close(true);
				});
				var loading = box.getElements(".loading");
				if (loading.length > 0)
					box.removeChild(loading[0]);
				var elements = [close, tl, tr, bl, br, top, bottom, left, right, img];
				for (var i = 0; i < elements.length; i++) {
					var elFx = new Fx.Morph(elements[i], {duration: 600});
					elFx.set({opacity: 0});
					box.adopt(elements[i]);
					elFx.start({opacity: 1});
				}
				var caption;
				if ((options.caption != "") && (options.enableCaptions == true)) {
					caption = document.createElement("div");
					caption.className = "caption";
					caption.innerHTML = "<p>" + options.caption + "</p>";
					box.appendChild(caption);
				}
				instance.addSetNavigation();
				if (box.getElements(".caption").length > 0) {
					caption = box.getElements(".caption")[0];
					var cfx = new Fx.Morph(caption, {duration: 200});
					cfx.set({opacity: 0});
					if (options.showCaptionBar == true) {
						caption.className += " visibleCaption";
						var cStartFx = new Fx.Morph(caption, {duration: 600});
						cStartFx.start({
							opacity: 1
						});
					}
					box.addEvent("mouseenter", function() {
						cfx.start({opacity: 1}).chain(function() { caption.className += " visibleCaption"; });
					});
					box.addEvent("mouseleave", function() {
						cfx.start({opacity: 0}).chain(function() { caption.className = caption.className.replace(/visibleCaption/g, ""); });
					});
					close.addEvent("mouseenter", function() {
						cfx.start({opacity: 0}).chain(function() { caption.className = caption.className.replace(/visibleCaption/g, ""); });
					});
					box.getElements(".image")[0].addEvent("click", function() {
						var action = "show";
						if (caption.className.indexOf("visibleCaption") != -1)
							action = "hide";
						if (action == "show")
							cfx.start({opacity: 1}).chain(function() { caption.className += " visibleCaption"; });
						else
							cfx.start({opacity: 0}).chain(function() { caption.className = caption.className.replace(/visibleCaption/g, ""); });
					});				
				}
				top.style.width = box.offsetWidth + "px";
				bottom.style.width = box.offsetWidth + "px";
				left.style.height = box.offsetHeight + "px";
				right.style.height = box.offsetHeight + "px";
				if (options.draggable == true)
					var move = new Drag.Move(box, {handle: img});
			});		
		}
		
		this.addSetNavigation = function() {
			var links = $$(document.links).filter(function(link) {
				if ((link.rel) && (link.rel.indexOf(options.rel) != -1))
					return true;
				else
					return false;
			});
			var set = false;
			for (var i = 0; i < links.length; i++) {
				if ((links[i].href.indexOf(options.image) != -1) && (links[i].rel) && (links[i].rel.indexOf(options.rel + '[' != -1))) {
					var rel = links[i].getAttribute("rel");
					set = instance.scanRel("after", options.rel + "[", this.scanRel("before", "]", rel));
				}
			}
			if (set != false) {
				var prevLink = false;
				var nextLink = false;
				var setLinks = new Array();
				for (i = 0; i < links.length; i++) {
					if (links[i].rel.indexOf(options.rel + "[" + set + "]") != -1) {
						setLinks[setLinks.length] = links[i];
					}
				}
				for (i = 0; i < setLinks.length; i++) {
					var link = setLinks[i];
					if ((link.href.indexOf(options.image) != -1) && (link.rel) && (link.rel.indexOf(options.rel != -1))) {
						if (i != 0)
							prevLink = setLinks[i - 1];
						if (i != setLinks.length - 1)
							nextLink = setLinks[i + 1];
					}
				}
				if ((prevLink != false) || (nextLink != false)) {
					if (box.getElements(".caption").length == 0) {
						var caption = document.createElement("div");
						caption.className = "caption";
						box.appendChild(caption);
					} else {
						var caption = box.getElements(".caption")[0];
					}
				}
				if (prevLink != false) {
					var previousButton = $(document.createElement("div"));
					previousButton.className = "previous";
					var prevCap = '';
					if (prevLink.title)
						prevCap = prevLink.title;
					var prevEl = prevLink;
					if (prevLink.getElements("img").length > 0)
						prevEl = prevLink.getElements("img")[0];
					previousButton.addEvent("click", function() {
						var newOptions = $unlink(options);
						var imagezoomPrev = new Imagezoom($extend(newOptions, {
							image: prevLink.href,
							caption: prevCap,
							rel: options.rel,
							startElement: prevEl,
							showCaptionBar: true
						}));
						instance.close();
						imagezoomPrev.show();
					});
					caption.appendChild(previousButton);
				}
				if (nextLink != false) {
					var nextButton = $(document.createElement("div"));
					nextButton.className = "next";
					var nextCap = '';
					if (nextLink.title)
						nextCap = nextLink.title;
					var nextEl = nextLink;
					if (nextLink.getElements("img").length > 0)
						nextEl = nextLink.getElements("img")[0];
					nextButton.addEvent("click", function() {
						var newOptions = $unlink(options);
						var imagezoomNext = new Imagezoom($extend(newOptions, {
							image: nextLink.href,
							caption: nextCap,
							rel: options.rel,
							startElement: nextEl,
							showCaptionBar: true
						}));
						instance.close();
						imagezoomNext.show();
					});
					caption.appendChild(nextButton);					
				}
			}
		}
		
		this.scanRel = function(where, needle, string) {
			var newstring = '';
			if (where == "after") {
				var startpos = string.indexOf(needle) + needle.length;
				var endpos = string.length;
			} else if (where == "before") {
				var startpos = 0;
				var endpos = string.indexOf(needle);
			}
			for (var i = startpos; i < endpos; i++) {
				newstring += string.charAt(i);
			}
			return newstring;
		}
		
		this.close = function(hideOverlay) {
			var img = box.getElements(".image")[0];
			box.removeChild(img);
			var close = box.getElements(".close")[0];
			box.removeChild(close);
			var caption = box.getElements(".caption");
			if (caption.length > 0)
				box.removeChild(caption[0]);
			var s = box.getElements(".s");
			for (var i = 0; i < s.length; i++)
				box.removeChild(s[i]);
			var x = options.x;
			var y = options.y;
			var boxWidth = options.initWidth;
			var boxHeight = options.initHeight;
			if (options.startElement != false) {
				x = options.startElement.getPosition().x;
				y = options.startElement.getPosition().y;
				boxWidth = options.startElement.offsetWidth;
				boxHeight = options.startElement.offsetHeight;
			}
			if ((hideOverlay == true) && ($('imagezoom_overlay'))) {
				var oFx = new Fx.Morph($('imagezoom_overlay'), {duration: options.closeDuration});
				oFx.start({opacity: 0}).chain(function() {
					$$('body')[0].removeChild($('imagezoom_overlay'));
				});
			}
			var fx = new Fx.Morph(box, {duration: options.closeDuration});
			fx.start({
				left: x,
				top: y,
				width: boxWidth,
				height: boxHeight,
				opacity: options.startOpacity
			}).chain(function() {
				fx.start({
					opacity: 0
				}).chain(function() {
					$$('body')[0].removeChild(box);
				});
			});
		}	
	}
	
/*
Script: Drag.js
	The base Drag Class. Can be used to drag and resize Elements using mouse events.

License:
	MIT-style license.
*/

var Drag = new Class({

	Implements: [Events, Options],

	options: {/*
		onBeforeStart: $empty,
		onStart: $empty,
		onDrag: $empty,
		onCancel: $empty,
		onComplete: $empty,*/
		snap: 6,
		unit: 'px',
		grid: false,
		style: true,
		limit: false,
		handle: false,
		invert: false,
		preventDefault: false,
		modifiers: {x: 'left', y: 'top'}
	},

	initialize: function(){
		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
		this.element = $(params.element);
		this.document = this.element.getDocument();
		this.setOptions(params.options || {});
		var htype = $type(this.options.handle);
		this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
		this.mouse = {'now': {}, 'pos': {}};
		this.value = {'start': {}, 'now': {}};
		
		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
		
		this.bound = {
			start: this.start.bind(this),
			check: this.check.bind(this),
			drag: this.drag.bind(this),
			stop: this.stop.bind(this),
			cancel: this.cancel.bind(this),
			eventStop: $lambda(false)
		};
		this.attach();
	},

	attach: function(){
		this.handles.addEvent('mousedown', this.bound.start);
		return this;
	},

	detach: function(){
		this.handles.removeEvent('mousedown', this.bound.start);
		return this;
	},

	start: function(event){
		if (this.options.preventDefault) event.preventDefault();
		this.fireEvent('beforeStart', this.element);
		this.mouse.start = event.page;
		var limit = this.options.limit;
		this.limit = {'x': [], 'y': []};
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
			else this.value.now[z] = this.element[this.options.modifiers[z]];
			if (this.options.invert) this.value.now[z] *= -1;
			this.mouse.pos[z] = event.page[z] - this.value.now[z];
			if (limit && limit[z]){
				for (var i = 2; i--; i){
					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
				}
			}
		}
		if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
		this.document.addEvent(this.selection, this.bound.eventStop);
	},

	check: function(event){
		if (this.options.preventDefault) event.preventDefault();
		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
		if (distance > this.options.snap){
			this.cancel();
			this.document.addEvents({
				mousemove: this.bound.drag,
				mouseup: this.bound.stop
			});
			this.fireEvent('start', this.element).fireEvent('snap', this.element);
		}
	},

	drag: function(event){
		if (this.options.preventDefault) event.preventDefault();
		this.mouse.now = event.page;
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
			if (this.options.invert) this.value.now[z] *= -1;
			if (this.options.limit && this.limit[z]){
				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
					this.value.now[z] = this.limit[z][1];
				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
					this.value.now[z] = this.limit[z][0];
				}
			}
			if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
			if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
			else this.element[this.options.modifiers[z]] = this.value.now[z];
		}
		this.fireEvent('drag', this.element);
	},

	cancel: function(event){
		this.document.removeEvent('mousemove', this.bound.check);
		this.document.removeEvent('mouseup', this.bound.cancel);
		if (event){
			this.document.removeEvent(this.selection, this.bound.eventStop);
			this.fireEvent('cancel', this.element);
		}
	},

	stop: function(event){
		this.document.removeEvent(this.selection, this.bound.eventStop);
		this.document.removeEvent('mousemove', this.bound.drag);
		this.document.removeEvent('mouseup', this.bound.stop);
		if (event) this.fireEvent('complete', this.element);
	}

});

Element.implement({
	
	makeResizable: function(options){
		return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
	}

});

/*
Script: Drag.Move.js
	A Drag extension that provides support for the constraining of draggables to containers and droppables.

License:
	MIT-style license.
*/

Drag.Move = new Class({

	Extends: Drag,

	options: {
		droppables: [],
		container: false
	},

	initialize: function(element, options){
		this.parent(element, options);
		this.droppables = $$(this.options.droppables);
		this.container = $(this.options.container);
		if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
		element = this.element;
		
		var current = element.getStyle('position');
		var position = (current != 'static') ? current : 'absolute';
		if (element.getStyle('left') == 'auto' || element.getStyle('top') == 'auto') element.position(element.getPosition(element.offsetParent));
		
		element.setStyle('position', position);
		
		this.addEvent('start', function(){
			this.checkDroppables();
		}, true);
	},

	start: function(event){
		if (this.container){
			var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cps[pad] = cont.getStyle('padding-' + pad).toInt();
				ems[pad] = el.getStyle('margin-' + pad).toInt();
			}, this);

			var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
			var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
			var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];

			this.options.limit = {x: x, y: y};
		}
		this.parent(event);
	},

	checkAgainst: function(el){
		el = el.getCoordinates();
		var now = this.mouse.now;
		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
	},

	checkDroppables: function(){
		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
		if (this.overed != overed){
			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
			if (overed){
				this.overed = overed;
				this.fireEvent('enter', [this.element, overed]);
			} else {
				this.overed = null;
			}
		}
	},

	drag: function(event){
		this.parent(event);
		if (this.droppables.length) this.checkDroppables();
	},

	stop: function(event){
		this.checkDroppables();
		this.fireEvent('drop', [this.element, this.overed]);
		this.overed = null;
		return this.parent(event);
	}

});

Element.implement({

	makeDraggable: function(options){
		return new Drag.Move(this, options);
	}

});



