//-----------------------------------------------------------------------------
// CORE AJAX Utility Functions And Classes Library
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
var GUI_FORM_TOP_ZINDEX = 10000;
var GUI_DESKTOP_ZINDEX = 0;
var GUI_MINIMIZED_FORM_WIDTH = 150;

var AJAX_ENTRYPOINT = "/ajax";
var AJAX_EOR = "||";
var AJAX_EOV = "``";
var AJAX_MESSAGE_ARG_SEPARATOR = "~|~";
var AJAX_MESSAGE_ARG_NAMEVALUE_SEPARATOR = "~@~";
var AJAX_MESSAGE_ARGS_DATA_SEPARATOR = "[END_OF_ARGS]";
var AJAX_SERVER_EVENT_CHECK_INTERVAL = 200;

//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
//! This associative array is used to keep the locks for various DB tables and records, while some user is working (editing) on it
//! Example: g_db_locks["users_134"] = true, that means user with ID 134 from DB table users is being edited right now, so its locked
var g_db_locks = Array();
var g_user_id = 0;
var g_username = "";

//! The main Gui class
var Gui = {};

//-----------------------------------------------------------------------------
// Utilitarian functions and classes
//-----------------------------------------------------------------------------

//! Used for prototype definition and class inheritance
Function.prototype.method = function( name, func )
{
	this.prototype[name] = func;
	return this;
};

//! Used for class inheritance
Function.method( "inherits", function( parent )
{
	var d = {}, p = ( this.prototype = new parent() );

	this.method( "uber", function uber( name )
	{
		if( !( name in d ) )
		{
			d[name] = 0;
		}
		
		var f, r, t = d[name], v = parent.prototype;
		
		if( t )
		{
			while( t )
			{
				v = v.constructor.prototype;
				t -= 1;
			}
			f = v[name];
		}
		else
		{
			f = p[name];
			
			if( f == this[name] )
			{
				f = v[name];
			}
		}
		
		d[name] += 1;
		r = f.apply( this, Array.prototype.slice.apply( arguments, [1] ) );
		d[name] -= 1;
		return r;
	});
	
	return this;
});

//! Return the widgets elements by classname (html tag name)
function get_widgets_by_class( classname )
{
	var elems;

    if( is_ie )
    {
        var elements = document.getElementsByTagName( classname );

        elems = new Array();
        j = 0;

        for( i = 0; i < elements.length; i++ )
        {
            if( elements[i].scopeName == "gui" )
            {
                elems[j++] = elements[i];
            }
        }
    }
    else
    {
        elems = document.getElementsByTagName( "gui:" + classname );
    }

    return elems;
}

//! Adds a new event handler for the specified target element and event
function add_handler( target, eventName, handlerName )
{ 
	if( target.addEventListener )
	{ 
		target.addEventListener( eventName, function(e){target[handlerName](e);}, false );
	}
	else
	if( target.attachEvent )
	{ 
		target.attachEvent("on" + eventName, function(e){target[handlerName](e);});
	}
	else
	{ 
		var originalHandler = target["on" + eventName]; 
		if( originalHandler )
		{ 
			target["on" + eventName] = function(e){originalHandler(e);target[handlerName](e);}; 
		}
		else
		{ 
			target["on" + eventName] = target[handlerName]; 
		} 
	} 
}

//! Function dispatcher for custom event model in widgets
//! \param e the core ajax event, can be null if you cannot provide an event, but you must set widget
//! \param event_id the custom widget's event id (each widget has its own events)
//! \param arg custom optional argument for that custom widget event
//! \param widget must be set to some widget if you cannot provide 'e' and set 'e' to null
function onEvent_dispatch( e, event_id, arg, widget /*optional, fill it when you have e == null (the event)*/ )
{
	var the_widget = undefined;

	if( e != null )
	{
		the_widget = e.target_node.widget;
	}
	else
	if( widget != undefined )
		the_widget = widget;

	if( the_widget != undefined )
	{
		if( the_widget.onEvent != null )
			the_widget.onEvent( event_id, the_widget, arg );
		else
		if( the_widget.onEventDefault != null )
			the_widget.onEventDefault( event_id, the_widget, arg );
	}
}

//! Get the attribute onEvent for the element
function get_event_handler( element, widgetClass )
{
	var id = get_attr_value( element, "id", "" );
	var event_id = get_attr_value( element, "event_id", "" );

	if( id == "" )
		return null;

	if( event_id != "" )
		id = event_id;

	var funcName = id + "_event";

	if( !function_exists( funcName ) )
	{
		if( !function_exists( ( widgetClass + "_event_default" ) ) )
			return null;
		else
			return eval( widgetClass + "_event_default" );
	}

	return eval( funcName );
}

