/************************************************************************
 *
 *  Howdy
 *  $Id: howdy.js 516 2010-06-16 08:54:36Z tom $
 *  © 2008 upstruct berlin oslo
 *
 ***************************************************************************/

/*
///////////////////////////////////////////////////////////////////////////////////
//
//  !HowdyController
//
///////////////////////////////////////////////////////////////////////////////////
*/
var HowdyController = Class.create({

	initialize: function(){
		
		pageTracker._trackPageview('/howdy');
		
		this.mouseX = 0;
		this.mouseY = 0;
		this.remarkIsOpen = false;
		this.colors = new Array('rosa','cyan','lime','yellow','red','green','blue','orange');
		this.color;
		this.logging = false;
		this.duration = 0.4;
		this.frame = '<div id="howdy" style="display:none;"><div id="history"><a id="toggle-history" href="#">History <img id="clock" src="/howdy/images/history-closed.png" alt="History" title="Click to see history." /></a><div id="history-container"><ul id="historymenu" style="left:-249px;"><li><a id="h-all" href="#">Show all</a></li><li class="selected"><a id="h-month" href="#">This month</a></li><li><a id="h-week" href="#">This week</a></li><li><a id="h-today" href="#">Today</a></li></ul></div></div><div id="status"></div><a id="exit" href="#">Press <img id="esc" src="/howdy/images/exit.png" alt="Close" title="Click to close." /> to close</a><div id="start-typing">Click anywhere on the <br/>page to type a message.</div></div>';
		this.form = '<form id="f-new-remark" class="f" method="post" action="/howdy" onsubmit="return false;"><textarea id="remark" rows="3" name="remark"></textarea><input id="char-count" disabled type="text" value="180 characters left" /><input type="submit" value="SAVE" /><input id="cancel" type="button" value="CANCEL" /></form>';
		this.startTypingTween;
		this.history = 'h-month';
		this.listener;
		
		this.observeMouse();
		this.observeKeys();
		this.checkForUpdates();
		
		$$('body')[0].insert({top:this.frame});
		var c = this;
		$('howdy').appear({ afterFinish:function(){
		
			/* This is added to a variable so it dosen't throw an error if howdy is closed before the animation is finished */
			c.startTypingTween = new Effect.Fade('start-typing',{
																	duration: 2.0,
																	delay: 4.0,
																	afterFinish:function(){
																		$('start-typing').remove();
																	}
																});
			c.showLoading('Looking for messages. Please be patient&#8230;');
			
			$('howdy').observe('click',c.showHowdyForm.bindAsEventListener(c));
			$('exit').observe('click',c.close.bindAsEventListener(c));
			$('clock').observe('click',c.toggleHistory.bindAsEventListener(c));
			
			$$('#historymenu li a').each(function(element){
				element.observe('click',c.setHistory.bindAsEventListener(c));
			});
			
			c.loadHowdys();
		
		}});

	},
	
	/**
	* Toggle the view of the hisory menu
	* @param	none
	* @return	void
	**/		
	toggleHistory: function(event){
		event.preventDefault();
		if($('historymenu').viewportOffset().left > 0){
			new Effect.Move($('historymenu'), { x: -249, y: 0, mode: 'absolute',
				afterFinish: function(){
					$('clock').src = '/howdy/images/history-closed.png';
				}
			});
		} else {
			$('clock').src = '/howdy/images/history-open.png';
			new Effect.Move($('historymenu'), { x: 0, y: 0, mode: 'absolute' });
		}
	},

	/**
	* Set the current scope of the history. This is triggerd by a listener attached on initialize.
	* @param	event
	* @return	void
	**/
	setHistory: function(event){
		event.preventDefault();
		var c = this;
		c.showLoading('Updating messages &#8230;');
	
		var element = event.target;
		$(c.history).up().removeClassName('selected');
		c.history = element.id;
		$(c.history).up().addClassName('selected');
		/*
			Optimally we could use 'this.toggleHistory' but since it does not have a callback there 
			is no way of knowing when the historymenu is finished closing to fire of the AJAX request.
			The workaround is to include the effect to close the historymenu here aswell.
		*/
		new Effect.Move($('historymenu'),{ x: -249, y: 0, mode: 'absolute',
				afterFinish: function(){ 
					$('clock').src = '/howdy/images/history-closed.png'; 
					
					new Ajax.Request('/howdy-history',{
						onCreate: function(){ },
						onSuccess: function(transport){
							if(this.logging)
								console.info('HowdyController-> New history is rescived.');
							
							var response = transport.responseJSON;
							var j_howdy = response.content;
							var a_howdy = new Array();
							
							$$('#howdy div.hremark').each(function(h){
								var exists = false;
								j_howdy.each(function(j_obj){
									if($(h).id.sub('h','') == j_obj.id)
										exists = true;
									
									if(!$('h'+j_obj.id)){
										var m = new Howdy(j_obj);
										m.add();
									}
								})
								
								if(!exists)
									$(h).remove();
								
							});
							
							c.hideLoading();
							
						},
						parameters: {
							history: c.history.sub('h-', '')
						}
					});
				}
		});
		
	},

	/**
	* Displays the loading element with $message
	* @param	message:String
	* @return	void
	**/
	showLoading: function(message){
		if($('status')){
			var loader = '<object id="loading-icon" width="20" height="20" data="/swf/loader.swf" type="application/x-shockwave-flash"><param value="/swf/loader.swf" name="src"/><param value="transparent" name="wmode"/></object> '+message;
			$('status').update(loader);
		}
	},
	
	/**
	* Hides the loading element
	* @param	none
	* @return	void
	**/
	hideLoading: function(){
		if($('status')){
			$('status').update('');
		}
	},
	
	/**
	* Creates a eventlistener that tracks the mouseposition
	* @param	none
	* @return	void
	**/
	observeMouse: function(){
		
		if(this.logging)
			console.info('HowdyController-> Starting to track mouse position');
		
		var c = this;
		c.listener = {
			mouse: function(e){
				c.mouseX = Event.pointerX(e);
				c.mouseY = Event.pointerY(e);
			}
		}
		c.listener.eMouse = c.listener.mouse.bindAsEventListener(c.listener);
		Event.observe(document, 'mousemove', c.listener.eMouse);
		
	},
	
	/**
	* Observes all keypresses, if esc is pressed Howdy will close
	* @param	none
	* @return	void
	**/
	observeKeys: function(){

		if(this.logging)
			console.info('HowdyController-> Starting to listen for keystrokes');
		
		var c = this;
		c.listener = {
			keys: function(e){
				if(e.keyCode == 27 || e.charCode == 27){
					if($('howdy'))
						c.close();
				}
			}
		}
		c.listener.eKeys = c.listener.keys.bindAsEventListener(c.listener);
		Event.observe(document, 'keypress', c.listener.eKeys);
	
	},
	
	/**
	* Loads all remarks from the server and adds them to the page
	* @param	none
	* @return	void
	**/
	loadHowdys: function(){
		
		if(this.logging)
			console.info('HowdyController-> Loading howdymessages from the server.');
		
		var c = this;
		
		new Ajax.Request('/howdy-load',{
			onCreate: function(){ },
			onSuccess: function(transport){
			
				if(this.logging)
					console.info('HowdyController-> howdymessages loaded successfully.');
				
				var j_howdy = transport.responseJSON.content;
				for(var h = 0; h < j_howdy.length; h++){
					var m = new Howdy(j_howdy[h]);
					m.add();
				}
				
				c.hideLoading();
			},
			parameters: {}
		});
	
	},
	
	/**
	* Called periodically to update any new messages
	* @param	none
	* @return	void
	**/
	checkForUpdates: function(){
		
		var c = this;
		
		new PeriodicalExecuter(function(u){
			
			if(this.logging)
				console.info('HowdyController-> Loading updates howdymessages from the server.');
			
			if($('howdy')){
				c.showLoading('Checking for new remarks&#8230;');
				new Ajax.Request('/howdy-updates',{
					onCreate: function(){ },
					onSuccess: function(transport){
						
						if(this.logging)
							console.info('HowdyController-> howdymessages loaded successfully.');
						
						var response = transport.responseJSON;
						var j_howdy = response.content;
						
						for(var h = 0; h < j_howdy.length; h++){
							var m = new Howdy(j_howdy[h]);
							m.add();
							m.show();
						}
						c.hideLoading();
					},
					parameters: {}
				});
			} else {
				if(this.logging)
					console.info('HowdyController-> Updater is quitting.');
				u.stop();
			}

		},7);
	
	},
	
	/**
	* Displays a new howdy-form
	* @param	none
	* @return	void
	**/
	showHowdyForm: function(event){
		
		if(!this.remarkIsOpen && Event.element(event).id == 'howdy'){
			
			if(this.logging)
				console.info('HowdyController-> Opening new form.');
			
			var c = this;
			this.color = this.colors[this.random(this.colors.length-1)];
			
			var htpl = new HowdyTemplate({top:this.mouseY,left:this.mouseX});
			var tpl = new Template(htpl.html);
			
			var assign = {
				id:		'new-remark',
				color:	this.color,
				top: 	this.mouseY,
				left:	this.mouseX,
				text:	this.form
			};
			
			$('howdy').insert({bottom:tpl.evaluate(assign)});
			this.position('new-remark',htpl.direction);
			$$('#new-remark table')[0].appear({duration:this.duration,afterFinish:function(){
				$('remark').focus();
				$('cancel').observe('click',c.removeHowdyForm.bindAsEventListener(c));
				$('f-new-remark').observe('submit',c.createHowdy.bindAsEventListener(c));
				$('remark').observe('keyup',c.count.bindAsEventListener(c));
				c.remarkIsOpen = true;
			}});
		
		} else {
			if(this.logging)
				console.info('HowdyController-> Can\'t open new form because, a form is allready open.');
		}
	
	},
	
	/**
	* Kills a form if it's open.
	* @param	none
	* @return	void
	**/
	removeHowdyForm: function(){
		
		var c = this;
		$('new-remark').fade({afterFinish: function(){
		
			if(this.logging)
				console.info('HowdyController-> Form removed.');
		
			$('new-remark').remove();
			c.remarkIsOpen = false;
		
		},
		duration:this.duration});
		
	},
	
	/**
	* Creates a new Howdy remark and saves it to the database.
	* @param	none
	* @return	void
	**/
	createHowdy: function(){

		if(this.logging)
			console.info('HowdyController-> Saving remark.');

		if($('remark').value.length > 0 && $('remark').value.length < 180){
			var h = new Howdy({
								id:new UUID(),
								text:$('remark').value,
								date:'added by you',
								left:$('new-remark').getStyle('left').sub('px',''),
								top:$('new-remark').getStyle('top').sub('px',''),
								color:this.color
							});
			
			h.save();
			this.removeHowdyForm();
			
			return false;
		} else {
			console.warn('HowdyController-> Can\'t save new remark because the text is too long (max 180 characters).');
		}
	},
	
	/**
	* Positions the remarkfield.
	* @param	none
	* @return	void
	**/
	position: function(id,direction){
		
		var element = $$('#'+id+' table')[0];
		
		switch(direction){
			case 'TRL':
				var top = '-'+($(element).getHeight() + 15);
				var left = '-'+($(element).getWidth() - 8);
				break;
			case 'TLR':
				var top = '-'+($(element).getHeight() + 15);
				var left = 8;
				break;
			case 'BRL':
				var top = 3;
				var left = '-'+($(element).getWidth() - 8);
				break;
			case 'BLR':
				var top = 3;
				var left = 8;
				break;
		}

		$(element).setStyle({display:'none',top:top+'px',left:left+'px'});

	},
	
	/**
	* Counts the number of characters in the remark-field.
	* @param	none
	* @return	void
	**/
	count: function(){

		var length = $('remark').value.length;
		var max = 180;

		if(length < max){
			$('char-count').value = (max - length)+' characters left.';
			$('char-count').removeClassName('error');
		} else {
			$('char-count').addClassName('error');
			$('char-count').value = (max - length)+' characters left.';
		}

	},
	
	/**
	* Returns a random number between 0 and max
	* @param	none
	* @return	number
	**/
	random: function(max) {
		var r = Math.random();
		return Math.ceil(max * r);
	},
	
	/**
	* Clean-up and close Howdy
	* @param	none
	* @return	void
	**/
	close: function(event){
		if(event)
			event.preventDefault();

		var c = this;
		$('howdy').fade({afterFinish:function(){
			c.startTypingTween.cancel();
			$('howdy').remove();
			Event.stopObserving(document,'mousemove',c.listener.eMouse);
			Event.stopObserving(document,'keypress',c.listener.eKeys);
			var bonusfeatures = new keyboardFun();
		},
		duration:this.duration});

	}

});


