/*
 * Orginal: http://adomas.org/javascript-mouse-wheel/
 * prototype extension by "Frank Monnerjahn" themonnie @gmail.com
 */
Object.extend(Event, {
	wheel:function (event){
	        var delta = 0;
	        if (!event) event = window.event;
	        if (event.wheelDelta) {
	                delta = event.wheelDelta/120;
	        } else if (event.detail) { delta = -event.detail/3; }
	        return Math.round(delta); //Safari Round
	}
});
/*
 * end of extension
 */
 

/** Some useful constants */
Constants = {
	// X coordinate of the left-most pixel 
	MIN_BASE_X: 720599.600,
	// Y coordinate of the bottom-most pixel
	MIN_BASE_Y: 167800.400,
	// X coordinate of the right most pixel (not tile!)
	MAX_BASE_X: 723799.600,
	// Y coordinate of the top-most pixel
	MAX_BASE_Y: 178200.400,
	
	// Width of a tile, in pixels.
	TILE_WIDTH: 200,
	
	// Difference between the most-right pixel of a tile and
	// the most-left pixel Lambert II coordinates (by zoomlevel) 
	originalTileWidths: [ 800, 400, 200, 160, 100, 80 ],
	
	SERVERS: [ 'lesreimsanciens.fr' ],
	
	// Do not refresh display if last refresh occured less than MIN_REFRESH_INTERVAL ms ago. 
	MIN_REFRESH_INTERVAL: 40,
	
	// Mouse move distance before dragging is activated. It seems that anything other than
	// 0 produces a "lag" effect. We should do the difference between current position
	// and initial drag point, not the last one.
	DRAG_THRESHOLD: 0
};

Constants.originalTileWidths[-1] = 2600;

if(Prototype.Browser.IE) {
	if(Constants.SERVERS.length > 2) {
		// IE doesn't handle more than two concurrent servers
		Constants.SERVERS.length = 2;
	}
} else if(Constants.SERVERS.length > 4) {
	Constants.SERVERS.length = 4;
}

Constants.HALF_TILE_WIDTH = Constants.TILE_WIDTH / 2;

Constants.BASE_PATH = window.location.pathname.replace(/\/[^/]+$/, '');
if(Constants.BASE_PATH.length == 0) {
	Constants.BASE_PATH = '/';
} else {
	
	if(Constants.BASE_PATH.charAt(0) != '/') {
		Constants.BASE_PATH = '/' + Constants.BASE_PATH;
	}
	
	if(Constants.BASE_PATH.charAt(Constants.BASE_PATH.length - 1) != '/') {
		Constants.BASE_PATH = Constants.BASE_PATH + '/';
	}
}


/** Max y indices of each zoomlevel (useful since we've to revert
 * the Y axis */
Constants.maxYs = [];
Constants.originalTileWidths.each(function(size, index) {
	Constants.maxYs[index] = Math.ceil((Constants.MAX_BASE_Y - Constants.MIN_BASE_Y) / size);
});
Constants.maxXs = [];
Constants.originalTileWidths.each(function(size, index) {
	Constants.maxXs[index] = Math.ceil((Constants.MAX_BASE_X - Constants.MIN_BASE_X) / size);
});

CoordinatesTranslator = {};

/**
 * Translates a lambert position into a row/column couple.
 * @param coordinates Object(x, y) coordinates of the point to translate.
 * @param zoomLevel Current zoomlevel (no validity check).
 * @return Object(x,y,offsetX,offsetY) column and row indices, offset (in pixel)
 * of the lambert coordinates into the tile
 */
CoordinatesTranslator.translateLambert = function(coordinates, zoomLevel) {
	var coords = {
		x: Math.floor((coordinates.x - Constants.MIN_BASE_X ) / Constants.originalTileWidths[zoomLevel]),
		y: Math.floor((Constants.MAX_BASE_Y - coordinates.y ) / Constants.originalTileWidths[zoomLevel]),
		offsetX: 
			Math.round(Constants.TILE_WIDTH * ((coordinates.x - Constants.MIN_BASE_X) % Constants.originalTileWidths[zoomLevel]) / Constants.originalTileWidths[zoomLevel]),
		offsetY: 
			Math.round(Constants.TILE_WIDTH * ((Constants.MAX_BASE_Y - coordinates.y ) % Constants.originalTileWidths[zoomLevel]) / Constants.originalTileWidths[zoomLevel])
	};
	return coords;
}