//! Get the additional parameters for the element
function get_additional_parameters_handler( element, widgetClass )
{
	var id = get_attr_value( element, "id", "" );
	var param_id = get_attr_value( element, "parameters_id", "" );

	if( id == "" )
		return null;

	if( param_id != "" )
		id = param_id;

	var funcName = id + "_parameters";

	if( !function_exists( funcName ) )
	{
		return null;
	}

	return eval( funcName );
}


//! Copy attributes from source element to destination element
function copy_attributes( src, dst )
{
	var attrs = src.attributes;

	for( var i = 0; i < attrs.length; i++ )
	{
		dst.setAttribute( attrs[i].nodeName, attrs[i].nodeValue );
    }	
}

//! Check if a lock is set on a specific DB table and record id
function ajax_is_record_locked( tableName, recordId )
{
	return ( g_db_locks[tableName + "_" + recordId] == true );
}

//! Check if a lock is on a generic context
function ajax_is_locked( context )
{
	return ( g_db_locks[context] == true );
}

//! Set a lock on a DB table record, by sending an ajax message
function ajax_set_lock_on_record( tableName, recordId, bLock, oncomplete )
{
	var args = Array();

	args["context"] = tableName + "_" + recordId;
	args["lock"] = bLock ? "1":"0";
	g_db_locks[args["context"]] = bLock;

	ajax_send_message( "set_server_db_lock", args, "", oncomplete );
}

//! Set a lock on a generic context, by sending an ajax message
function ajax_set_lock( context, bLock, oncomplete )
{
	var args = Array();

	args["context"] = context;
	args["lock"] = bLock ? "1":"0";
	g_db_locks[context] = bLock;

	ajax_send_message( "set_server_db_lock", args, "", oncomplete );
}

//! This function will retreive the current db locks
function ajax_get_server_db_locks()
{
	var args = Array();

	ajax_send_message( "get_server_db_locks", args, "", on_ajax_get_db_locks_response );
}

//! This response function will fill the locks
function on_ajax_get_db_locks_response( http_request )
{
	if( ajax_success( http_request ) )
	{
		var msg = ajax_read_message( http_request );
		var recs = ajax_unserialize_db_array( msg.data );

		g_db_locks = Array();

		for( var i = 0; i < recs.length; i++ )
		{
			g_db_locks[recs[i]["lock_context"]] = recs[i]["lock_state"];
		}

		if( function_exists( "on_ajax_update_locks" ) )
			on_ajax_update_locks();
	}
}

//! Create the main HTTP Request object used in AJAX communication
function create_http_request()
{
	var http_request;

	try
	{
		http_request = new XMLHttpRequest();
	}
	catch( e )
	{
		http_request = null;
	}

	return http_request;
}

//! Returns true if there is a HTTP call in progress
function ajax_is_busy( http_request )
{
	if( !http_request )
		return false;

	if( http_request.readyState == "undefined" )
		return false;

	switch( http_request.readyState )
	{
		case 1, 2, 3:
			return true;
			break;
		// case 4 and 0
		default:
			return false;
			break;
	}

	return false;
}

//! Returns true if response is ready/successful
function ajax_success( http_request )
{
	return http_request.readyState == 4;
}

//! Make an AJAX GET request on the server
//! \param args the arguments array
//! \param oncompletefunc the function name called when request was completed
//! \param object_ref some optional object reference you want to attach to http request before sending request (used for widgets)
function ajax_call( args, oncompletefunc, object_ref )
{
	var http_request = create_http_request();
	var newArgs = args.join( "&" );

	if( object_ref != undefined && object_ref != null )
		http_request.objectRef = object_ref;

	http_request.open( "GET", AJAX_ENTRYPOINT + "?" + newArgs + "&cachekill=" + Math.random(), true );

	if( oncompletefunc != "undefined" && oncompletefunc != null )
		http_request.onreadystatechange = function relay_with_object(){ oncompletefunc( http_request ) };

	http_request.send(null);

	return http_request;
}

//! Return the response text of XMLHttpRequest object
function ajax_get_response( http_request )
{
	return http_request.responseText;
}

//! This will return an associative array of db records
function ajax_unserialize_db_array( results )
{
	if( results == "" || results == undefined || results == null )
		return new Array();

	var dataset = new Array();
	var rows = results.split( AJAX_EOR );

	if( rows.length == 0 )
		return dataset;

	// first row is field names
	var fields = rows[0].split( AJAX_EOV );

	for( var i = 1; i < rows.length; i++ )
	{
		data = rows[i].split( AJAX_EOV );
		dataset[i-1] = new Array();

		for( var j = 0; j < fields.length; j++ )
		{
			dataset[i-1][fields[j]] = data[j];
		}
	}

	return dataset;
}