/*
///////////////////////////////////////////////////////////////////////////////////
//
//  !Howdy
//
///////////////////////////////////////////////////////////////////////////////////
*/
var Howdy = Class.create({

	initialize: function(params){

		this.id;
		this.text;
		this.date;
		this.left;
		this.top;
		this.color;
		this.logging = false;
		this.duration = 0.4;

		if(params != undefined){
			this.set(params);
		}
		
		if(this.logging)
			console.info('Howdy-> New howdy created with id: '+this.id);

	},

	/**
	* Sets any param.
	* @param	none
	* @return	void
	**/
	set: function(params){
	
		if(params.id != undefined)
			this.id = params.id;
		if(params.text != undefined)
			this.text = params.text;
		if(params.date != undefined)
			this.date = params.date;
		if(params.left != undefined)
			this.left = params.left;
		if(params.top != undefined)
			this.top = params.top;
		if(params.color != undefined)
			this.color = params.color;
					
	},
	
	/**
	* Saves a remark
	* @param	none
	* @return	void
	**/
	save: function(){
		
		if(this.logging)
			console.info('Howdy-> Saving new comment to the server.');
		
		var h = this;
		
		new Ajax.Request('/howdy-save',{
			onCreate: function(){ },
			onSuccess: function(transport){
				if(h.debug)
					console.info('Howdy-> Save success, adding the remark to the page.');			
			
				h.add();
			},
			parameters: {
				text: h.text,
				color: h.color,
				top: h.top,
				left: h.left
			}
		});		
				
	},
	
	/**
	* Adds the object to the page.
	* @param	none
	* @return	void
	**/
	add: function(){

		var tplHtml = new HowdyTemplate({top:this.top,left:this.left});
		var template = new Template(tplHtml.html);

		var assign = {
			id:		('h'+this.id),
			color:	this.color,
			date:	this.date,
			top: 	this.top,
			left:	this.left,
			text:	this.text
		};
		
		if(this.logging)
			console.info('Howdy-> Adding new Howdy to the page.');
		
		$('howdy').insert({bottom:template.evaluate(assign)});

		if(this.logging)
			console.info('Howdy-> Howdy added to the page. Repositioning remark and attaching eventlisteners');
		
		this.position(tplHtml.direction);

		var dot = $$('#h'+this.id+' .hdot')[0];
		$(dot).observe('mouseover',this.show.bindAsEventListener(this));
		$(dot).observe('mouseout',this.hide.bindAsEventListener(this));

	},
	
	/**
	* Show the remark.
	* @param	none
	* @return	void
	**/
	show: function(){
	
		$$('#h'+this.id+' table')[0].appear({queue:{
													position: 'front',
													scope: this.id.toString(),
													limit: 2
													},
											duration:this.duration
											});
	},
	
	/**
	* Hide the remark.
	* @param	none
	* @return	void
	**/
	hide: function(){
		
		$$('#h'+this.id+' table')[0].fade({queue:{
													position: 'end',
													scope: this.id.toString(),
													limit: 2
													},
											duration:this.duration
											});

	},

	/**
	* Positions the remark correctly relative to the position of the dot.
	* @param	none
	* @return	void
	**/
	position: function(direction){

		var element = $$('#h'+this.id+' table')[0];
		
		switch(direction){
			case 'TRL':
				var top = '-'+($(element).getHeight() + 20);
				var left = '-'+($(element).getWidth() - 8);
				break;
			case 'TLR':
				var top = '-'+($(element).getHeight() + 20);
				var left = 8;
				break;
			case 'BRL':
				var top = 3;
				var left = '-'+($(element).getWidth() - 8);
				break;
			case 'BLR':
				var top = 3;
				var left = 8;
				break;
		}

		$(element).setStyle({display:'none',top:top+'px',left:left+'px'});
	}

});

