/* * *
	developer: bitl@ua.fm
	2004-09-15: v 1.0 beta

	todo: keyboard event processing; IE/NS
 * * */

/* * *
	WARNING: some key+mouse combinations may be used by user system instead of this event processor
	for example under WindowMaker: alt+mouseLeft and alt+mouseRight - produces "mouseout" for this event processor
 * * */

/* * *
 * *	Usage example
 *
 * *	user page HTML
 *
 * <SCRIPT language="JavaScript1.2" type="text/javascript" src="/js/event_core.js"></SCRIPT>
 *
 *
 * *	user page javascript
 *
 * // create event handler (more then one may be created for different page elements)
 * eh_1 = new EventHandler( "handler_1" );
 *
 * // assign your functions to actions that will be raised for corresponding events
 * eh_1.handleMouseDown = eh_1.handleMouseUp = eh_1.handleMouseOver = eh_1.handleMouseOut = EventHandler_1_handleMouseEvent;
 *
 * // define your functions
 * function EventHandler_1_handleMouseEvent( mouse_state, keyboard_state, an_event ){
 *	window.alert( "handler "+ this.id +": "+ mouse_state.toString() +"; "+ keyboard_state.toString()
 *		+"; event target: "+ an_event.target_id +", ctime: "+ an_event.ctime );
 * }
 *
 * // define the function where your event handlers wiil be registered
 * function post_load_actions(){
 *	event_core__dispatcher.register( "test_target", eh_1 );
 * }
 *
 * *	user page HTML
 *
 * <BODY onload="javascript:post_load_actions();void(0);">
 * ...
 *
 * <IMG id="test_target" src="file:///home/bitl/javamachine150.jpg" />
 *
 * *
 *
 * Feel free to create several handlers and to register them for different targets.
 * You may also register one handler for several targets.
 *
 * * */

/* * *
	MouseState class
 * * */

function discardEvent( event_object ){
	if( event_object ){
		if( userBrowser.isNS() ){
			//window.status = "browser: NS";
			//event_object.stopPropagation( true );
			event_object.stopPropagation( true );
			//event_object.preventDefault( true );
			event_object.preventDefault( true );
			//window.alert( "NS" );
		}else{
			//event_object.returnValue = true;
			event_object.cancelBubble = true;
			event_object.returnValue = false;
			//window.status = "browser: IE";
		}
		//event_object = null;
	}
}

function MouseState( std_event_object ){
	this.type = std_event_object.type.toLowerCase();

	this.button = std_event_object.button;
	this.leftDown =   ( userBrowser.isNS() ? 0 : 1 ) == this.button;
	this.middleDown = ( userBrowser.isNS() ? 1 : 4 ) == this.button;
	this.rightDown =  2 == this.button;

	this.clientX = std_event_object.clientX;
	this.clientY = std_event_object.clientY;

	this.isClick = MouseState_isClick;
	this.isDown = MouseState_isDown;
	this.isUp = MouseState_isUp;
	this.isOver = MouseState_isOver;
	this.isMove = MouseState_isMove;

	// IE
	this.isDragStart = MouseState_isDragStart;
	this.isDragOver = MouseState_isDragOver;
	this.isDrag = MouseState_isDrag;

	this.isOut = MouseState_isOut;
	this.isDoubleClick = MouseState_isDoubleClick;

	this.toString = MouseState_toString;
}

function MouseState_toString(){
	return "type: "+ this.type +", button: "+ this.button +", clientX: "+ this.clientX +", clientY: "+ this.clientY;
}

function MouseState_isClick(){ return this.type == "click"; }

function MouseState_isDown(){ return this.type == "mousedown"; }

function MouseState_isUp(){ return this.type == "mouseup"; }

function MouseState_isOver(){ return this.type == "mouseover"; }

function MouseState_isMove(){ return this.type == "mousemove"; }

function MouseState_isDragStart(){
	return this.type == "mousedragstart" || this.type == "dragstart" || this.type == "dragdrop"; }

function MouseState_isDragOver(){
	return this.type == "mousedragover" || this.type == "dragover" || this.type == "dragdrop"; }

function MouseState_isDrag(){
	return this.type == "mousedrag" || this.type == "drag" || this.type == "dragdrop"; }

function MouseState_isOut(){ return this.type == "mouseout"; }