//! This will return a simple associative array
function ajax_unserialize_array( result )
{
	if( results == "" || results == undefined || results == null )
		return new Array();

	var dataset = new Array();
	var elems = result.split( AJAX_EOR );

	if( elems.length == 0 )
		return dataset;

	for( var i = 0; i < elems.length; i++ )
	{
		data = elems[i].split( AJAX_EOV );
		dataset[data[0]] = data[1];
	}

	return dataset;
}

//! Send an AJAX message to the php script entrypoint (named usually: ajax.php or ajax - when friendly urls on)
function ajax_send_message( message, args, data, oncomplete, object_ref )
{
	var the_args = "";

	for( var argname in args )
	{
		the_args += argname + AJAX_MESSAGE_ARG_NAMEVALUE_SEPARATOR + args[argname] + AJAX_MESSAGE_ARG_SEPARATOR;
	}

	var http_request = ajax_call( new Array( "ajax_message=" + message,
								  "ajax_message_args=" + the_args,
								  "ajax_message_data=" + data ),
								  oncomplete, object_ref );
	return http_request;
}

//! Read the incoming message data
//! \return { message, args /*associative array*/, data }
function ajax_read_message( http_request )
{
	var message = ajax_get_response( http_request );

	if( message == "" )
		return { message : "", args : new Array(), data : "" };

	var data = message.split( AJAX_MESSAGE_ARGS_DATA_SEPARATOR );
	var msg_args = data[0].split( AJAX_MESSAGE_ARG_SEPARATOR );
	var msg_data = data[1];

	var the_args = new Array();

	for( var i = 0; i < msg_args.length; i++ )
	{
		data = msg_args[i].split( AJAX_MESSAGE_ARG_NAMEVALUE_SEPARATOR );
		the_args[data[0]] = data[1];
	}

	return { message : the_args["ajax_message"], args : the_args, data : msg_data };
}

function include( file )
{
	var script  = document.createElement('script');
	script.src  = file;
	script.type = "text/javascript";
	script.defer = true;
	document.getElementsByTagName( "head" ).item( 0 ).appendChild( script );
}

Gui.Widget = function()
{
	this.x = 0;
	this.y = 0;
	this.width = 0;
	this.height = 0;
	this.widget_class = "Widget";
	this.state = 0;
}

include( "/core/js/gui_autocompleteedit.js?version=1.03" );
// include( "/core/js/gui_button.js?version=1.02" );
// include( "/core/js/gui_check.js?version=1.02" );
// include( "/core/js/gui_edit.js?version=1.02" );
// include( "/core/js/gui_fieldset.js?version=1.02" );
// include( "/core/js/gui_form.js?version=1.02" );
// include( "/core/js/gui_group.js?version=1.02" );
// include( "/core/js/gui_imagecroptool.js?version=1.02" );
// include( "/core/js/gui_list.js?version=1.02" );
// include( "/core/js/gui_textarea.js?version=1.02" );
// include( "/core/js/gui_shortcuticon.js?version=1.02" );
// include( "/core/js/gui_uploader.js?version=1.02" );
// include( "/core/js/gui_grid.js?version=1.02" );
// include( "/core/js/gui_tabs.js?version=1.02" );
// include( "/core/js/gui_navbar.js?version=1.02" );
// include( "/core/js/gui_dialog.js?version=1.02" );
// include( "/core/js/gui_record_editor.js?version=1.02" );

Gui.init_widget_class = function( classname )
{
	// init buttons
	var elems = get_widgets_by_class( classname );
	var idx = 0;

	for( var i = 0; i < elems.length; i++ )
	{
		var id = elems[i].getAttribute( "id" );
		var widget = null;

		if( id == "" || id == undefined )
			id = "widget" + ( idx++ );

		switch( classname )
		{
			//case "button":				widget = new Gui.Button( elems[i] ); break;
			//case "edit":				widget = new Gui.Edit( elems[i] ); break;
			//case "check":				widget = new Gui.Check( elems[i] ); break;
			//case "fieldset":			widget = new Gui.Fieldset( elems[i] ); break;
			//case "group":				widget = new Gui.Group( elems[i] ); break;
			//case "list":				widget = new Gui.List( elems[i] ); break;
			//case "textarea":			widget = new Gui.Textarea( elems[i] ); break;
			//case "form":				widget = new Gui.Form( elems[i] ); break;
			//case "image_crop_tool":		widget = new Gui.ImageCropTool( elems[i] ); break;
			case "autocomplete_edit":	widget = new Gui.AutocompleteEdit( elems[i] ); break;
			//case "shortcut_icon":		widget = new Gui.ShortcutIcon( elems[i] ); break;
			//case "uploader":			widget = new Gui.Uploader( elems[i] ); break;
			//case "grid":				widget = new Gui.Grid( elems[i] ); break;
			//case "tabs":				widget = new Gui.Tabs( elems[i] ); break;
			//case "tab":					widget = new Gui.Tab( elems[i] ); break;
			//case "navbar":				widget = new Gui.Navbar( elems[i] ); break;
			//case "dialog":				widget = new Gui.Dialog( elems[i] ); break;
			//case "record_editor":		widget = new Gui.RecordEditor( elems[i] ); break;
			default:					document.write( classname + " widget class unknown" );
		}

		if( widget )
			Gui.widgets[id] = widget;
		
		i -= ( is_ie ? 0 : 1 );
	}
}