/*
///////////////////////////////////////////////////////////////////////////////////
//
//  HowdyTemplate
//
///////////////////////////////////////////////////////////////////////////////////
*/
var HowdyTemplate = Class.create({

	initialize: function(params){
		
		this.html;
		this.breakTop = 120;
		this.breakLeft = document.viewport.getDimensions().width - 365;
		this.top = params.top;
		this.left = params.left;
		this.direction;
		
		this.position();
		
	},
	
	position: function(){

		if(this.left > this.breakLeft && this.top < this.breakTop){
			this.html = this.remarkBRL();
			this.direction = 'BRL';
		} else if(this.left > this.breakLeft){
			this.html = this.remarkTRL();
			this.direction = 'TRL';
		} else if(this.top < this.breakTop){
			this.html = this.remarkBLR();
			this.direction = 'BLR';
		} else {
			this.html = this.remarkTLR();
			this.direction = 'TLR';
		}

	},
	
	remarkTLR: function(){
	
		var tpl = '';
		tpl +=	'<div id="#{id}" class="hremark #{color}" style="top:#{top}px; left:#{left}px;">';
		tpl +=		'<span class="hdot">#{date}</span>';
		tpl +=		'<table class="tlr" border="0" cellspacing="0" cellpadding="0">';
		tpl +=			'<tr>';
		tpl +=				'<td class="pole"></td>';
		tpl +=				'<td class="text">#{text}</td>';
		tpl +=				'<td class="shr"></td>';
		tpl +=			'</tr>';
		tpl +=			'<tr>';
		tpl +=				'<td class="point"></td>';
		tpl +=				'<td class="shb"></td>';
		tpl +=				'<td class="shc"></td>';
		tpl +=			'</tr>';
		tpl +=		'</table>';
		tpl +=	'</div>';
		return tpl;
	
	},
	
	remarkTRL: function(){

		var tpl = '';
		tpl +=	'<div id="#{id}" class="hremark #{color}" style="top:#{top}px; left:#{left}px;">';
		tpl +=		'<span class="hdot">#{date}</span>';
		tpl +=		'<table class="trl" border="0" cellspacing="0" cellpadding="0">';
		tpl +=			'<tr>';
		tpl +=				'<td class="shr"></td>';
		tpl +=				'<td class="text">#{text}</td>';
		tpl +=				'<td class="pole"></td>';
		tpl +=			'</tr>';
		tpl +=			'<tr>';
		tpl +=				'<td class="shc"></td>';
		tpl +=				'<td class="shb"></td>';
		tpl +=				'<td class="point"></td>';
		tpl +=			'</tr>';
		tpl +=		'</table>';
		tpl +=	'</div>';
		return tpl;
	
	},

	remarkBRL: function(){
	
		var tpl = '';
		tpl +=	'<div id="#{id}" class="hremark #{color}" style="top:#{top}px; left:#{left}px;">';
		tpl +=		'<span class="hdot">#{date}</span>';
		tpl +=		'<table class="brl" border="0" cellspacing="0" cellpadding="0">';
		tpl +=			'<tr>';
		tpl +=				'<td class="shc"></td>';
		tpl +=				'<td class="shb"></td>';
		tpl +=				'<td class="point"></td>';
		tpl +=			'</tr>';
		tpl +=			'<tr>';
		tpl +=				'<td class="shr"></td>';
		tpl +=				'<td class="text">#{text}</td>';
		tpl +=				'<td class="pole"></td>';
		tpl +=			'</tr>';
		tpl +=		'</table>';
		tpl +=	'</div>';
		return tpl;
	
	},
	
	remarkBLR: function(){
	
		var tpl = '';
		tpl +=	'<div id="#{id}" class="hremark #{color}" style="top:#{top}px; left:#{left}px;">';
		tpl +=		'<span class="hdot">#{date}</span>';
		tpl +=		'<table class="blr" border="0" cellspacing="0" cellpadding="0">';
		tpl +=			'<tr>';
		tpl +=				'<td class="point"></td>';
		tpl +=				'<td class="shb"></td>';
		tpl +=				'<td class="shc"></td>';
		tpl +=			'</tr>';
		tpl +=			'<tr>';
		tpl +=				'<td class="pole"></td>';
		tpl +=				'<td class="text">#{text}</td>';
		tpl +=				'<td class="shr"></td>';
		tpl +=			'</tr>';
		tpl +=		'</table>';
		tpl +=	'</div>';
		return tpl;
	
	}
	
});


