/**
 * @author Ryan Johnson <http://syntacticx.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/control/contextmenu
 * @require prototype.js, livepipe.js
 */

/*global window, document, Prototype, Class, Event, $, $A, $R, Control, $value */

if(typeof(Prototype) == "undefined") {
	throw "Control.ContextMenu requires Prototype to be loaded."; }
if(typeof(Object.Event) == "undefined") {
	throw "Control.ContextMenu requires Object.Event to be loaded."; }

Control.ContextMenu = Class.create({
	initialize: function(container,options){
		Control.ContextMenu.load();
		this.options = Object.extend({
			leftClick: false,
			disableOnShiftKey: true,
			disableOnAltKey: true,
			selectedClassName: 'selected',
			activatedClassName: 'activated',
			animation: true,
			animationCycles: 2,
			animationLength: 300,
			delayCallback: true
		},options || {});
		this.activated = false;
		this.items = this.options.items || [];
		this.container = $(container);
		this.container.observe(this.options.leftClick ? 'click' : (Prototype.Browser.Opera ? 'click' : 'contextmenu'),function(event){
			if(!Control.ContextMenu.enabled || Prototype.Browser.Opera && !event.ctrlKey) {
				return; }
			this.open(event);
		}.bindAsEventListener(this));
	},
	open: function(event){
		if(Control.ContextMenu.current && !Control.ContextMenu.current.close()) {
			return; }
		if(this.notify('beforeOpen',event) === false) {
			return false; }
		this.buildMenu();
		if(this.items.length === 0){
			this.close(event);
			return false;
		}
		this.clicked = Event.element(event);
		Control.ContextMenu.current = this;
		Control.ContextMenu.positionContainer(event);
		Control.ContextMenu.container.show();
		if(this.notify('afterOpen',event) === false) {
			return false; }
		event.stop();
		return true;
	},
	close: function(event){
		if(event) {
			event.stop(); }
		if(this.notify('beforeClose') === false) {
			return false; }
		Control.ContextMenu.current = false;
		this.activated = false;
		Control.ContextMenu.container.removeClassName(this.options.activatedClassName);
		Control.ContextMenu.container.select('li').invoke('stopObserving');
		Control.ContextMenu.container.hide();
		Control.ContextMenu.container.update('');
		if(this.notify('afterClose') === false) {
			return false; }
		return true;
	},
	buildMenu: function(){
		var list = document.createElement('ul');
		Control.ContextMenu.container.appendChild(list);
		this.items.each(function(item){
			if(!(!item.condition || item.condition && item.condition() !== false)) {
				return; }
			var item_container = $(document.createElement('li'));
			item_container.update($value(item.label));
			list.appendChild(item_container);
			item_container[$value(item.enabled) ? 'removeClassName' : 'addClassName']('disabled');
			item_container.observe('mousedown',function(event,item){
				if(!$value(item.enabled)) {
					return event.stop(); }
				this.activated = $value(item.label);
			}.bindAsEventListener(this,item));
			item_container.observe('click',this.selectMenuItem.bindAsEventListener(this,item,item_container));
			item_container.observe('contextmenu',this.selectMenuItem.bindAsEventListener(this,item,item_container));
		}.bind(this));
	},
	addItem: function(params){
        if (!('enabled' in params)) { params.enabled = true; }
		this.items.push(params);
		return this;
	},
	destroy: function(){
		this.container.stopObserving(Prototype.Browser.Opera || this.options.leftClick ? 'click' : 'contextmenu');
		this.items = [];
	},
	selectMenuItem: function(event,item,item_container){
		if(!$value(item.enabled)) {
			return event.stop(); }
		if(!this.activated || this.activated == $value(item.label)){
			if(this.options.animation){
				Control.ContextMenu.container.addClassName(this.options.activatedClassName);
				$A($R(0,this.options.animationCycles * 2)).each(function(i){
					window.setTimeout(function(){
						item_container.toggleClassName(this.options.selectedClassName);
					}.bind(this),i * parseInt(this.options.animationLength / (this.options.animationCycles * 2), 10));
				}.bind(this));
				window.setTimeout(function(){
					if(this.close() && this.options.delayCallback) {
						item.callback(this.clicked); }
				}.bind(this),this.options.animationLength);
				if(!this.options.delayCallback) {
					item.callback(this.clicked); }
			}else if(this.close()) {
				item.callback(this.clicked); }
		}
		event.stop();
		return false;
	}
});
Object.extend(Control.ContextMenu,{
	loaded: false,
	capture_all: false,
	menus: [],
	current: false,
	enabled: false,
	offset: 4,
	load: function(capture_all){
		if(Control.ContextMenu.loaded) {
			return; }
		Control.ContextMenu.loaded = true;
		if(typeof(capture_all) == 'undefined') {
			capture_all = false; }
		Control.ContextMenu.capture_all = capture_all;
		Control.ContextMenu.container = $(document.createElement('div'));
		Control.ContextMenu.container.id = 'control_contextmenu';
		Control.ContextMenu.container.style.position = 'absolute';
		Control.ContextMenu.container.style.zIndex = 99999;
		Control.ContextMenu.container.hide();
		document.body.appendChild(Control.ContextMenu.container);
		Control.ContextMenu.enable();
	},
	enable: function(){
		Control.ContextMenu.enabled = true;
		Event.observe(document.body,'click',Control.ContextMenu.onClick);
		if(Control.ContextMenu.capture_all) {
			Event.observe(document.body,'contextmenu',Control.ContextMenu.onContextMenu); }
	},
	disable: function(){
		Event.stopObserving(document.body,'click',Control.ContextMenu.onClick);
		if(Control.ContextMenu.capture_all) {
			Event.stopObserving(document.body,'contextmenu',Control.ContextMenu.onContextMenu);	}
	},
	onContextMenu: function(event){
		event.stop();
		return false;
	},
	onClick: function(){
		if(Control.ContextMenu.current) {
			Control.ContextMenu.current.close(); }
	},
	positionContainer: function(event){
		var dimensions = Control.ContextMenu.container.getDimensions();
		var top = Event.pointerY(event);
		var left = Event.pointerX(event);
		var bottom = dimensions.height + top;
		var right = dimensions.width + left;
		var viewport_dimensions = document.viewport.getDimensions();
		var viewport_scroll_offsets = document.viewport.getScrollOffsets();
		if(bottom > viewport_dimensions.height + viewport_scroll_offsets.top) {
			top -= bottom - ((viewport_dimensions.height  + viewport_scroll_offsets.top) - Control.ContextMenu.offset); }
		if(right > viewport_dimensions.width + viewport_scroll_offsets.left) {
			left -= right - ((viewport_dimensions.width + viewport_scroll_offsets.left) - Control.ContextMenu.offset); }
		Control.ContextMenu.container.setStyle({
			top: top + 'px',
			left: left + 'px'
		});
	}
});
Object.Event.extend(Control.ContextMenu);