//--- initialize GUI
Gui.init = function()
{
	g_cScrollBarWidth = get_scrollbar_width();

	//Gui.Button.inherits( Gui.Widget );
	//Gui.Form.inherits( Gui.Widget );
	//Gui.Edit.inherits( Gui.Widget );
	//Gui.Fieldset.inherits( Gui.Widget );
	//Gui.Group.inherits( Gui.Widget );
	//Gui.List.inherits( Gui.Widget );
	//Gui.Textarea.inherits( Gui.Widget );
	//Gui.ImageCropTool.inherits( Gui.Widget );
	Gui.AutocompleteEdit.inherits( Gui.Widget );
	//Gui.ShortcutIcon.inherits( Gui.Widget );
	//Gui.Uploader.inherits( Gui.Widget );
	//Gui.Grid.inherits( Gui.Widget );
	//Gui.Navbar.inherits( Gui.Widget );
	//Gui.Dialog.inherits( Gui.Widget );
	//Gui.RecordEditor.inherits( Gui.Widget );

	Gui.widgets = new Array();
	Gui.widgets_index_array = new Array();
	//this.init_widget_class( "button" );
	//this.init_widget_class( "edit" );
	//this.init_widget_class( "check" );
	//this.init_widget_class( "fieldset" );
	//this.init_widget_class( "group" );
	//this.init_widget_class( "list" );
	//this.init_widget_class( "textarea" );
	//this.init_widget_class( "form" );
	//this.init_widget_class( "image_crop_tool" );
	this.init_widget_class( "autocomplete_edit" );
	//this.init_widget_class( "shortcut_icon" );
	//this.init_widget_class( "uploader" );
	//this.init_widget_class( "grid" );
	//this.init_widget_class( "tabs" );
	//this.init_widget_class( "tab" );
	//this.init_widget_class( "navbar" );
	//this.init_widget_class( "dialog" );
	//this.init_widget_class( "record_editor" );
}

Gui.get	= function( id )
{
	return Gui.widgets[id];
}

function ajax_init_admin()
{
	// start the ajax status timer, used for checking server events, like locking DB records by other users
	setTimeout( "ajax_server_event_check_timer()", AJAX_SERVER_EVENT_CHECK_INTERVAL );

	// get the record locks
	ajax_get_server_db_locks();
}

// the ajax server event check timer, checks for new events for this user
function ajax_server_event_check_timer()
{
	var args = Array();

	args["user_id"] = g_user_id;

	ajax_send_message( "get_server_events", args, "", function_exists( "on_server_event_response" ) ? on_server_event_response : on_server_event_default_response );
	setTimeout( "ajax_server_event_check_timer()", AJAX_SERVER_EVENT_CHECK_INTERVAL );
}

// the default response on server events messages
function on_server_event_default_response( http_request )
{
	if( ajax_success( http_request ) )
	{
		var msg = ajax_read_message( http_request );
		var events = ajax_unserialize_db_array( msg.data );

		// let the default function process the events first
		ajax_process_server_events( events );
	}
}

// this function processes the system events
function ajax_process_server_events( events )
{
	for( var i = 0; i < events.length; i++ )
	{
		switch( events[i]["event_name"] )
		{
			case "alert":
				alert( events[i]["event_data"] );
				break;

			case "lock": 
				var lock = events[i]["event_data"].split( ";" );
				g_db_locks[lock[0]] = lock[1];

				if( function_exists( "on_ajax_update_locks" ) )
					on_ajax_update_locks();
				break;
		}
	}
}

var Template = function( html )
{
	this.html = html;
	this.vars = new Array();

	this.set_html = function( html )
	{
		this.html = html;
	}

	this.set = function( variable, value )
	{
		this.vars[variable] = value;
	}

	this.parse = function()
	{
		var result = this.html;

		for( var key in this.vars ) 
		{
			result = result.replace( new RegExp( "\{"+key+"\}", "g" ), this.vars[key] );
		}

		return result;
	}
}