/*
///////////////////////////////////////////////////////////////////////////////////
//
//  UUID
//
///////////////////////////////////////////////////////////////////////////////////
uuid.js - Version 0.3
JavaScript Class to create a UUID like identifier

Copyright (C) 2006-2008, Erik Giberti (AF-Design), All rights reserved.
*/
function UUID(){
	this.id = this.createUUID();
}
UUID.prototype.valueOf = function(){ return this.id; }
UUID.prototype.toString = function(){ return this.id; }
UUID.prototype.createUUID = function(){
	var dg = new Date(1582, 10, 15, 0, 0, 0, 0);
	var dc = new Date();
	var t = dc.getTime() - dg.getTime();
	var h = '';
	var tl = UUID.getIntegerBits(t,0,31);
	var tm = UUID.getIntegerBits(t,32,47);
	var thv = UUID.getIntegerBits(t,48,59) + '1'; //version 1, security version is 2
	var csar = UUID.getIntegerBits(UUID.rand(4095),0,7);
	var csl = UUID.getIntegerBits(UUID.rand(4095),0,7);
	var n = UUID.getIntegerBits(UUID.rand(8191),0,7) +
			UUID.getIntegerBits(UUID.rand(8191),8,15) +
			UUID.getIntegerBits(UUID.rand(8191),0,7) +
			UUID.getIntegerBits(UUID.rand(8191),8,15) +
			UUID.getIntegerBits(UUID.rand(8191),0,15); //this last number is two octets long
	return tl + h + tm + h + thv + h + csar + csl + h + n;
}
UUID.getIntegerBits = function(val,start,end){
	var base16 = UUID.returnBase(val,16);
	var quadArray = new Array();
	var quadString = '';
	var i = 0;
	for(i=0;i<base16.length;i++){
		quadArray.push(base16.substring(i,i+1));
	}
	for(i=Math.floor(start/4);i<=Math.floor(end/4);i++){
		if(!quadArray[i] || quadArray[i] == '') quadString += '0';
		else quadString += quadArray[i];
	}
	return quadString;
}
UUID.returnBase = function(number, base){
	return (number).toString(base).toUpperCase();
}
UUID.rand = function(max){
	return Math.floor(Math.random() * (max + 1));
}

Event.observe(window, 'load', function() {

	if (window.location.hash == '#howdy' || window.location.search.toQueryParams().howdy)
		new HowdyController();	
})
