// Black Box ver 1.0
// To do: Add close trigger with default styles

var BlackBox = Class.create({
	// Settable Properties
	mask_opacity:		0.75,
	
	fx_use: 			true,
	fx_duration:		0.25,
	
	// Constants
	
	// State Flags
	frozen:				false,
	locked:				false,
	
	// Structural Elements
	mask:				null,
	wrapper:			null,
	container:			null,
	
	// Initialize
	initialize: function(params)
	{
		// Ensure settable parameter object
		if(!params) params = {};
		
		// Settable mask parameters
		if(typeof(params.mask) != 'undefined')
		{
			if(typeof(params.mask.opacity) != 'undefined') this.mask_opacity = params.mask.opacity;
		}
		
		// Settable fx parameters
		if(typeof(params.fx) != 'undefined')
		{
			if(typeof(params.fx.use) != 'undefined') this.fx_use = params.fx.use;
			if(typeof(params.fx.duration) != 'undefined') this.fx_duration = params.fx.duration;
		}
	},
	
	// Create/position mask
	createMask: function()
	{
		// Create element
		var mask = new Element('div', {id:'blackbox-mask'});
		
		// Set required mask styles
		mask.setStyle(
		{
			display:					'none',
			position:					'fixed',
			top:0, left:0,
			zIndex:						1000,
			opacity:					this.mask_opacity
		});
		
		// Set IE6 specific styles as it cannot understand position:fixed
		if(/MSIE 6/i.test(navigator.userAgent)) mask.setStyle(
		{
			position:	'absolute',
			height:		'100%',
			width:		'100%'
		});
		
		// Add behaviours
		Event.observe(mask, 'click', this.hide.bind(this));
		Event.observe(window, 'resize', this.positionMask.bind(this));
		
		// If IE6, attach position method to scroll event
		if(/MSIE 6/i.test(navigator.userAgent)) Event.observe(window, 'scroll', this.positionMask.bind(this));
		
		// Add to instance (includes check for IE6 which cannot understand position:fixed)
		this.mask = mask;
	},
	positionMask: function()
	{
		// Set height and width of mask to match the viewport
		this.mask.setStyle({width:document.viewport.getDimensions().width+'px', height:document.viewport.getDimensions().height+'px'});
		
		// For IE6, reposition the mask to sit at the top of the viewport (using scroll offset)
		if(/MSIE 6/i.test(navigator.userAgent)) this.mask.setStyle({top:document.viewport.getScrollOffsets().top + 'px'});
	},
	
	// Create/position content container
	createContainer: function()
	{
		// Create the wrapper
		var wrapper = new Element('div', {id:'blackbox-wrapper'});
		
		// Set required styles
		wrapper.setStyle({
			display:	'none',
			position:	'fixed',
			zIndex:		this.mask.getStyle('zIndex') + 1
		});
		
		// Set IE6 specific styles as it cannot understand position:fixed
		if(/MSIE 6/i.test(navigator.userAgent)) wrapper.setStyle({position:	'absolute'});
		
		// Add corner/border elements
		wrapper.appendChild(new Element('div').addClassName('box-cnr top left'));
		wrapper.appendChild(new Element('div').addClassName('box-bdr top'));
		wrapper.appendChild(new Element('div').addClassName('box-cnr top right'));
		wrapper.appendChild(new Element('div').addClassName('box-bdr left'));

		// Add body
		var container = wrapper.appendChild(new Element('div').addClassName('box-body'));

		// Add remaining corner/borders
		wrapper.appendChild(new Element('div').addClassName('box-bdr right'));
		wrapper.appendChild(new Element('div').addClassName('box-cnr bottom left'));
		wrapper.appendChild(new Element('div').addClassName('box-bdr bottom'));
		wrapper.appendChild(new Element('div').addClassName('box-cnr bottom right'));
		
		// Required child element styles
		wrapper.select('div').each(function(element) {element.setStyle({float:'left'});});
		
		// Add behaviours
		Event.observe(window, 'resize', this.positionContainer.bind(this));
		
		// If IE6, attach position method to scroll event
		if(/MSIE 6/i.test(navigator.userAgent)) Event.observe(window, 'scroll', this.positionContainer.bind(this));
		
		// Add wrapper and container to instance
		this.wrapper = wrapper;
		this.container = container;
	},
	positionContainer: function()
	{
		// Cancel if container is empty
		if(!this.container.firstDescendant()) return;

    // Set temporary styles that place the wrapper out of the viewport
    this.wrapper.setStyle({top:'-9999px', left:'-9999px'});
    // Append wrapper to the page body
    this.wrapper.show();
    // Get the content dimensions while the parent elements are 'visible'
    var content = this.container.firstDescendant().getDimensions();
    // Re-hide the wrapper and remove from stage
    this.wrapper.hide();
		
		// Resize borders to fit content
		this.wrapper.down('.box-bdr.top').setStyle({width:(content.width)+'px'});
		this.wrapper.down('.box-bdr.right').setStyle({height:(content.height)+'px'});
		this.wrapper.down('.box-bdr.bottom').setStyle({width:(content.width)+'px'});
		this.wrapper.down('.box-bdr.left').setStyle({height:(content.height)+'px'});
		
		// Get the cumulative container border sizes
		var borders = {
			width: parseInt(this.wrapper.down('.box-bdr.left').getStyle('width')) + parseInt(this.wrapper.down('.box-bdr.right').getStyle('width')),
			height: parseInt(this.wrapper.down('.box-bdr.top').getStyle('height')) + parseInt(this.wrapper.down('.box-bdr.bottom').getStyle('height'))
		};
		
		// Resize and reposition content to fit content/viewport
		this.wrapper.setStyle({
			width:		(content.width + borders.width) + 'px',
			height:		(content.height + borders.height) + 'px',
			top:		(document.viewport.getDimensions().height/2 - (content.height + borders.height)/2) + 'px',
			left:		(document.viewport.getDimensions().width/2 - (content.width + borders.width)/2) + 'px'
		});
		
		// For IE6, reposition the mask to sit at the top of the viewport (using scroll offset)
		if(/MSIE 6/i.test(navigator.userAgent)) this.wrapper.setStyle({top:	(document.viewport.getScrollOffsets().top + (document.viewport.getDimensions().height/2 - (content.height + borders.height)/2)) + 'px'});
	},
	
	// Set/clear modal content
	write: function(content, params)
	{
		
		// Create mask and wrapper/container if required
		if(!this.mask) this.createMask();
		if(!this.container) this.createContainer();
		
		// Ensure optional parameters
		if(typeof(params) == 'undefined') params = {};
		
		// Where class has been supplied, add to wrapper
		if(typeof(params.override_class) != 'undefined') this.wrapper.addClassName(params.override_class);
		
		// Lock mask if required (eg. for true modal window behaviours)
		if(typeof(params.lock) != 'undefined') this.locked = params.lock;
		
		// Append content to container
		this.container.update(content);
		
		// Validate supplied content
		if(!this.container.firstDescendant())
		{
			// Use broadcast to display validation results
			return;
		}
		
		// Display
		this.display();
	},
	clear: function()
	{
		// Erase all content from modal box body
		this.container.removeChild(this.container.firstDescendant());
		
		// Clear any set classes from wrapper
		this.wrapper.setAttribute('class','');
	},
	
	// Display/hide the modal window
	display: function()
	{
		// Cancel if frozen (locked for animation)
		if(this.frozen) return;
		
		// Add mask to the stage
		document.body.appendChild(this.mask);

		// Position mask
		this.positionMask();
		
		// Display mask
		if(this.fx_use) this.mask.appear({
			to:this.mask_opacity, 
			duration:this.fx_duration, 
			queue:{scope:'BlackBox:MASK', position:'end'},
			beforeStart: function() { this.frozen = true; }.bind(this),
			afterFinish: function() { this.frozen = false; }.bind(this)
		});
		else this.mask.show();
		
		// Add wrapper to stage
		document.body.appendChild(this.wrapper);
		
		// Position container via wrapper
		this.positionContainer();
		
		// Display wrapper/container
		if(this.fx_use) this.wrapper.appear({
			duration:this.fx_duration, 
			queue:{scope:'BlackBox:WRAPPER', position:'end'}
		});
		else this.mask.show();
	},
	hide: function(event, unlock)
	{
		// Cancel any native event
		if(event) Event.stop(event);
		
		// Unlock of argument supplied
		if(unlock) this.locked = false;
		
		// Cancel if locked
		if(this.locked || this.frozen) return;
		
		// Hide and remove mask from stage
		if(this.fx_use) this.mask.fade({
			from:this.mask_opacity,
			duration:this.fx_duration,
			queue:{scope:'BlackBox:MASK', position:'end'},
			beforeStart: function() { this.locked = true; }.bind(this),
			afterFinish: function() { this.locked = false; this.mask.remove(); }.bind(this)
		});
		else 
		{
			this.mask.hide();
			this.mask.remove();
		}
		
		// Hide and remove wrapper from stage
		if(this.fx_use) this.wrapper.fade({
			duration:this.fx_duration,
			queue:{scope:'BlackBox:WRAPPER', position:'end'},
			afterFinish: function() { this.wrapper.remove(); this.clear(); }.bind(this)
		});
		else 
		{
			this.wrapper.hide();
			this.wrapper.remove();
			this.clear();
		}
	}
});

blackbox = new BlackBox();