function MouseState_isDoubleClick(){ return this.type == "dblclick"; }

/* * *
	KeyboardState class
 * * */

function KeyboardState( std_event_object ){
	this.type = std_event_object.type;

	this.shiftDown = std_event_object.shiftKey;
	this.ctrlDown = std_event_object.ctrlKey;
	this.altDown = std_event_object.altKey;

	// if by keyboard: keyCode, Use String.fromCharCode(keyCode) to convert code to string

	this.toString = KeyboardState_toString;
}

function KeyboardState_toString(){
	return "type: "+ this.type +", shift: "+ this.shiftDown +", ctrl:"+ this.ctrlDown +", alt: "+ this.altDown;
}

/* * *
	UserEvent class
 * * */

function UserEvent( target_id, type, message ){
	this.target_id = target_id;
	this.type = type;
	this.message = message;

	this.substituteTarget = UserEvent_substituteTarget;
}

function UserEvent_substituteTarget( new_target_id ){
	this.target_id = new_target_id;
}

/* * *
	UserState class
 * * */

function UserState( user_event ){
	this.type = user_event.type;
	this.message = user_event.message;

	this.toString = UserState_toString;
}

function UserState_toString(){ return "type: "+ this.type +", message: "+ this.message; }

/* * *
	MyEvent class
	('Event' is a reserved word for IE)
 * * */

/* currently support next event generators: "mouse", "keyboard", "user"
	the parameter "message" plays role only if generator is "user"
	the parameter "event_object" should be the standard JavaScript event object in case of "mouse" or "keyboard" generator
		in case of "user" generator this parameter must contain a UserEvent object
 */
function MyEvent( event_object, generator, message ){
	/* assign methods */
	this.substituteTarget = MyEvent_substituteTarget;
	this.toString = MyEvent_toString;
	this.byMouse = MyEvent_byMouse;
	this.byKeyboard = MyEvent_byKeyboard;
	this.byUser = MyEvent_byUser;

	/* */
	this.generator = generator == "" ? "mouse" : generator;

	/* standard event.timeStamp property doesn't supported by IE */
	this.ctime = ( new Date() ).getTime();

	//this.target_id = event_object.target.id;
	this.target_id = typeof event_object.target == "undefined" ? event_object.srcElement.id
		: event_object.target.id;

	//window.alert( "i1" );

	if( this.byMouse() || this.byKeyboard() ){
	//window.alert( "i2" );
		this.keyboard_state = new KeyboardState( event_object );
	//window.alert( "i3" );
		if( this.byMouse() ){
			this.mouse_state = new MouseState( event_object );
		}else if( this.byKeyboard() ){
			// add pressed key to this.keyboard_state
		}
		discardEvent( event_object );
	}else if( this.byUser() ){
		this.user_state = new UserState( event_object );
	}
}

function MyEvent_substituteTarget( new_target_id ){
	this.target_id = new_target_id;
}

function MyEvent_toString(){
	var state_string = "";
	if( this.byMouse() || this.byKeyboard() ){
		state_string = "keyboard state: " + this.keyboard_state.toString();
		if( this.byMouse() ){
			state_string += "; " + "mouse state: "+ this.mouse_state.toString();
		}
	}else if( this.byUser() ){
		state_string = this.user_state.toString();
	}

	return "generator: "+ this.generator +", target_id:"+ this.target_id +", ctime: "+ this.ctime +"; "+ state_string;
}

function MyEvent_byMouse(){ return this.generator == "mouse"; }

function MyEvent_byKeyboard(){ return this.generator == "keyboard"; }

function MyEvent_byUser(){ return this.generator == "user"; }

/* * *
	EventDispatcher class
 * * */

function EventDispatcher(){
	this.handlers = new Array();

	this.target_substitutions = new Array();

	/* assign methods */
	this.addHandler = EventDispatcher_addHandler;
	this.removeHandler = EventDispatcher_removeHandler;

	this.dispatch = EventDispatcher_dispatch;

	this.assignStdActions = EventDispatcher_assignStdActions;
	this.clearStdActions = EventDispatcher_clearStdActions;
	this.register = EventDispatcher_register;
	this.unregister = EventDispatcher_unregister;
	this.registerTargetSubstitution = EventDispatcher_registerTargetSubstitution;
}