/**
 * Returns a tile top left lambert coordinates from a row/column couple.
 * @param coordinates Object(x,y) Column and row indice
 * @param zoomLevel Current zoomLevel (no validity check performed).
 * @return Object(x,y) Lambert coordinates of the top left corner of
 * the tile.
 */
CoordinatesTranslator.tileTopLeft = function(coordinates, zoomLevel) {
	var coords = {
		x: Constants.MIN_BASE_X + coordinates.x * Constants.originalTileWidths[zoomLevel],
		y: Constants.MAX_BASE_Y - coordinates.y * Constants.originalTileWidths[zoomLevel]
	};
	return coords;
}

/**
 * Translates a pixel distance into lambert "distance"
 * @param count integer Number of pixel
 * @param zoomLevel integer Current zoomLevel
 * @return the equivalent lambert difference.
 */
CoordinatesTranslator.pixelsToLambert = function(count, zoomLevel) {
	return (count * Constants.originalTileWidths[zoomLevel] / Constants.TILE_WIDTH);
}

/**
 * Translate a distances in lambert into a pixel count.
 * @param count float Lambert distance.
 * @param zoomLevel integer current zoomLevel.
 * @return float pixel count.
 */
CoordinatesTranslator.lambertToPixels = function(count, zoomLevel) {
	return (count * Constants.TILE_WIDTH) / Constants.originalTileWidths[zoomLevel];
}
	
