(function($) {	var has_VML, has_canvas, create_canvas_for, add_shape_to, clear_canvas, shape_from_area,		canvas_style, hex_to_decimal, css3color, is_image_loaded, options_from_area;	has_VML = document.namespaces;	has_canvas = !!document.createElement('canvas').getContext;	if(!(has_canvas || has_VML)) {		$.fn.maphilight = function() { return this; };		return;	}		if(has_canvas) {		hex_to_decimal = function(hex) {			return Math.max(0, Math.min(parseInt(hex, 16), 255));		};		css3color = function(color, opacity) {			return 'rgba('+hex_to_decimal(color.substr(0,2))+','+hex_to_decimal(color.substr(2,2))+','+hex_to_decimal(color.substr(4,2))+','+opacity+')';		};		create_canvas_for = function(img) {			var c = $('<canvas style="width:'+img.width+'px;height:'+img.height+'px;"></canvas>').get(0);			c.getContext("2d").clearRect(0, 0, c.width, c.height);			return c;		};		var draw_shape = function(context, shape, coords, x_shift, y_shift) {			x_shift = x_shift || 0;			y_shift = y_shift || 0;						context.beginPath();			if(shape == 'rect') {				// x, y, width, height				context.rect(coords[0] + x_shift, coords[1] + y_shift, coords[2] - coords[0], coords[3] - coords[1]);			} else if(shape == 'poly') {				context.moveTo(coords[0] + x_shift, coords[1] + y_shift);				for(i=2; i < coords.length; i+=2) {					context.lineTo(coords[i] + x_shift, coords[i+1] + y_shift);				}			} else if(shape == 'circ') {				// x, y, radius, startAngle, endAngle, anticlockwise				context.arc(coords[0] + x_shift, coords[1] + y_shift, coords[2], 0, Math.PI * 2, false);			}			context.closePath();		}		add_shape_to = function(canvas, shape, coords, options, name) {			var i, context = canvas.getContext('2d');						// Because I don't want to worry about setting things back to a base state						// Shadow has to happen first, since it's on the bottom, and it does some clip /			// fill operations which would interfere with what comes next.			if(options.shadow) {				context.save();				if(options.shadowPosition == "inside") {					// Cause the following stroke to only apply to the inside of the path					draw_shape(context, shape, coords);					context.clip();				}								// Redraw the shape shifted off the canvas massively so we can cast a shadow				// onto the canvas without having to worry about the stroke or fill (which				// cannot have 0 opacity or width, since they're what cast the shadow).				var x_shift = canvas.width * 100;				var y_shift = canvas.height * 100;				draw_shape(context, shape, coords, x_shift, y_shift);								context.shadowOffsetX = options.shadowX - x_shift;				context.shadowOffsetY = options.shadowY - y_shift;				context.shadowBlur = options.shadowRadius;				context.shadowColor = css3color(options.shadowColor, options.shadowOpacity);								// Now, work out where to cast the shadow from! It looks better if it's cast				// from a fill when it's an outside shadow or a stroke when it's an interior				// shadow. Allow the user to override this if they need to.				var shadowFrom = options.shadowFrom;				if (!shadowFrom) {					if (options.shadowPosition == 'outside') {						shadowFrom = 'fill';					} else {						shadowFrom = 'stroke';					}				}				if (shadowFrom == 'stroke') {					context.strokeStyle = "rgba(0,0,0,1)";					context.stroke();				} else if (shadowFrom == 'fill') {					context.fillStyle = "rgba(0,0,0,1)";					context.fill();				}				context.restore();								// and now we clean up				if(options.shadowPosition == "outside") {					context.save();					// Clear out the center					draw_shape(context, shape, coords);					context.globalCompositeOperation = "destination-out";					context.fillStyle = "rgba(0,0,0,1);";					context.fill();					context.restore();				}			}						context.save();						draw_shape(context, shape, coords);						// fill has to come after shadow, otherwise the shadow will be drawn over the fill,			// which mostly looks weird when the shadow has a high opacity			if(options.fill) {				context.fillStyle = css3color(options.fillColor, options.fillOpacity);				context.fill();			}			// Likewise, stroke has to come at the very end, or it'll wind up under bits of the			// shadow or the shadow-background if it's present.			if(options.stroke) {				context.strokeStyle = css3color(options.strokeColor, options.strokeOpacity);				context.lineWidth = options.strokeWidth;				context.stroke();			}						context.restore();						if(options.fade) {				$(canvas).css('opacity', 0).animate({opacity: 1}, 100);			}		};		clear_canvas = function(canvas) {			canvas.getContext('2d').clearRect(0, 0, canvas.width,canvas.height);		};	} else {   // ie executes this code		create_canvas_for = function(img) {			return $('<var style="zoom:1;overflow:hidden;display:block;width:'+img.width+'px;height:'+img.height+'px;"></var>').get(0);		};		add_shape_to = function(canvas, shape, coords, options, name) {			var fill, stroke, opacity, e;			fill = '<v:fill color="#'+options.fillColor+'" opacity="'+(options.fill ? options.fillOpacity : 0)+'" />';			stroke = (options.stroke ? 'strokeweight="'+options.strokeWidth+'" stroked="t" strokecolor="#'+options.strokeColor+'"' : 'stroked="f"');			opacity = '<v:stroke opacity="'+options.strokeOpacity+'"/>';			if(shape == 'rect') {				e = $('<v:rect name="'+name+'" filled="t" '+stroke+' style="zoom:1;margin:0;padding:0;display:block;position:absolute;left:'+coords[0]+'px;top:'+coords[1]+'px;width:'+(coords[2] - coords[0])+'px;height:'+(coords[3] - coords[1])+'px;"></v:rect>');			} else if(shape == 'poly') {				e = $('<v:shape name="'+name+'" filled="t" '+stroke+' coordorigin="0,0" coordsize="'+canvas.width+','+canvas.height+'" path="m '+coords[0]+','+coords[1]+' l '+coords.join(',')+' x e" style="zoom:1;margin:0;padding:0;display:block;position:absolute;top:0px;left:0px;width:'+canvas.width+'px;height:'+canvas.height+'px;"></v:shape>');			} else if(shape == 'circ') {				e = $('<v:oval name="'+name+'" filled="t" '+stroke+' style="zoom:1;margin:0;padding:0;display:block;position:absolute;left:'+(coords[0] - coords[2])+'px;top:'+(coords[1] - coords[2])+'px;width:'+(coords[2]*2)+'px;height:'+(coords[2]*2)+'px;"></v:oval>');			}			e.get(0).innerHTML = fill+opacity;			$(canvas).append(e);		};		clear_canvas = function(canvas) {			$(canvas).find('[name=highlighted]').remove();		};	}		shape_from_area = function(area) {		var i, coords = area.getAttribute('coords').split(',');		for (i=0; i < coords.length; i++) { coords[i] = parseFloat(coords[i]); }		return [area.getAttribute('shape').toLowerCase().substr(0,4), coords];	};	options_from_area = function(area, options) {		var $area = $(area);		return $.extend({}, options, $.metadata ? $area.metadata() : false, $area.data('maphilight'));	};		is_image_loaded = function(img) {		if(!img.complete) { return false; } // IE		if(typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { return false; } // Others		return true;	};	canvas_style = {		position: 'absolute',		left: 0,		top: 0,		padding: 0,		border: 0	};		var ie_hax_done = false;	$.fn.maphilight = function(opts) {		opts = $.extend({}, $.fn.maphilight.defaults, opts);				if(!has_canvas && $.browser.msie && !ie_hax_done) {			document.namespaces.add("v", "urn:schemas-microsoft-com:vml");			var style = document.createStyleSheet();			var shapes = ['shape','rect', 'oval', 'circ', 'fill', 'stroke', 'imagedata', 'group','textbox'];			$.each(shapes,				function() {					style.addRule('v\\:' + this, "behavior: url(#default#VML); antialias:true");				}			);			ie_hax_done = true;		}				return this.each(function() {			var img, wrap, options, map, canvas, canvas_always, mouseover, highlighted_shape, usemap;			img = $(this);			if(!is_image_loaded(this)) {				// If the image isn't fully loaded, this won't work right.  Try again later.				return window.setTimeout(function() {					img.maphilight(opts);				}, 200);			}			options = $.extend({}, opts, $.metadata ? img.metadata() : false, img.data('maphilight'));			// jQuery bug with Opera, results in full-url#usemap being returned from jQuery's attr.			// So use raw getAttribute instead.			usemap = img.get(0).getAttribute('usemap');			map = $('map[name="'+usemap.substr(1)+'"]');			if(!(img.is('img') && usemap && map.size() > 0)) {				return;			}			if(img.hasClass('maphilighted')) {				// We're redrawing an old map, probably to pick up changes to the options.				// Just clear out all the old stuff.				var wrapper = img.parent();				img.insertBefore(wrapper);				wrapper.remove();				$(map).unbind('.maphilight').find('area[coords]').unbind('.maphilight');			}			wrap = $('<div></div>').css({				display:'block',				background:'url("'+this.src+'")',				position:'relative',				padding:0,				width:this.width,				height:this.height				});			if(options.wrapClass) {				if(options.wrapClass === true) {					wrap.addClass($(this).attr('class'));				} else {					wrap.addClass(options.wrapClass);				}			}			img.before(wrap).css('opacity', 0).css(canvas_style).remove();			if($.browser.msie) { img.css('filter', 'Alpha(opacity=0)'); }			wrap.append(img);						canvas = create_canvas_for(this);			$(canvas).css(canvas_style);			canvas.height = this.height;			canvas.width = this.width;						mouseover = function(e) {				var shape, area_options;				area_options = options_from_area(this, options);				if(					!area_options.neverOn					&&					!area_options.alwaysOn				) {					shape = shape_from_area(this);					add_shape_to(canvas, shape[0], shape[1], area_options, "highlighted");					if(area_options.groupBy) {						var areas;						// two ways groupBy might work; attribute and selector						if(/^[a-zA-Z][-a-zA-Z]+$/.test(area_options.groupBy)) {							areas = map.find('area['+area_options.groupBy+'="'+$(this).attr(area_options.groupBy)+'"]')						} else {							areas = map.find(area_options.groupBy);						}						var first = this;						areas.each(function() {							if(this != first) {								var subarea_options = options_from_area(this, options);								if(!subarea_options.neverOn && !subarea_options.alwaysOn) {									var shape = shape_from_area(this);									add_shape_to(canvas, shape[0], shape[1], subarea_options, "highlighted");								}							}						});					}					// workaround for IE7, IE8 not rendering the final rectangle in a group					if(!has_canvas) {						$(canvas).append('<v:rect></v:rect>');					}				}			}			$(map).bind('alwaysOn.maphilight', function() {				// Check for areas with alwaysOn set. These are added to a *second* canvas,				// which will get around flickering during fading.				if(canvas_always) {					clear_canvas(canvas_always)				}				if(!has_canvas) {					$(canvas).empty();				}				$(map).find('area[coords]').each(function() {					var shape, area_options;					area_options = options_from_area(this, options);					if(area_options.alwaysOn) {						if(!canvas_always && has_canvas) {							canvas_always = create_canvas_for(img.get());							$(canvas_always).css(canvas_style);							canvas_always.width = img.width();							canvas_always.height = img.height();							img.before(canvas_always);						}						area_options.fade = area_options.alwaysOnFade; // alwaysOn shouldn't fade in initially						shape = shape_from_area(this);						if (has_canvas) {							add_shape_to(canvas_always, shape[0], shape[1], area_options, "");						} else {							add_shape_to(canvas, shape[0], shape[1], area_options, "");						}					}				});			});						$(map).trigger('alwaysOn.maphilight').find('area[coords]')				.bind('mouseover.maphilight', mouseover)				.bind('mouseout.maphilight', function(e) { clear_canvas(canvas); });;						img.before(canvas); // if we put this after, the mouseover events wouldn't fire.						img.addClass('maphilighted');		});	};	$.fn.maphilight.defaults = {		fill: true,		fillColor: '647d9c',		fillOpacity: 0.3,		stroke: true,		strokeColor: 'ffffff',		strokeOpacity: 1,		strokeWidth: 2,		fade: true,		alwaysOn: false,		neverOn: false,		groupBy: false,		wrapClass: true,		// plenty of shadow:		shadow: true,		shadowX: 4,		shadowY: 4,		shadowRadius: 6,		shadowColor: '000000',		shadowOpacity: 0.7,		shadowPosition: 'outside',		shadowFrom: false	};})(jQuery);
