/*
---
script: MeaShapeHover.js
based on: ShapeHover.js

description: Add rollover effect on an image map

license: MIT-style license.

authors: Yannick Croissant

requires:
 core/1.3: '*'
 more/1.3.0.1: [Assets, Array.Extras]

provides: [ShapeHover]

...
*/



var tmpcanvas = document.createElement('canvas');
var isIE = window.navigator.systemLanguage && (!document.documentMode||document.documentMode<9) ? 1 : 0;
var useVML = isIE && document.namespaces ? 1 : 0;
var useCNV = tmpcanvas.getContext ? 1 : 0;


var MeaShapeHover = new Class({

	Implements: [Options, Events],

	options: {
		/*
		onReady: function(event){},
		onMouseOver: function(event){},
		onMouseOut: function(event){},
		onClick: function(event){},
		*/
		fill: {				// Set it to false to disable filling
			type: 'color',	// Can be color or image
			content: '#000',// Can be a color (in hex) or an absolute image path
			opacity: .5		// Opacity, only used if type is color (min: 0, max: 1)
		},
		stroke: {			// Set it to false to disable stroke
			color: '#F00',	// Color (in hex)
			opacity: 1,		// Opacity (min: 0, max: 1)
			width: 1		// Width in px
		},
		fade: true,			// Fade effect on :hover (canvas only)
		alwaysOn: false		// If true, all areas got the :hover style by default
	},

	/*
	 * Constructor
	 */
	initialize: function(element, options){
		this.element = document.id(element);
		this.map = document.getElement('map[name="' + this.element.get('usemap').split('#')[1] + '"]'); // Get the map related to the image
		this.setOptions(options);

		// Detect technology to use : canvas or vml
		// this.type = document.createElement('canvas') && document.createElement('canvas').getContext ? 'canvas' : (document.namespaces ? 'vml' : false);
		this.type = (useCNV) ? 'canvas' : (useVML) ? 'vml' : false;


		// If the browser is not supported, exit
		if (!this.type || !this.map) return false;

		// Add namespaces for vml
		if(this.type == 'vml') {
			if(document.namespaces['v']==null) {
				sheet = document.createStyleSheet();
				Array.each(["shape","shapetype","group","background","path","formulas","handles","fill","stroke","shadow","textbox","textpath","imagedata","line","polyline","curve","roundrect","oval","rect","arc","image"], function(item, index){
					sheet.addRule("v\\:"+item, "behavior: url(#default#VML); antialias: true;");
				});
				document.namespaces.add("v","urn:schemas-microsoft-com:vml");
			}
		}

		if (this.options.fill.type != 'image') {
			return this.ready();
		}

		// Preload the hover image (clean for canvas, hacky for vml)
		if (this.type == 'canvas') {
			this.fillContent = new Asset.image(this.options.fill.content, {onload: this.ready.bind(this)});
		} else {
			// Create an hidden vml image at the bottom of the page (no onload event, just a fake preload)
			var tmp = new Element('div', {styles:{position:'absolute', left:'-9999em'}});
			tmp.inject(this.element, 'after');
			tmp.innerHTML = '<v:rect><v:fill type="tile" src="' + this.options.fill.content + '" /></v:rect>';
			this.ready();
		}
	},

	/*
	 * The document is ready (technologie detected, namespace added, image preloaded)
	 * We can now manipulate the DOM to add the elements and the events
	 */
	ready: function(){

//			if (this.type == 'vml') {
//				// create 'background' image
//				 var imgBg = this.element.clone(true, false).set('id', this.element.get('id')+'_imgbg');
//			}

			// Create the wrapper and inject it before the image
			this.wrap = new Element('div', {
				id: this.element.get('id')+'_wrapper',
				styles: {
					background: 'url(' + this.element.get('src') + ')',
					height: this.element.getSize().y,
					position: 'relative',
					width: this.element.getSize().x
				}
			}).inject(this.element, 'before');

			// Render the image transparent, and inject it in the wrapper
			this.element.style.opacity = 0;

			if (isIE) {
				this.element.style.filter = 'Alpha(opacity=0)';
			}
			this.element.setStyles({position: 'absolute', left: 0, top: 0, padding: 0, border: 0});

			this.wrap.adopt(this.element);

			// Create the canvas (always called canvas, even if it is vml under IE)
			if (this.type != 'vml') {
				this.canvas = this.createCanvas().set({
					id: this.element.get('id') + '_canvas',
					styles: {
						border: 0,
						left: 0,
						padding: 0,
						position: 'absolute',
						top: 0
					},
					width: this.element.getSize().x,
					height: this.element.getSize().y
				});

			} else {
				// create the 'canvas' for vml
				this.canvas = this.createCanvas().set({
					id: this.element.get('id') + '_canvas',
					styles: {
						border: 0,
						left: 0,
						padding: 0,
						position: 'absolute',
						top: 0,
						zoom: 1,
						display: 'block'
					},
					width: this.element.getSize().x,
					height: this.element.getSize().y
				});
			}


			if(this.options.alwaysOn) {
				// Add the shape over all areas
				this.map.getElements('area[coords]').forEach(function(el) {
					var shape = this.shapeFromArea(el);
					this.addShapeTo(shape[0], shape[1]);
				}.bind(this));
			} else {
				// Add events on each area
				this.map.getElements('area[coords]')
					.addEvent('mouseover', function(e) {
						var shape = this.shapeFromArea(e.target);
						this.addShapeTo(shape[0], shape[1]);

// ThK modif.
						var el = e.target;
						el.addEvent('mouseout', function(e){
							this.clearCanvas();
							this.fireEvent('onMouseOut', e);
						}.bind(this));

						this.fireEvent('onMouseOver', e);
					}.bind(this))
//					.addEvent('mouseout', function(e){
//						this.clearCanvas();
//						this.fireEvent('onMouseOut', e);
//					}.bind(this))
					.addEvent('click', function(e){
						this.fireEvent('onClick', e);
					}.bind(this));
			}


			if (this.type != 'vml') {
				 // Inject the canvas before the image
				this.canvas.inject(this.element, 'before');
			} else {
				// create div 'blind_area' and inject before image
				var divBlind = new Element('div', {
					id: this.element.get('id') + '_blind',
					styles: {
						position: 'absolute',
						width: this.element.getSize().x + 'px',
						height: this.element.getSize().y + 'px',
						padding: '0px',
						margin: '0px',
						background: 'none transparent scroll repeat'
					}
				}).inject(this.element, 'before');

				 // Inject the canvas before the divBlind
				this.canvas.inject(divBlind, 'before');

				// inject imgBg before 'canvas'
				// imgBg.inject(this.canvas, 'before');

			}

			this.fireEvent('onReady');


	},

	/*
	 * Create the canvas (or vml) root element and return it
	 */
	createCanvas: function() {
		if (this.type == 'canvas') {
			var c = new Element('canvas', {
				styles: {
					width: this.element.getSize().x + 'px',
					height: this.element.getSize().y + 'px'
				}
			});
			c.getContext("2d").clearRect(0, 0, c.width, c.height);

			// Set the options for the fade-in effect
			if (this.options.fade) {
				c.set({
					tween: {duration: 'short'}
				});
			}

			return c;
		}


		if (this.type == 'vml') {
			var c = new Element('var', {
				styles: {
					height: this.element.getSize().y + 'px',
					width: this.element.getSize().x + 'px'
				}
			});

			return c;
		}

	},

	/*
	 * Add a shape in the canvas
	 */
	addShapeTo: function(shape, coords) {
		if (this.type == 'canvas') {
			// Init the opacity for the fade effect
			if (this.options.fade) this.canvas.set('opacity', 0);

			var context = this.canvas.getContext('2d');

			// Draw the shape according to his type
			context.beginPath();
			if (shape == 'rect') context.rect(coords[0], coords[1], coords[2] - coords[0], coords[3] - coords[1]);
			else if (shape == 'circ') context.arc(coords[0], coords[1], coords[2], 0, Math.PI * 2, false);
			else if (shape == 'poly') {
				context.moveTo(coords[0], coords[1]);
				for (var i = 2, coordsL = coords.length; i < coordsL; i += 2) {
					context.lineTo(coords[i], coords[i + 1]);
				}
			}
			context.closePath();

			// Fill the shape
			if (this.options.fill) {
				// Fill it with an image
				if (this.options.fill.type == 'image') {
					var pattern = context.createPattern(this.fillContent ,'repeat');
					context.fillStyle = pattern;
				// else fill it with a color
				} else context.fillStyle = 'rgba(' + this.options.fill.content.hexToRgb(true).join(',') + ',' + this.options.fill.opacity + ')';
				context.fill();
			}

			// Add stroke to the shape
			if (this.options.stroke) {
				context.strokeStyle = 'rgba(' + this.options.stroke.color.hexToRgb(true).join(',') + ',' + this.options.stroke.opacity + ')';
				context.lineWidth = this.options.stroke.width;
				context.stroke();
			}

			// Start the fade-in effect
			if (this.options.fade) this.canvas.tween('opacity', 1);

		// Ho shit, we are under IE
		} else {

			// The background position is very weird under vml (look at this http://msdn.microsoft.com/en-us/library/bb229615%28VS.85%29.aspx )
			// So, we have to calculate our background position in this way :
			// Shape width = (X coord of the most-on-the-right point) - (X coord of the most-on-the-left point)
			// Shape height = (Y coord of the most-on-the-top point) - (Y coord of the most-on-the-bottom point)
			// Position X = -1 * (X coord of the most-on-the-left point) / (Shape width)
			// Position Y = -1 * (Y coord of the most-on-the-left point) / (Shape height)
			// That's it ! Thanks Microsoft
			var x = [], y = [];
			coords.filter(function(item, index){
				// ### runden auf Ganzzahl
				item = parseInt(item + 0.5);
				coords[index] = item;
				if (index % 2) y.push(item);		// Put Y coords in an array
				else x.push(item);					// Put X coords in another array
			}.bind(this));
			x = -1 * x.min() / (x.max() - x.min());	// The equation explained before
			y = -1 * y.min() / (y.max() - y.min());	// The same one, for the Y axis

			// Draw the child elements
			if (this.options.fill && this.options.fill.type == 'image') {
				var fill = '<v:fill type="tile" position="' + x + ',' + y + '" src="' + this.options.fill.content + '" />';
			} else {
				var fill = '<v:fill opacity="' + (this.options.fill ? this.options.fill.opacity : 0) + '" />';
			}

			var stroke = this.options.stroke ? 'strokeweight="' + this.options.stroke.width+'px" stroked="t" strokecolor="' + this.options.stroke.color + '"' : 'stroked="f"';
			var opacity = '<v:stroke opacity="' + (this.options.stroke ? this.options.stroke.opacity : 0) + '"/>';

			// Draw the shape according to his type
			if(shape == 'rect') {
				var e = '<v:rect 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;">' + fill + opacity + '</v:rect>';
			} else if(shape == 'circ') {
				var e = '<v:oval 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;">' + fill + opacity + '</v:oval>';
			} else if(shape == 'poly') {
				var e = '<v:shape coordorigin="0,0" filled="t" ' + stroke + ' fillcolor="' + (this.options.fill ? this.options.fill.content : '') + '" coordsize="' + this.canvas.width + ',' + this.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:'+this.canvas.width+'px;height:'+this.canvas.height+'px;">' + fill + opacity + '</v:shape>';
			}


			this.canvas.innerHTML = e;

			// Big hack because IE8 has some fucking weird bugs ( http://www.lrbabe.com/?p=104 )
//			var tmp = document.createElement('div');
//			tmp.style.display = 'none';
//			document.body.appendChild(tmp);
//			tmp.innerHTML = e;
//			e = tmp.childNodes[0];

			// document.id(e).inject(this.canvas);
			//document.id(tmp).destroy();

		}
	},


	/*
	 * Empty the canvas
	 */
	clearCanvas: function() {
		if (this.type == 'canvas') {
			this.canvas.getContext('2d').clearRect(0, 0, this.canvas.width, this.canvas.height);
		} else {
			// this.canvas.empty();
			this.canvas.innerHTML = '';
		}
	},

	/*
	 * Get the shape type (rect, poly or circ) and the coords of the shape
	 * return an array with the shape type and the coords as another array
	 */
	shapeFromArea: function(area) {
		return [
			area.get('shape').toLowerCase().substr(0,4),
			area.get('coords').split(',').map(function(item){
				return parseFloat(item);
			})
		];
	}
});