function EventDispatcher_addHandler( target_id, handler ){
	this.handlers[ target_id ] = handler;
}

function EventDispatcher_removeHandler( target_id ){
	this.handlers[ target_id ] = "";
}

function EventDispatcher_dispatch( my_event ){
	var old_id = my_event.target_id;
	if( this.target_substitutions[ old_id ] ){
		my_event.substituteTarget( this.target_substitutions[ old_id ] );
		// window.alert( this.target_substitutions[ old_id ] +" ... "+ my_event.target_id );
	}

	/*evtype = my_event.mouse_state.type;
	if( !( evtype == "mousemove" || evtype == "mouseup" || evtype == "mousedown"
		|| evtype == "mouseover" || evtype == "mouseout" )
	){
		document.getElementById( "event_debug" ).value += "\ndispatch "+ my_event.toString();
		//document.getElementById( "event_debug" ).value += "\n"+ this.handlers[ my_event.target_id ].handle;
	}*/

	if( this.handlers[ my_event.target_id ] ){
		this.handlers[ my_event.target_id ].handle( my_event );
	}else{
		//window.alert( "No handlers for: old_id: "+ old_id +"; "+ my_event.toString() );
	}
}

function EventDispatcher_assignStdActions( target_id ){
	elm = document.getElementById( target_id );
	elm.onclick = on_mouse_event;
	elm.onmousedown = on_mouse_event;
	elm.onmouseup = on_mouse_event;
	elm.onmousemove = on_mouse_event;
	elm.onDragStart = on_mouse_event;
	elm.onDragOver = on_mouse_event;
	elm.onDrag = on_mouse_event;
	elm.onmouseover = on_mouse_event;
	elm.onmouseout = on_mouse_event;
	elm.ondblclick = on_mouse_event;
	//
	elm.onkeypress = on_keyboard_event;
	// NO: elm.onfocus = on_keyboard_event;
	// NO: elm.onchange = on_keyboard_event;
}

function EventDispatcher_clearStdActions( target_id ){
	elm = document.getElementById( target_id );
	elm.onclick = "";
	elm.onmousedown = "";
	elm.onmouseup = "";
	elm.onmousemove = "";
	elm.onDragStart = "";
	elm.onDragOver = "";
	elm.onDrag = "";
	elm.onmouseover = "";
	elm.onmouseout = "";
	elm.ondblclick = "";
	//
	elm.onkeypress = "";
}

function EventDispatcher_register( target_id, event_handler ){
	this.addHandler( target_id, event_handler );
	this.assignStdActions( target_id );
}

function EventDispatcher_unregister( target_id ){
	this.removeHandler( target_id );
	this.clearStdActions( target_id );
}

function EventDispatcher_registerTargetSubstitution( donor_id, recipient_id ){
	this.target_substitutions[ donor_id ] = recipient_id;
	this.assignStdActions( donor_id );
}

/* * *
	EventHandler class
 * * */

function EventHandler( id ){
	this.id = id ? id : ( new Date() ).getTime();

	/* adding methods */
	this.handle = EventHandler_handle;

	/* init actions for testing purposes
	this.handleMouseClick = EventHandler_handleMouseClick;
	this.handleMouseDown = EventHandler_handleMouseDown;
	this.handleMouseUp = EventHandler_handleMouseUp;
	this.handleUserEvent = EventHandler_handleUserEvent;
	*/

	this.handleMouseClick = this.handleMouseDown = this.handleMouseUp
		= this.handleMouseOver = this.handleMouseMove
		= this.handleDragStart = this.handleDragOver = this.handleDrag
		= this.handleMouseOut = this.handleDoubleClick = false;
	this.handleKeyPress = false;
	this.handleUserEvent = false;

}

/* test actions
function EventHandler_handleMouseClick( mouse_state, keyboard_state ){
	window.alert( "the event handler "+ this.id + " is handling MouseClick: "+ mouse_state.toString() ); }
function EventHandler_handleMouseDown( mouse_state, keyboard_state ){
	window.alert( "the event handler "+ this.id + " is handling MouseDown: "+ mouse_state.toString() ); }
function EventHandler_handleMouseUp( mouse_state, keyboard_state ){
	window.alert( "the event handler "+ this.id + " is handling MouseUp: "+ mouse_state.toString() ); }
function EventHandler_handleUserEvent( user_state ){
	window.alert( "the event handler "+ this.id + " is handling UserEvent: "+ user_state.toString() ); }
*/

