//MODIFIED by C Phillips charles@doublerebel.com for NO EVAL, extra events, options, and more 5 March 2009
(function($) {

	//If the UI scope is not availalable, add it
	$.ui = $.ui || {};
	
	//Make nodes selectable by expression
	$.extend($.expr[':'], { magnifier: "(' '+a.className+' ').indexOf(' ui-magnifier ')" });

	//Macros for external methods that support chaining
	var methods = "destroy,enable,disable,reset".split(","), i = methods.length - 1;
	for(;i > -1;i--) {
		var cur = methods[i];
		$.fn["magnifier"+cur.substr(0,1).toUpperCase()+cur.substr(1)] = function() {
			var a = arguments;
			return this.each(function() {
				if($(this).is(".ui-magnifier")) $(this).data("ui-magnifier")[cur](a);
			});
		};
	}

	//get instance method
	$.fn.extend({
		magnifierInstance: function() {
			return $(this[0]).is(".ui-magnifier") ? $(this).data("ui-magnifier") : false;
		},
		magnifier: function(options) {
			return this.each(function() {
				new $.ui.magnifier(this,options);	
			});
		}
	});
	
	$.ui.magnifier = function(el,options) {
		var self = this; this.items = []; this.element = el; $el = $(el).addClass("ui-magnifier");
		this.options = options || {}; var o = this.options;
		$el.data("ui-magnifier", this);
		o = $.extend({
			distance: 150,
			magnification: 2,
			baseline: 0,
			verticalLine: -0.5,
			items: '> *'
		}, o);
		
		this.pp = $el.offset({ border: false });
		
		$(o.items, el).each(function() {
			var co = $(this).offset({ border: false });
			if(self.options.overlap) var cp = $(this).position();
			self.items.push([this, co, [$(this).width(),$(this).height()], (cp || null)]);
			
			if(o.opacity)
				$(this).css('opacity', o.opacity.min);	
		});
	
		if(o.overlap) {
			for(var i = this.items.length - 1; i > -1; i--) {
				//Absolute stuff
				$(this.items[i][0]).css({
					position: "absolute",
					top: this.items[i][3].top,
					left: this.items[i][3].left
				});
			}
		}
		
		this.mousemoveEvent = function(e) { if(!self.disabled) self.magnify.apply(self, [e]); };
		$(document).bind("mousemove", this.mousemoveEvent);
		
		var events = 'mousedown,mouseup,click'.split(',');
		for (var i = events.length - 1; i > -1; i--) {
			var ei = events[i];
			if (o[ei]) {
				this[ei + 'Event'] = function(e) { if(!self.disabled) o[ei].apply(self, [e, { options: self.options, current: self.current[0], currentOffset: self.current[1] }]); };
				$el.bind(ei, this[ei + 'Event']);
			}
		} 
	};
	
	$.extend($.ui.magnifier.prototype, {
		destroy: function() {
			$(this.element).removeClass("ui-magnifier").removeClass("ui-magnifier-disabled");
			$(document).unbind("mousemove", this.mousemoveEvent);
			var events = 'mousedown,mouseup,click'.split(','), i = events.length - 1;
			for (; i > -1; i--)	if(this[events[i] + 'Event'])	$(this.element).unbind(events[i], this[events[i] + 'Event']);
		},
		enable: function() {
			$(this.element).removeClass("ui-magnifier-disabled");
			this.disabled = false;
		},
		disable: function() {
			$(this.element).addClass("ui-magnifier-disabled");
			this.reset();
			this.disabled = true;
		},
		reset: function(e) {
			var o = this.options, c, $c0, distance = 1, i = this.items.length - 1;
			
			for(;i > -1;i--) {
				c = this.items[i];
				$c0 = $(c[0]);
				$c0.css({
					width: c[2][0],
					height: c[2][1],
					top: (c[3] ? c[3].top : 0),
					left: (c[3] ? c[3].left : 0)
				});
				if(o.opacity) $c0.css({ opacity: o.opacity.min });
				if(o.zIndex) $c0.css({ zIndex:  ""});	
			}		
		},
		magnify: function(e) {
			var p = [e.pageX,e.pageY], o = this.options, c, $c0, iL = this.items.length, distance = 1, olddistance;
			this.current = this.items[0];
			
			//Adjust for offset/overlapped items
			if (o.offset) { p[0] += o.offset.x; p[1] += o.offset.y; }
	
			//Compute the parents distance, because we don't need to fire anything if we are not near the parent
			var overlap = (((p[0] > this.pp.left-o.distance) && (p[0] < this.pp.left + this.element.offsetWidth + o.distance)) && ((p[1] > this.pp.top-o.distance) && (p[1] < this.pp.top + this.element.offsetHeight + o.distance)));
			if(!overlap) return false;
			
			//for(;i>-1;i--) {
			for(var i=0; i < iL; i++) {
				c = this.items[i];
				$c0 = $(c[0]);
				if ($c0.data('disabled')) continue;
				olddistance = distance;
				if(!o.axis) {
					distance = Math.sqrt(
						  Math.pow(p[0] - ((c[3] ? this.pp.left : c[1].left) + parseInt(c[0].style.left)) - (c[0].offsetWidth/2), 2)
						+ Math.pow(p[1] - ((c[3] ? this.pp.top  : c[1].top ) + parseInt(c[0].style.top )) - (c[0].offsetHeight/2), 2)
					);
				} else {
					if(o.axis == "y") distance = Math.abs(p[1] - ((c[3] ? this.pp.top  : c[1].top ) + parseInt(c[0].style.top )) - (c[0].offsetHeight/2));
					else distance = Math.abs(p[0] - ((c[3] ? this.pp.left : c[1].left) + parseInt(c[0].style.left)) - (c[0].offsetWidth/2));
				}			
				if(distance < o.distance) {
					this.current = distance < olddistance ? c : this.current;
					if(!o.axis || o.axis != "y") {
						$c0.css({
							//width: c[2][0]+ (c[2][0] * (o.magnification-1)) - (((distance/o.distance)*c[2][0]) * (o.magnification-1)),
							left: (c[3] ? (c[3].left + o.verticalLine * ((c[2][1] * (o.magnification-1)) - (((distance/o.distance)*c[2][1]) * (o.magnification-1)))) : 0)
						});
					}
					if(!o.axis || o.axis != "x") {
						$c0.css({
							//height: c[2][1]+ (c[2][1] * (o.magnification-1)) - (((distance/o.distance)*c[2][1]) * (o.magnification-1)),
							top: (c[3] ? c[3].top : 0) + (o.baseline-0.5) * ((c[2][0] * (o.magnification-1)) - (((distance/o.distance)*c[2][0]) * (o.magnification-1)))
						});					
					}
					if(o.opacity) $c0.css('opacity', o.opacity.max-(distance/o.distance) < o.opacity.min ? o.opacity.min : o.opacity.max-(distance/o.distance));
				} else {
					$c0.css({
						width: c[2][0],
						height: c[2][1],
						top: (c[3] ? c[3].top : 0),
						left: (c[3] ? c[3].left : 0)
					});
					if(o.opacity) $c0.css({ opacity: o.opacity.min });				
				}
				if(o.zIndex) $c0.css({ zIndex: "" });
			}
			if (this.options.zIndex) $(this.current).css({ zIndex: this.options.zIndex });
		}
	});

})(jQuery);