MapViewer = Class.create({
	/**
	 * Constructor.
	 * @param container HTMLElement Element used to display the map.
	 * @param options Hash Various options:
	 * <ul>
	 * 	<li>coordinates: starting coordinates (lambert) ; </li>
	 *	<li>zoomLevel: starting zoomLevel ;</li>
	 *	<li>layers: Array of hash(name,opacity) indicating which layers are
	 * 	displayed and with which opacity. Order matters!</li>
	 * </ul>
	 */
	initialize: function(container, options) {
		this._zoomListeners = [];
		
		// Default options
		this.state = {
			coordinates: {
				x: (Constants.MIN_BASE_X + Constants.MAX_BASE_X) / 2,
				y: (Constants.MIN_BASE_Y + Constants.MAX_BASE_Y) / 2
			},
			zoomLevel: -1,
			layers: [
				{
					name: 'cada',
					opacity: 100
				}
			],
			polygons: [
			]
		};
		
		
		this._onDragStart = options.dragstart || Prototype.emptyFunction;
		this._onDragStop = options.dragstop || Prototype.emptyFunction;
		delete options.dragstart;
		delete options.dragstop;
		
		Object.extend(this.state, options || { });
		
		this.state.layers.each(function(l) {
			l.opacity = l.opacity / 100.0;
		});
		
		container = $(container);
		container.makePositioned();
		
		var containerId = container.identify();
		
		this.container = new Element('div');
		this.container.setStyle({
			overflow: 'hidden',
			position: 'absolute',
			left: '0px',
			top: '0px',
			width: '100%',
			height: '100%'
		})
		
		container.appendChild(this.container);
		
		this._containerId = containerId;
		this._serverIndex = 0;
		
		this._polygons = [];
		this.state.polygons.each(function(p) {
			this.addPolygon(p.id, p.coordinates, p.color, p.filled, p.onclick, p.ondblclick, p.showonover, p.onover, p.onout, p.zindex);
		}, this);
		
		this.refresh();
		
		Event.observe(this.container, "mousedown", this._startDrag.bindAsEventListener(this));
		Event.observe(this.container, "mousemove", this._drag.bindAsEventListener(this));
		Event.observe(document.body, "mouseup", this._stopDrag.bindAsEventListener(this));
		Event.observe(this.container, "mousewheel", this._mouseZoom.bindAsEventListener(this));
		Event.observe(this.container, "DOMMouseScroll", this._mouseZoom.bindAsEventListener(this));
	},
	
	/**
	 * Moves the viewpoint's center to the given coordinates.
	 * Does not refresh the map.
	 * @param coordinates Lambert coordinates of the
	 * new center.
	 */
	moveTo: function(coordinates) {
		this.state.coordinates = coordinates;
	},
	
	/**
	 * Changes the zoomLevel (if a bigger level exists)
	 * and refresh the display
	 */
	zoomIn: function() {
		if(this.setZoomLevel(this.state.zoomLevel + 1)) {
			this._dispatchZoomEvent({
				direction: 'in',
				currentLevel: this.state.zoomLevel
			});
			this.refresh();
		}
	},
	
	/**
	 * Changes the zoomLevel (if a lower level exists)
	 * and refresh the display
	 */
	zoomOut: function() {
		if(this.setZoomLevel(this.state.zoomLevel - 1)) {
			this._dispatchZoomEvent({
				direction: 'out',
				currentLevel: this.state.zoomLevel
			});
			this.refresh();
		}
	},
	
	/**
	 * Set the zoomlevel to the given value.
	 * @return boolean true if the zoomlevel can be set (i.e. exists),
	 * false otherwise.
	 */
	setZoomLevel: function(zl) {
		if(zl >= -1 && zl < Constants.originalTileWidths.length) {
			this.state.zoomLevel = zl;
			return true;
		}
		return false;
	},
	
	/**
	 * Dispatches a zoom event to all registered
	 * zoom listeners.
	 */
	_dispatchZoomEvent: function(event) {
		this._zoomListeners.each(function(l) {
			l(event);
		});
	},
	
	/**
	 * Returns coordinates relative to the container (x,y) from
	 * a mouse event. No check is made to know if the event occurs
	 * inside the container or not.
	 * @param mouseEvent An event (or, at least an object with pageX and pageY
	 * properties. 
	 */
	_getMouseOffset: function(mouseEvent) {
		var containerCoords = this.container.cumulativeOffset();
		return { x: mouseEvent.pageX - containerCoords.left, y: mouseEvent.pageY - containerCoords.top};
	},
	
	/**
	 * Translate a position inside the container into Lambert II coordinates;
	 * @params coords X and Y offsets inside the container (in pixels).
	 */
	getLambertCoordinates: function(pixelCoords) {
		var containerCoords = this.getContainerTopLeftCoords();
		return {
			x: containerCoords.x + CoordinatesTranslator.pixelsToLambert(pixelCoords.x, this.state.zoomLevel),
			y: containerCoords.y - CoordinatesTranslator.pixelsToLambert(pixelCoords.y, this.state.zoomLevel)
		};
	},
	
	/**
	 * Computes the container's top left corner lambert coordinates.
	 */
	getContainerTopLeftCoords: function() {
		var coords = this.state.coordinates;
		// Converting height and width into lambert
		var YLambertOffset = (this.container.getHeight() / 2) * Constants.originalTileWidths[this.state.zoomLevel] / Constants.TILE_WIDTH;
		var XLambertOffset = (this.container.getWidth() / 2) * Constants.originalTileWidths[this.state.zoomLevel] / Constants.TILE_WIDTH;
		return {
			x: coords.x - XLambertOffset,
			y: coords.y + YLambertOffset // Warning, Y axis is inverted
		};
	},
	
	_getCacheId: function(str) {
		return this._containerId + '-' + str.replace(MapViewer.NON_ALPHA_NUM_RE, '_');
	},
	
	/**
	 * Return a handle on the image object whose src is
	 * src. If the image isn't already loaded, it is fetched from
	 * server, otherwise, the already loaded version is used.
	 * @param src String URL of the image.
	 * @return Image a handle on the image. Its position is absolute,
	 * it has neither border nor padding nor margin.
	 */
	_getImage: function(src) {
		var cacheId = this._getCacheId(src);
		var cached = $(cacheId);
		if(cached) return cached;
		
		var img = new Element('img');

		img.writeAttribute({
			src: 'http://' + Constants.SERVERS[this._serverIndex] + Constants.BASE_PATH + src,
			id: cacheId,
			galleryImg: 'no',
			unselectable: 'on'
			
		});
		img.setStyle({
			'position': 'absolute',
			border: 0,
			margin: 0,
			padding: 0
		});
		this._serverIndex = (this._serverIndex + 1) %  Constants.SERVERS.length;
		this.container.appendChild(img);
		return img;
	},
	
	/**
	 * Refreshes the display.
	 */
	refresh: function() {
		// Enforces a maximum refresh rate to avoid useless computations.
		if(Object.isUndefined(this._lastRefresh) == false && (new Date().getTime() - this._lastRefresh) < Constants.MIN_REFRESH_INTERVAL) {
			return;
		}
		
		this._refreshing = true;
		
		var coordinates = this.state.coordinates;
		// Get the center coordinates
		var center = CoordinatesTranslator.translateLambert(coordinates, this.state.zoomLevel);
		var containerHalfHeight = this.container.getHeight() / 2;
		var containerHalfWidth = this.container.getWidth() / 2;
		
		var startX = center.x - Math.ceil(containerHalfWidth / (Constants.TILE_WIDTH)) - 1;
		var startY = center.y - Math.ceil(containerHalfHeight / (Constants.TILE_WIDTH)) - 1;
		var endX = center.x + Math.ceil(containerHalfWidth / (Constants.TILE_WIDTH)) + 1;
		var endY = center.y + Math.ceil(containerHalfHeight / (Constants.TILE_WIDTH)) + 1;
		var maxX = Constants.maxXs[this.state.zoomLevel];
		var maxY = Constants.maxYs[this.state.zoomLevel];
		
		var tl = CoordinatesTranslator.tileTopLeft({x: endX, y: endY}, this.state.zoomLevel);
		var containertl = this.getContainerTopLeftCoords();
		
		var maxLeft = CoordinatesTranslator.lambertToPixels(tl.x - containertl.x, this.state.zoomLevel);
		var maxTop = CoordinatesTranslator.lambertToPixels(containertl.y - tl.y , this.state.zoomLevel);
		
		var top = maxTop;
		var eligibleLayers = [];
		this.state.layers.each(function(l) {
			if(l.opacity >= 0.01) eligibleLayers.unshift(l);
		});
		var re = /z_(\d+)_x_(\d+)_y_(\d+)$/;
		
		this.container.select('img.displayed').each(function(t) {
				t.hide();
				t.removeClassName('displayed');
		});

		for(var j = endY ; j >= startY ; --j) {
			var left = maxLeft;
			for(var i = endX ; i >= startX ; --i) {
				var out = i < 0 || j < 0 || j > maxY || i > maxX;
				eligibleLayers.each(function(l, index) {
					var src;
					if(out) {
						src = "tiles/null/blank.gif";
					} else {
						src = "tiles/tile.php?p=" + l.name + "&z=" + this.state.zoomLevel + "&x=" + i + "&y=" + j;
					}
					
					var img = this._getImage(src);
					
					img.addClassName("tile-layer-" + l.name);
					img.addClassName('displayed');
					
					img.setStyle({
						display: 'block',
						'left': left + 'px',
						'top': top + 'px',
						zIndex: eligibleLayers.length - index,
						opacity: l.opacity
					});
				}, this);
				left -= Constants.TILE_WIDTH;
			}
			top -= Constants.TILE_WIDTH;
		}
		
		var containerRec = this._getContainerRec();
		
		this._polygons.each(function(p) {
			if(this.isVisible(p.rect, containerRec)) {
				this._drawPolygon(p, containerRec);
			}
		}, this);
		
		this._lastRefresh = new Date().getTime();
		this._refreshing = false;
	},
	
	/**
	 * Starts a drag action.
	 * @param e MouseEvent
	 */
	_startDrag: function(e) {
		// Avoid image DnD in FF3 and image selection in
		// other browsers
		e.stop();
		this.dragInProgress = true;
		this._onDragStart(e);
	},
	
	/**
	 * Sets the opacity of a given layer.
	 * @param layer String name of the layer
	 * @param opacity integer Opacity % (0-100)
	 */
	setOpacity: function(layer, opacity) {
		opacity = opacity / 100.0;
		this.state.layers.each(function(l) {
			if(l.name == layer) {
				l.opacity = opacity;
				throw $break;
			}
		}, this);
		
		this.container.select('img.tile-layer-' + layer).each(function(t) {
			t.setOpacity(opacity);
		});
	},
	
	/**
	 * Adds a layer on the top of the stack. If a layer
	 * of the same name already exists, its opacity is updated,
	 * no other action is taken.
	 * @param layer String the name of the layer (name of the
	 * directory containing tiles).
	 * @param opacity integer Opacity % (0-100).
	 */
	addLayer: function(layer, opacity) {
		var found = false;
		this.state.layers.each(function(l) {
			if(l.name == layer) {
				l.opacity = opacity;
				found = true;
				throw $break;
			}
		});
		
		if(found) {
			this.container.select('img.tile-layer-' + layer).each(function(t) {
				t.setOpacity(opacity / 100);
			});
			return;
		}
		
		this.state.layers.push({
			'name': layer,
			'opacity': opacity
		});
		this.refresh();
	},
	
	/**
	 * Add a listener on the zoom event.
	 * Listener will be called on each zoom (in or
	 * out) with one parameter: the zoomEvent(direction, currentLevel).
	 * @param listener Function Function to call when
	 * user zoom in (or out).
	 */
	addZoomListener: function(listener) {
		this._zoomListeners.push(listener);
	},
	
	/**
	 * Ends a drag action
	 * @param e MouseEvent
	 */
	_stopDrag: function(e) {
		if(!this.dragInProgress) return;
		this.dragInProgress = false;
		this.lastMousePosition = null;
		this._onDragStop(e);
	},
	
	/**
	 * Perform a drag action (display refresh)
	 */
	_drag: function(e) {
		var el;
		var mouseCoords = this._getMouseOffset(e);
		
		if(this.dragInProgress == true) {
			if(this.lastMousePosition != null) {
				var pxX = this.lastMousePosition.x - mouseCoords.x;
				var pxY = this.lastMousePosition.y - mouseCoords.y;
				if(Math.abs(pxX) >= Constants.DRAG_THRESHOLD || Math.abs(pxY) >= Constants.DRAG_THRESHOLD) {
					// Ok, let's drag
					var offsetX = CoordinatesTranslator.pixelsToLambert(pxX, this.state.zoomLevel);
					var offsetY = CoordinatesTranslator.pixelsToLambert(pxY, this.state.zoomLevel);
					this.moveTo({x: this.state.coordinates.x + offsetX, y: this.state.coordinates.y - offsetY});
					this.refresh();
				}
			}
			this.lastMousePosition = mouseCoords;
		}
	},
	
	/**
	 * Handle zoom with mouse
	 */
	_mouseZoom: function(e) {
		e.stop();
		var delta = Event.wheel(e);
		if(delta > 0) {
			var mouseCoords = this._getMouseOffset(e);
			this.moveTo(this.getLambertCoordinates(mouseCoords));
			this.zoomIn();
		} else {
			this.zoomOut();
		}
	},
	
	/**
	 * Adds a polygon to the map.
	 * @param id ID of the polygon
	 * @param coordinates Array of coordinates. A line will
	 * be drawn between each point in the order in wich they
	 * appear.
	 * @param color String indicating the color to use for the
	 * polygon. See poly.php to get an array of every defined
	 * colors.
	 * @param filled boolean indicates wether the polygon should
	 * be filled or not.
	 * @param onclick Function On click event listener (bound
	 * on the onclick event of the image). It receives the click
	 * event as parameter and can process it as it wishes.
	 * @param ondblclick Function On double click event listener.
	 * @param showOnOver boolean indicates if the polygon should be
	 * displayed all the time (false, default) or only on mouse
	 * over (true).
	 * @param onover Function On Mouse Over event listener (bound
	 * to the onmouseover on the polygon). Same behavior than
	 * onclick
	 * @param onout Function On Mouse Out event listener (see onover).
	 * @param zindex integer Indicates the Z-index of the polygon. A polygon
	 * with an high zIndex value is displayed on top of low zIndex value
	 * polygons (default is 0).
	 */
	addPolygon: function(id, coordinates, color, filled, onclick, ondblclick, showOnOver, onover, onout, zindex) {
		var p = {};
		
		if(Object.isUndefined(id)) {
			id = 'poly_' + rand() + new Date().getTime();
		}
		p.id = id;
		p.onclickhandler = onclick;
		p.ondblclickhandler = ondblclick
		p.onoverhandler = onover;
		p.onouthandler = onout;
		p.images = {};	// Do not use associative arrays, use objects!
		
		if(Object.isUndefined(color)) {
			color = 'black';
		}
		
		if(Object.isUndefined(filled)) {
			filled = false;
		}
		
		if(Object.isUndefined(zindex)) {
			zindex = 0;
		}
		
		p.coordinates = coordinates;
		p.color = color;
		p.filled = filled;
		p.wasInPoly = false;
		p.zindex = zindex;
		
		var tlcorner = { x: Number.MAX_VALUE, y: 0 };
		var brcorner = { x: 0, y: Number.MAX_VALUE };
		
		coordinates.each(function(c) {
			if(c.x < tlcorner.x) {
				tlcorner.x = c.x;
			}
			if(c.x > brcorner.x) {
				brcorner.x = c.x;
			}
			
			if(c.y > tlcorner.y) {
				tlcorner.y = c.y;
			}
			if(c.y <  brcorner.y) {
				brcorner.y = c.y;
			}
		});
		
		p.onOver = showOnOver;
		
		p.rect = {
			tl: tlcorner,
			tr: { x: brcorner.x, y: tlcorner.y },
			bl: { x: tlcorner.x, y: brcorner.y },
			br: brcorner
		};
		
		this._polygons.push(p);
		
	},
	
	/**
	 * Remove polygon whose id is id
	 */
	removePolygon: function(id) {
		var p;
		for(var i = 0 ; i < this._polygons.length ; ++i) {
			if(this._polygons[i].id == id) {
				// Clears the cache
				$H(this._polygons[i].images).each(function(pair) {
					if(!pair.value || !pair.value.parentNode) {
						return;
					}
					pair.value.remove();
				});
				this._polygons.splice(i, 1);
				break;
			}
		}
	},
	
	setPolygonColor: function(id, color) {
		for(var i = 0 ; i < this._polygons.length ; ++i) {
			if(this._polygons[i].id == id) {
				var p = this._polygons[i];
				// Clears the cache
				$H(p.images).each(function(pair) {
					if(!pair.value || !pair.value.parentNode) {
						return;
					}
					pair.value.remove();
				});
				p.images = {};
				p.color = color;
				this._drawPolygon(p);
				break;
			}
		}
	},
	
	/**
	 * Indicates wether a rectangle defined by its Lambert coordinates
	 * is visible or not.
	 * @param rec Object containing attributes tl, tr, bl, br (top left, top right,
	 * bottom left, bottom right). Each attribute is a coordinate (x,y) describing
	 * the rectangle.
	 */
	isVisible: function(rec, containerRec) {
		
		// It is easier (for my mind safety) to determine
		// it they do not intersect than to determine if
		// they do...
		var value =
		!(
			( rec.br.y > containerRec.tl.y ) ||
			( rec.tl.y < containerRec.br.y) ||
			( rec.br.x < containerRec.tl.x ) ||
			( rec.tl.x > containerRec.br.x)
		);
		
		// Without intermediate variable, we alway return undefined (?!) 
		return value;
	},
	
	/**
	 * Returns an Object(tl, br) containing Lambert
	 * coordinates of the top-left and bottom-right
	 * corners of the container.
	 */
	_getContainerRec: function() {
		var containerRec = {};
		containerRec.tl = this.getContainerTopLeftCoords();
		var containerWidth = CoordinatesTranslator.pixelsToLambert(this.container.getWidth(), this.state.zoomLevel);
		var containerHeight = CoordinatesTranslator.pixelsToLambert(this.container.getHeight(), this.state.zoomLevel);
		containerRec.br = {
			x: containerRec.tl.x + containerWidth,
			y: containerRec.tl.y - containerHeight
		};
		
		return containerRec;
	},
	
	_drawPolygon: function(p) {
		var tlcorner = p.rect.tl;
		
		var img;
		
		if(Object.isUndefined(p.images[this.state.zoomLevel])) {
			
		
			// Translate everything into the top left corner coordinates system.
			var localCoordinates = [];
		
			p.coordinates.each(function(c) {
			localCoordinates.push(CoordinatesTranslator.lambertToPixels(c.x - tlcorner.x, this.state.zoomLevel) + ',' +
				CoordinatesTranslator.lambertToPixels(tlcorner.y - c.y, this.state.zoomLevel));
			}, this);
		
			// z is useless for php script, it just allow us to cache the image
			var src = '/tiles/poly.php?z=' + this.state.zoomLevel + '&points[]=' + localCoordinates.join('&points[]=');
			if(p.filled) {
				src += '&filled=true';
			}
		
			src += '&color=' + p.color;
			
			src += '&poly_id=' + p.id;	// Évite des problèmes lorsque plusieurs polygones ont exactement les mêmes coordonnées.
		
			img = this._getImage(src);
			p.images[this.state.zoomLevel] = img;
		} else {
			img = p.images[this.state.zoomLevel];
		}
		
		var contl = this.getContainerTopLeftCoords();
		img.setStyle({
			left: CoordinatesTranslator.lambertToPixels(tlcorner.x - contl.x, this.state.zoomLevel) + 'px',
			top:  CoordinatesTranslator.lambertToPixels(contl.y - tlcorner.y, this.state.zoomLevel) + 'px',
			height: 'auto',
			width: 'auto',
			'zIndex': 1000 + p.zindex,
			display: 'block'
		});
		
		if(p.onOver) {
			// Do not use visibility="hidden" since we want to
			// receive events
			img.setStyle({'opacity':'0'});
		}

		if(p.onOver || p.onoverhandler || p.onouthandler) {
			if(Event.hasListeners(img, 'mousemove') == false) {
				Event.observe(img, 'mouseout', function(e) { this._polyOnMouseMove(e, p); }.bind(this));
				Event.observe(img, 'mousemove', function(e) { this._polyOnMouseMove(e, p); }.bind(this));
			}
		}
		
		if(p.onclickhandler && Event.hasListeners(img, 'click') == false) {
			Event.observe(img, 'click', function(e) { this._polyOnClick(e, p); }.bind(this));
		}
		
		if(p.ondblclickhandler && Event.hasListeners(img, 'dblclick') == false) {
			Event.observe(img, 'dblclick', function(e) { this._polyOnDblClick(e, p); }.bind(this));
		}

		img.addClassName('displayed');
	},
	
	/**
	 * Event handler called when mouse moves over an element.
	 * Emulates onover and onout event to take the polygon as
	 * a reference and not the image containing it.
	 */
	_polyOnMouseMove: function(e, p) {
		var mouseCoords = this.getLambertCoordinates(this._getMouseOffset(e));
		if(this._isInPoly(mouseCoords, p)) {
			if(!p.wasInPoly) {
				// Handle event only when we first over the poly
				if(p.onOver) Event.findElement(e, 'img').setStyle({opacity: 100});
				if(p.onoverhandler) p.onoverhandler(e);
				
				// Register the new current polygon and notifies the old one.
				if(this._currentOverPolygon && this._currentOverPolygon != p) {
					if(this._currentOverPolygon.onOver) this._currentOverPolygon.img.setStyle({opacity: 0});
					if(this._currentOverPolygon.onouthandler) this._currentOverPolygon.onouthandler(e);
					this._currentOverPolygon.wasInPoly = false;
				}
				
				
				this._currentOverPolygon = p;
				this._currentOverPolygon.img = Event.findElement(e, 'img');
				p.wasInPoly = true;
			}
		} else if(p.wasInPoly){
			// We were on the poly but this is not the case anymore.
			if(p.onOver) Event.findElement(e, 'img').setStyle({opacity: 0});
			if(p.onouthandler) p.onouthandler(e);
			p.wasInPoly = false;
			this._currentoverPolygon = undefined;
		}
	},
	
	_polyOnClick: function(e, p) {
		var mouseCoords = this.getLambertCoordinates(this._getMouseOffset(e));
		if(this._isInPoly(mouseCoords, p)) {
			p.onclickhandler(e);
		}
	},
	
	_polyOnDblClick: function(e, p) {
		var mouseCoords = this.getLambertCoordinates(this._getMouseOffset(e));
		if(this._isInPoly(mouseCoords, p)) {
			p.ondblclickhandler(e);
		}
	},
	
	/**
	 * Determines if a point is inside a polygon
	 * or not.
	 */
	_isInPoly: function(point, poly) {
		var coords = poly.coordinates;
		var length = coords.length;
		var c = false;
		
		for(i = 0, j = length - 1 ; i < length ; j = i++) {
			if(
				((coords[i].y <= point.y && point.y < coords[j].y) ||
				 (coords[j].y <= point.y && point.y < coords[i].y ) ) &&
				 (point.x < (coords[j].x - coords[i].x) * (point.y - coords[i].y) / (coords[j].y - coords[i].y) + coords[i].x)) {
					c = !c;
				}

		}
		return c;
	}
});

MapViewer.NON_ALPHA_NUM_RE = /[^A-Za-z0-9]/g;