function EventHandler_handle( my_event ){
	if( my_event.byMouse() ){
		var mstate = my_event.mouse_state;
		var kstate = my_event.keyboard_state;

		/*evtype = mstate.type;
		if( !( evtype == "mousemove" || evtype == "mouseup" || evtype == "mousedown"
			|| evtype == "mouseover" || evtype == "mouseout" )
		){
			document.getElementById( "event_debug" ).value += "\nhandle: "+ my_event.toString();
			document.getElementById( "event_debug" ).value += "\nhandle: "
				+ ( this.handleDoubleClick && mstate.isDoubleClick() ? " DOUBLE " : " NOTD " );
		}*/

		if( this.handleDoubleClick && mstate.isDoubleClick() ){
			this.handleDoubleClick( mstate, kstate, my_event );

                }else if( this.handleMouseClick && mstate.isClick() ){
			this.handleMouseClick( mstate, kstate, my_event );

//userBrowser.isNS()

		}else if( this.handleDragStart && mstate.isDragStart() ){
			this.handleDragStart( mstate, kstate, my_event );
		}else if( this.handleDragOver && mstate.isDragOver() ){
			this.handleDragOver( mstate, kstate, my_event );
		}else if( this.handleDrag && mstate.isDrag() ){
			this.handleDrag( mstate, kstate, my_event );

		}else if( this.handleMouseDown && mstate.isDown() ){
			this.handleMouseDown( mstate, kstate, my_event );
		}else if( this.handleMouseUp && mstate.isUp() ){
			this.handleMouseUp( mstate, kstate, my_event );
		}else if( this.handleMouseOver && mstate.isOver() ){
			this.handleMouseOver( mstate, kstate, my_event );
		}else if( this.handleMouseMove && mstate.isMove() ){
			this.handleMouseMove( mstate, kstate, my_event );
		}else if( this.handleMouseOut && mstate.isOut() ){
			this.handleMouseOut( mstate, kstate, my_event );
		}else{
			//window.status = "warning: unknown event type: " + my_event.type;
			//window.alert( "warning: unknown event type: " + my_event.type );
		}
        }else if( my_event.byKeyboard() ){
		//if( this.handleKeyPress
		//window.alert( "key pressed" );
	}else if( this.handleUserEvent && my_event.byUser() ){
		this.handleUserEvent( my_event.user_state, my_event );
	}
}

var prev_click_event = false;

function on_mouse_event( e ){
	/*evtype = (window.event ? window.event : e).type.toLowerCase();
	if( !( evtype == "mousemove" || evtype == "mouseup" || evtype == "mousedown"
		|| evtype == "mouseover" || evtype == "mouseout" )
	){
		document.getElementById( "event_debug" ).value += "\n["+ evtype +"]";
	}*/

	evt = (window.event ? window.event : e);
	my_event = new MyEvent( evt, "mouse", "" )
	discardEvent( evt );
	event_core__dispatcher.dispatch( my_event );

	//*
	/*evt = (window.event ? window.event : e);
	cur_event = new MyEvent( evt, "mouse", "" );
	if( prev_click_event ){
		event_core__dispatcher.dispatch( prev_click_event );
		prev_click_event = false;
		event_core__dispatcher.dispatch( cur_event );
	}else{
		if( evt.type == "click" ){
			prev_click_event = cur_event;
		}else{
			event_core__dispatcher.dispatch( cur_event );
		}
	}//*/

	/*if( !( userBrowser.isNS() && evt.type == "click" ) ){
		discardEvent( evt );
	}*/

	/*
	evt = window.event ? window.event : e;
	event_object = evt;
	my_evt = new MyEvent( event_object, "mouse", "" );
	event_core__dispatcher.dispatch( my_evt );
	*/
}

function on_keyboard_event( e ){
	event_core__dispatcher.dispatch( new MyEvent( (window.event ? window.event : e), "keyboard", "" ) );
	discardEvent( window.event ? window.event : e );
}

var event_core__dispatcher = new EventDispatcher();

/* * * end of event model implementation * * */
