

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 
// 		var: Bubble
// 		This Class will generate an image- or HTML-Bubble from the given Data. 
//		Most of its functions will be called <ControlCenter>. 
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




var Bubble = new Class(
{
	Implements: [Options, Events],
	
	options: 
	{
		elm:					null,		// the original html element structure
		data:					null,		// the data object given on instanciation
		w: 						0,			// width of the bubble, will be self-calculated
		h: 						0,			// height of the bubble, will be self-calculated
		detailsLink: 			'#',		// the details hash for this bubble
		title: 					'',			// the title for this bubble
		animationPeriod: 		24,			// Wiederholungsgeschwindigkeit der Animation
		normalHeight: 			260,		// begrenzt Hochformate auf den oberen Bereich, damit die Bridge sichtbar bleibt
		sizeX: 					0,			// (Breite des Bubbles)
		sizeY: 					0,			// (Höhe des Bubbles)
		minSizeX: 				80, 		// Breite in Px, ab dem in Richtung Vergrößerung beschleunigt wird
		minSizeY: 				80, 		// Höhe in Px ab dem in Richtung Vergrößerung beschleunigt wird
		maxSizeX: 				50, 		// Prozent der Ursprungsbreite, ab dem in Richtung Verkleinerung beschleunigt wird
		maxSizeY: 				50, 		// Prozent der Ursprungshöhe, ab dem in Richtung Verkleinerung beschleunigt wird
		resizeFactorX: 			0,			// (aktuelle Geschwindigkeit)
		resizeFactorY: 			0,			// (aktuelle Geschwindigkeit)
		resizeSpeedFactor: 		0.008,		// Beschleunigungsmenge für Resize
		maxResizeSpeed: 		0.8,		// maximale Resize-Geschwindigkeit
		expandSpeed: 			26, 		// Ausklappgeschwindigkeit, sollte durch 2 teilbar sein!
		deflateSpeed: 			14, 		// Einklappgeschwindigkeit, sollte durch 2 teilbar sein!
		posX: 					0,			// (aktuelle Position)
		posY: 					0,			// (aktuelle Position)
		padding: 				50, 		// Abstand zum Rand des Bubbles, ab dem in die Gegenrichtung beschleunigt wird
		repositionFactorX:		0,			// (aktuelle Geschwindigkeit)
		repositionFactorY:		0,			// (aktuelle Geschwindigkeit)
		repositionSpeedFactor:	0.006,		// Beschleunigungsmenge für Bewegung
		maxRepositionSpeed: 	0.6			// maximale Bewegungsgeschwindigkeit
	},
	
	elements: {},
	
	initialize: function(options) 
	{
		this.setOptions(options);
		var wrapper = this.options.elm;
		this.elements.contentBox = wrapper.getChildren()[0];
				
		// delete PHP detailsLink
		if( wrapper.getChildren()[0].getChildren()[0].get('tag')=='a')
		{
			wrapper.getChildren()[0].getChildren()[0].getChildren().each( function(num)
			{
				num.inject( wrapper.getChildren()[0]);
			},this);
			wrapper.getChildren()[0].getChildren()[0].destroy();
		}
				
		// manage JS detailsLink, title etc.
		var bubbleData = this.options.data;
		this.options.detailsLink = '#'+bubbleData.dir+'/';
		this.options.title = bubbleData.title;
		this.options.w = bubbleData.w;
		this.options.h = bubbleData.h;
		if( this.options.data.numSlides )
		{
			this.elements.contentBox.setStyle( 'cursor', 'pointer' );
			this.elements.contentBox.addEvent( 'mouseover', function() { $('topInfo').set('html',this.options.title).fade('in'); wrapper.addClass('BubbleOver'); this.elements.contentBox.fade('show'); }.bind(this));
			this.elements.contentBox.addEvent( 'mouseout', function() { $('topInfo').fade(0.0001); wrapper.removeClass('BubbleOver'); }.bind(this));
			this.elements.contentBox.addEvent( 'mousedown', function() { wrapper.removeClass('BubbleOver'); this.elements.contentBox.fade(0.2); }.bind(this));
			this.elements.contentBox.addEvent( 'mouseup', function() { this.elements.contentBox.fade('in'); }.bind(this));
			this.elements.contentBox.addEvent( 'click', function() { window.location.href = this.options.detailsLink; }.bind(this));
		}
		
		
		
		


		
		// var: extensions ///////////////////////////////////////////////////////////////////////////////////////////////////
		// This in fact is the *real* class. All important functionality will be implemented into the HTML wrapper DIV for easy access by $(ID).
		
		var extensions = 
		{
			options: this.options,
			elements: this.elements,
			
			// vars: Status
			// animationPeriod		- the animation period for acces by <start>, <freeze>, and <resume>.
			// initialized			- status var
			animationPeriod: $empty.delay(100),
			initialized: false,
	
			// func: init
			// Calculates the start position and speed of this Bubble and applies EventHandlers.
			init: function()
			{
				var o = this.options;
				
				// normalHeight var will keep this Bubble away from the bridge, even if its content is portrait format. 
				if( o.h < o.normalHeight) o.normalHeight = o.h;
				
				// Generating initial random values for position and Speed.
				var plusMinus = [1,-1];
				o.repositionFactorX = $random(o.maxRepositionSpeed*0.3*100, o.maxRepositionSpeed*100)/100 * plusMinus.getRandom();
				o.repositionFactorY = $random(o.maxRepositionSpeed*0.3*100, o.maxRepositionSpeed*100)/100 * plusMinus.getRandom();
				o.resizeFactorX = $random(o.maxResizeSpeed*0.3*100, o.maxResizeSpeed*100)/ 100 * plusMinus.getRandom();
				o.resizeFactorY = $random(o.maxResizeSpeed*0.3*100, o.maxResizeSpeed*100)/ 100 * plusMinus.getRandom();
				o.sizeX = $random( o.minSizeX, o.w * o.maxSizeX / 100 );
				o.sizeY = $random( o.minSizeY, o.normalHeight * o.maxSizeY / 100 );
				o.posX = $random( 0, o.w - o.sizeX );
				o.posY = $random( 0, o.normalHeight - o.sizeY );
				this.applyPosition();
				
				// Setting Events for the enlarging and reducing in <animate>.
				var mouseout = $empty.delay(1,this);
				this.addEvent( 'mouseover', function() 
				{
					$clear( mouseout ); 
					o.bubbleMouseover = true; 
					o.repositionFactorX = $random(o.maxRepositionSpeed*0.3*100, o.maxRepositionSpeed*100)/100 * plusMinus.getRandom();
					o.repositionFactorY = $random(o.maxRepositionSpeed*0.3*100, o.maxRepositionSpeed*100)/100 * plusMinus.getRandom();
					o.resizeFactorX = $random(o.maxResizeSpeed*0.3*100, o.maxResizeSpeed*100)/ 100 * plusMinus.getRandom();
					o.resizeFactorY = $random(o.maxResizeSpeed*0.3*100, o.maxResizeSpeed*100)/ 100 * plusMinus.getRandom();
				}.bind(this));
				this.addEvent( 'mouseout', function() 
				{
					var mouseoutFunc = function() { o.bubbleMouseover = false; }; 
					mouseout = mouseoutFunc.delay(10,this); 
				}.bind(this));
				this.initialized = true;
			},
			
			// func: start
			// Starts the movement of this Bubble. Called by <ControlCenter>. Calls <init>, if not initialized already.
			start: function(delay)
			{
				if( !this.initialized) this.init();
				$clear( this.animationPeriod );
				this.animationPeriod = this.animate.periodical( this.options.animationPeriod, this );
				this.setStyle('display','block');
				(function(){ this.fade('in'); }).delay(delay, this);
			},
			
			// func: freeze
			// Stops the movement of this Bubble. Called by <ControlCenter> to save CPU-Power in Details-View.
			freeze: function()
			{
				$clear( this.animationPeriod );
			},
						
			// func: appylyPosition
			// Realizes the movement of this Bubble. Is called by <start> or <animate>.
			applyPosition: function()
			{
				this.elements.contentBox.setStyles(
				{
					marginLeft:Math.round(-this.options.posX)+'px', 
					marginTop:Math.round(-this.options.posY)+'px' 
				});
				this.setStyles(
				{
					width:Math.round(this.options.sizeX)+'px', 
					height:Math.round(this.options.sizeY)+'px', 
					marginLeft:Math.round(this.options.posX)+'px', 
					marginTop:Math.round(this.options.posY)+'px' 
				});
			},
						
			// func: animate
			// Calculates the movement of this Bubble. Is called periodically by <start>. 
			animate: function()
			{
				var o = this.options;
				
				// On MouseOver the Bubble will be enlarged. 
				// The previous position will be kept for later return. Animation Speeds are randomly generated by the EventHandler in <start>. 
				if( o.bubbleMouseover )
				{
					if( !o.lastBubbleStatus) { o.lastBubbleStatus = { sizeX:o.sizeX, sizeY:o.sizeY }; }
					o.sizeX = Number(o.sizeX) + o.expandSpeed; 
					if( o.sizeX > o.w ) o.sizeX = o.w;
					o.sizeY = Number(o.sizeY) + o.expandSpeed; 
					if( o.sizeY > o.h ) o.sizeY = o.h;
					o.posX = Number(o.posX) - o.expandSpeed /2; 
					if( o.posX <0 ) o.posX = 0;
					else if( o.posX + o.sizeX > o.w ) o.posX = o.w - o.sizeX;
					o.posY = Number(o.posY) - o.expandSpeed /2; 
					if( o.posY <0 ) o.posY = 0;
					else if( o.posY + o.sizeY > o.h ) o.posY = o.h - o.sizeY;
				}
				
				// After Mouseout the size will be reduced back. 
				else if( o.lastBubbleStatus )
				{
					var check = 0;
					o.sizeX = Number(o.sizeX) - o.deflateSpeed; 
					if( o.sizeX < o.lastBubbleStatus.sizeX ) { check += 1; o.sizeX = o.lastBubbleStatus.sizeX; }
					else o.posX = Number(o.posX) + o.deflateSpeed /2; 
					o.sizeY = Number(o.sizeY) - o.deflateSpeed; 
					if( o.sizeY < o.lastBubbleStatus.sizeY ) { check += 1; o.sizeY = o.lastBubbleStatus.sizeY; }
					else o.posY = Number(o.posY) + o.deflateSpeed /2; 
					if( check >1) o.lastBubbleStatus = null;
				}
				
				// And, of course, the normal, free movement: 
				else
				{
					// - Changing Height
					if( o.sizeX < o.minSizeX && o.resizeFactorX < o.maxResizeSpeed ) o.resizeFactorX += o.resizeSpeedFactor;
					else if( o.sizeX > o.w * o.maxSizeX / 100 && o.resizeFactorX > -o.maxResizeSpeed ) o.resizeFactorX -= o.resizeSpeedFactor;
					o.sizeX += o.resizeFactorX; 
					o.posX -= o.resizeFactorX / 2;
					if( o.sizeX > o.w * 0.7 ) { o.sizeX = o.w * 0.7; o.resizeFactorX = 0; }
					if( o.sizeX < 30 ) { o.sizeX = 30; o.resizeFactorX = 0; }
					
					// - Changing Width
					if( o.sizeY < o.minSizeY && o.resizeFactorY < o.maxResizeSpeed ) o.resizeFactorY += o.resizeSpeedFactor;
					else if( o.sizeY > o.normalHeight * o.maxSizeY / 100 && o.resizeFactorY > -o.maxResizeSpeed ) o.resizeFactorY -= o.resizeSpeedFactor;
					o.sizeY += o.resizeFactorY;
					o.posY -= o.resizeFactorY / 2;
					if( o.sizeY > o.normalHeight * 0.7 ) { o.sizeY = o.normalHeight * 0.7; o.resizeFactorY = 0; }
					if( o.sizeY < 30 ) { o.sizeY = 30; o.resizeFactorY = 0; }
					
					// - Changing X-Position
					if( o.posX < o.padding && o.repositionFactorX < o.maxRepositionSpeed ) o.repositionFactorX += o.repositionSpeedFactor;
					else if( o.posX + o.sizeX > o.w - o.padding && o.repositionFactorX > -o.maxRepositionSpeed ) o.repositionFactorX -= o.repositionSpeedFactor;
					o.posX += o.repositionFactorX;
					if( o.posX <0 ) { o.posX = 0; o.repositionFactorX = 0; }
					if( o.posX + o.sizeX > o.w ) { o.posX = o.w - o.sizeX; o.repositionFactorX = 0; }
					
					// - Changing Y-Position
					if( o.posY < o.padding && o.repositionFactorY < o.maxRepositionSpeed ) o.repositionFactorY += o.repositionSpeedFactor;
					else if( o.posY + o.sizeY > o.normalHeight - o.padding && o.repositionFactorY > -o.maxRepositionSpeed ) o.repositionFactorY -= o.repositionSpeedFactor;
					o.posY += o.repositionFactorY;
					if( o.posY <0 ) { o.posY = 0; o.repositionFactorY = 0; }
					if( o.posY + o.sizeY > o.h ) { o.posY = o.h - o.sizeY; o.repositionFactorY = 0; }
				}
				
				this.applyPosition();
			},
			
			// func: validateMail
			// validates the user input to the mail form
			validateMail: function()
			{
				// console.log( 'Bubble.validateMail()' );
				if( this.elements.inputName.value.trim()=='Name' || !this.elements.inputName.value.trim())
				{
					alert( 'Bitte geben Sie Ihren Namen an!' );
					return false;
				}
				else if( this.elements.inputMail.value.trim()=='eMail' || !this.elements.inputMail.value.trim())
				{
					alert( 'Bitte geben Sie Ihre eMail-Adresse an!' );
					return false;
				}
				else if( !this.elements.inputMail.value.test( /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/ ))
				{
					alert( 'Bitte geben Sie Ihre eMail-Adresse korrekt an!' );
					return false;
				}
				else if( this.elements.inputMsg.value.trim()=='Nachricht' || !this.elements.inputMsg.value.trim())
				{
					alert( 'Bitte geben Sie vor dem Absenden eine Nachricht ein!' );
					return false;
				}
				else
				{
					return true;
				}
			},
			
			// func: sendMail
			// send the mail after successful validation
			sendMail: function()
			{
				if( this.validateMail() )
				{
					// console.log( 'Bubble.sendMail()' );
					this.elements.sending.setStyle('display','block').fade(0.85);
					var mailRequest = new Request(
					{
						url: 'http://KorbinianMoser.com/mailer.php',
						data:
						{
							'from_name':this.elements.inputName.get('value').trim(), 
							'from_email':this.elements.inputMail.get('value').trim(), 
							'BODY':this.elements.inputMsg.get('value')
						},
						onFailure: function(error)
						{
							// alert('anmeldeRequest error: '+error); 
							alert( 'Failure: Beim Senden der Mail ist ein Fehler aufgetreten! Bitte versuchen Sie es erneut.' );
							this.elements.sending.fade('hide').setStyle('display','none');
						},
						onException: function(headerName, value) 
						{
							// alert('anmeldeRequest exception: '+headerName+' '+value); 
							alert( 'Exception: Beim Senden der Mail ist ein Fehler aufgetreten! Bitte versuchen Sie es erneut.' );
							this.elements.sending.fade('hide').setStyle('display','none');
						},
						onSuccess: function(responseText) 
						{
							// console.log(responseText);
							var result = JSON.decode(responseText);
							if( result.sent=='OK' )
							{
								// console.log( 'hat geklappt...');
								( function() {
									this.elements.form.setStyle('display','none').fade('hide');
									this.elements.thankyou.setStyle('display','block').fade('in');
									this.elements.sending.fade('out');
								}).delay(2500, this);
							}
							else if( result.error ) 
							{
								alert( result.error );
								this.elements.sending.fade('hide').setStyle('display','none');
							}
							else 
							{
								alert( responseText );
								this.elements.sending.fade('hide').setStyle('display','none');
							}
						}.bind(this)
					}).send();
				}
			}
		};
		$extend( wrapper, extensions );
		
		



		// some extras for mailer
		if( wrapper.getElements('.mailer')[0])
		{
			wrapper.getElements('input textarea').each( function(num)
			{
				if( num.get('name')=='from_name' || num.get('name')=='from_email' ||  num.get('name')=='BODY')
				{
					num.addEvent( 'focus', function()
					{
						if( num.value=='Name' || num.value=='eMail' || num.value=='Nachricht' )
						{
							num.setStyle('color','#300');
							num.set('value', '');
						}
					}.bind(this));
				}
			}, this);
			wrapper.elements.form = wrapper.getElements('form')[0];
			wrapper.elements.thankyou = new Element( 'div', { 'class':'thankyou', html:'<h3>Vielen Dank</h3><p>Ihre Nachricht wurde erfolgreich versendet.</p>' }).fade('hide').inject( wrapper.getElements('.mailer')[0] );
			wrapper.elements.sending = new Element( 'div', { 'class':'sending' }).fade('hide').inject( wrapper.getElements('.mailer')[0] );
			wrapper.elements.inputName = wrapper.getElements('input[name=from_name]')[0];
			wrapper.elements.inputMail = wrapper.getElements('input[name=from_email]')[0];
			wrapper.elements.inputMsg = wrapper.getElements('textarea[name=BODY]')[0];
			wrapper.getElements('input[type=submit]')[0].set('type','button').addEvent( 'click', wrapper.sendMail.bind(wrapper));
		}
		
		
		

		// return: wrapper ///////////////////////////////////////////////////////////////////////////////////////////////////
		// The whole HTML structure will be returned, with all the important attributes and functions implemented into it. ///
		// So the class can easyly be accessed by $(ID) and the rest of it won't be needed from now on. //////////////////////
		return wrapper; 
	}
	
});
