var Blackout = Class.create
(Toolkit, {
	// Attributes
	classes_display_container: 'BLACKOUT-display-container',	//
	classes_document_mask:     'BLACKOUT-document-mask',			//
	
	effects_duration: 0.25,	//
	
	mask_opacity: 0.85,   //
	mask_z_index: 99999,  //
	
	// Structural elements
	document_mask: null,
	display_container: null,
	
	// Event keys
	WILL_DISPLAY:  'BLACKOUT:will_display',		//
	HAS_DISPLAYED: 'BLACKOUT:has_displayed',	//
	WILL_DISMISS:  'BLACKOUT:will_dismiss',		//
	HAS_DISMISSED: 'BLACKOUT:has_dismissed',	//
	
	// Constructor
	initialize: function (parameters)
	{
		this.document_mask = this.constructDocumentMask();
		this.display_container = this.constructDisplayContainer();
		
		Event.observe(window, 'resize', this.position.bindAsEventListener(this));
		if(/MSIE 6/i.test(navigator.userAgent)) Event.observe(window, 'scroll', this.position.bindAsEventListener(this));
	},
  
	constructDocumentMask: function()
	{
		mask = new Element('div', { 'class':this.classes_document_mask }).setStyle({ display:'none', position:'fixed', zIndex:this.mask_z_index, top:0, left:0 });
		mask.observe('click', this.dismiss.bindAsEventListener(this));
		if(/MSIE 6/i.test(navigator.userAgent)) mask.setStyle({position:'absolute'});
		return mask;
	},
	
	constructDisplayContainer: function()
	{
		var container = new Element('div', { 'class':this.classes_display_container }).setStyle({ display:'none', position:'fixed', zIndex:parseInt(this.document_mask.getStyle('zIndex')) + 1 });
		if(/MSIE 6/i.test(navigator.userAgent)) container.setStyle({position:	'absolute'});
		return container;
	},
	
	position: function (event)
	{
		this.positionDocumentMask();
		this.positionDisplayContainer();
	},
	
	positionDocumentMask: function ()
	{
		this.document_mask.setStyle({ width:document.viewport.getWidth() + 'px', height:document.viewport.getHeight() + 'px' });
		if(/MSIE 6/i.test(navigator.userAgent)) this.mask.setStyle({top:document.viewport.getScrollOffsets().top + 'px'});
	},
	
	positionDisplayContainer: function ()
	{
		var target_dimensions = {width:null, height:null};
		if(!this.display_container.visible())
		{
			this.display_container.setStyle({width:'auto', height:'auto', top:'-9999px', left:'-9999px'});
			this.display_container.show();
			target_dimensions = this.display_container.getDimensions();
			this.display_container.hide();
		}
		else target_dimensions = this.display_container.getDimensions();

		this.display_container.setStyle
		({
			top: ((document.viewport.getHeight() - target_dimensions.height) / 2) + 'px',
			left: ((document.viewport.getWidth() - target_dimensions.width) / 2) + 'px'
		});
		if(/MSIE 6/i.test(navigator.userAgent)) this.display_container.setStyle({top:	(document.viewport.getScrollOffsets().top + ((document.viewport.getHeight() - target_dimensions.height) / 2)) + 'px'});
	},
	
	position: function ()
	{
		this.positionDocumentMask();
		this.positionDisplayContainer();
	},
	
	display: function (event)
	{
		if(event) event.stop();
		
    if(!this.display_container.visible())
		{
      document.body.appendChild(this.document_mask); 
      document.body.appendChild(this.display_container);

      this.position();

      this.fire(this.WILL_DISPLAY);
      this.document_mask.appear({ duration:this.effects_duration, to:this.mask_opacity });
      this.display_container.appear({ duration:this.effects_duration, afterFinish: function () { this.fire(this.HAS_DISPLAYED); }.bind(this) });
    }
    else
    {
      this.position();
    }
	},
	
	dismiss: function (event)
	{
		if(!this.display_container.visible()) return;
		this.fire(this.WILL_DISMISS);
		this.document_mask.fade({ duration:this.effects_duration });
		this.display_container.fade({ duration:this.effects_duration, afterFinish: function () { this.clear(); this.fire(this.HAS_DISMISSED); }.bind(this) });
	},
  
  clear: function ()
	{
		this.display_container.childElements().invoke('remove');
	}
});

var Broadcast = Class.create
(Blackout, {
	// Attributes
	classes_basic_message_view:         'BROADCAST-basic-message-view',					//
	classes_basic_message_view_control: 'BROADCAST-basic-message-view-control',	//
	classes_basic_message_view_control_group: 'BROADCAST-basic-message-view-control-group',	//
	classes_basic_message_view_message: 'BROADCAST-basic-message-view-message',	//
	classes_display_container:          'BROADCAST-display-container',					//
	classes_document_mask:              'BROADCAST-document-mask',							//
	
	// Constructor
	initialize: function ($super, parameters)
	{
		$super(parameters);
		
		this.observe(this.HAS_DISMISSED, this.clear.bindAsEventListener(this));
	},
	
	display: function ($super, content)
	{
		this.clear();
    this.display_container.insert(content);
		$super();
	},
	
	alert: function (event, parameters)
	{
		if(typeof parameters == 'undefined' || typeof parameters.message == 'undefined') throw('Broadcast Exception | Missing arguments | alert(event, parameters) | Method requires the parameter: message, via a parameter object as the second argument (the first must be event or null)');
		else if(parameters.controls && parameters.controls.length != 1) throw('Broadcast Exception | Invalid argument | alert(event, parameters) | When using the controls parameter,  you must supply exactly one (1) control element');
		
		if(event != null && (typeof parameters.event_passthrough == 'undefined' || parameters.event_passthrough)) event.stop();
		
		if(typeof parameters.controls == 'undefined')
		{
			controls =
			[
				new Element('a', { 'href':'#' }).update('Ok')
			];
			controls.first().observe('click', this.dismiss.bindAsEventListener(this));
		}
		else
		{
			parameters.controls.each(function(control) { control.observe('click', this.dismiss.bind(this)); }.bind(this));
			controls = parameters.controls;
		}
		
		this.write(this.constructBasicMessageView([parameters.message], controls));
		return false;
	},
	
	confirm: function (event, parameters)
	{
		if(typeof parameters == 'undefined' || typeof parameters.message == 'undefined' || (typeof parameters.callback == 'undefined' && typeof parameters.controls == 'undefined')) throw('Broadcast Exception | Missing arguments | confirm(event, parameters) | Method requires the parameters: message and one of callback or controls, via a parameter object as the second argument (the first must be event or null)');
		else if(parameters.callback && parameters.controls) throw('Broadcast Exception | Invalid argument combination | confirm(event, parameters) | You cannot specify both callback and controls parameters during the same method call');
		else if(parameters.controls && parameters.controls.length != 2) throw('Broadcast Exception | Invalid argument | confirm(event, parameters) | When using the controls parameter,  you must supply exactly two (2) control elements');
		
		if(event != null && (typeof parameters.event_passthrough == 'undefined' || parameters.event_passthrough)) event.stop();
		
		if(typeof parameters.controls == 'undefined')
		{
			controls =
			[
				new Element('a', { 'href':'#' }).update('Cancel'),
				new Element('a', { 'href':'#' }).update('Confirm')
			];
			controls.first().observe('click', this.dismiss.bindAsEventListener(this));
			controls.last().observe('click', function(event, callback) { event.stop(); callback(event); this.dismiss(); }.bindAsEventListener(this, parameters.callback));
		}
		else
		{
			parameters.controls.each(function(control) { control.observe('click', this.dismiss.bind(this)); }.bind(this));
			controls = parameters.controls;
		}
		
		this.write(this.constructBasicMessageView([parameters.message], controls));
		return false;
	},
	
	prompt: function (event, parameters)
	{
		if(typeof parameters == 'undefined' || typeof parameters.message == 'undefined' || typeof parameters.field == 'undefined' || (typeof parameters.callback == 'undefined' && typeof parameters.controls == 'undefined')) throw('Broadcast Exception | Missing arguments | prompt(event, parameters) | Method requires the parameters: message, field and one of callback or controls, via a parameter object as the second argument (the first must be event or null)');
		else if(typeof parameters.field.tagName == 'undefined' || ['INPUT','SELECT','TEXTAREA'].indexOf(parameters.field.tagName) < 0) throw('Broadcast Exception | Invalid argument | prompt(event, parameters | Field parameter muct be an input, select or text area element handle');
		else if(parameters.callback && parameters.controls) throw('Broadcast Exception | Invalid argument combination | prompt(event, parameters) | You cannot specify both callback and controls parameters during the same method call');
		else if(parameters.controls && parameters.controls.length != 2) throw('Broadcast Exception | Invalid argument | prompt(event, parameters) | When using the controls parameter,  you must supply at least one (1) and a maximum of two (2) control elements');
		
		if(event != null && (typeof parameters.event_passthrough == 'undefined' || parameters.event_passthrough)) event.stop();
		
		if(typeof parameters.controls == 'undefined')
		{
			controls =
			[
				new Element('a', { 'href':'#' }).update('Cancel'),
				new Element('a', { 'href':'#' }).update('OK')
			];
			controls.first().observe('click', this.dismiss.bindAsEventListener(this));
			controls.last().observe('click', function(event, callback) { event.stop(); callback(); this.dismiss(); }.bindAsEventListener(this, parameters.callback));
		}
		else
		{
			parameters.controls.each(function(control) { control.observe('click', this.dismiss.bind(this)); }.bind(this));
			controls = parameters.controls;
		}
		
		this.write(this.constructBasicMessageView([parameters.message,parameters.field], controls));
		return false;
	},
	
	constructBasicMessageView: function (body_elements, controls)
	{
		var container = new Element('div', { 'class':this.classes_basic_message_view });
		body_elements.each(function (content) { container.insert(new Element('div', { 'class':this.classes_basic_message_view_message }).update(content)); }.bind(this));
		
		var control_group = new Element('ul', { 'class':this.classes_basic_message_view_control_group });
		controls.each(function(control) { control_group.insert(new Element('li', { 'class':this.classes_basic_message_view_control }).update(control)); }.bind(this));
		this.positionControlGroup(control_group);
		
		return container.insert(control_group);
	},
	
	positionControlGroup: function(control_group)
	{
		document.body.appendChild(control_group.hide());
		var dimensions = control_group.setStyle({position:'absolute', left:'-9999px'}).show().getDimensions();
		control_group.remove().setStyle({width:dimensions.width + 'px', height:dimensions.height + 'px', margin:'auto', position:'', left:''});
	}
});

var broadcast = new Broadcast();
