Call (AJAX-style asynchronous web requests)

The Call module provides a simple and generic mechanism for AJAX style calling of asychronous web data.

Call

Call( url, parameters, handler )

If the 'url' passed does not include a protocol and hostname, the 'Resolve()' function is called to retrieve an API host. When the response is received, the 'responseText' will be passed to 'handler'. An example call is shown below:

Call( "https://chart.googleapis.com/chart", ["cht":"qr", "chs":"300x300", "chl=MyQRCode"], my_response_handler );
function Call( endpoint, parameters, custom_handler )
{
	parameters['wab_requesting_url'] = location.protocol + "//" + location.host + location.pathname;

	if ( DataStorage.Local.HasItem ( "sessionid" ) && !Call.UseXSessionIDHeader )
	{
		parameters['sid'] = DataStorage.Local.GetItem( "sessionid" );
	}

	if ( document.body.hasAttribute( "data-csrf" ) && !Call.UseXCSRFTokenHeader )
	{
		var csrf = document.body.getAttribute( "data-csrf" );

		if ( "NONE" != csrf ) parameters['wab_csrf_token'] = csrf;
	}

	var search = endpoint.indexOf( "?" );
	if ( -1 !== search )
	{
		var override_parameters = GetSearchValues.CreateDictionary( endpoint.substring( search ) );
		for ( const index in override_parameters )
		{
			parameters[index] = override_parameters[index];
		}
	}

	var command = Call.EncodeToString( parameters );
	var handler = (custom_handler) ? custom_handler : Call.DoNothing;

	if ( "http" != endpoint.substr( 0, 4 ) )
	{
		switch ( endpoint.substr( 0, 3 ) )
		{
		case "/ap":
		case "/au":
			endpoint = Resolve( "api" ) + endpoint;
			break;

		case "/fn":
			endpoint = Resolve( "fn"  ) + endpoint;
			break;
		}
	}

	if ( 'async' in parameters && ("true" === parameters['async']) )
	{
		var httpRequest = Call.Post( endpoint, command, null, 0, 0 );
			httpRequest.send( command );

			handler( null, event );
	}
	else
	{
		var target = event ? event.target : null;

		//var wrapper
		//=
		//function ( responseText )
		//{
		//	handler( responseText, target );
		//}

		var httpRequest = Call.Post( endpoint, command, handler, 0, 0 );

		if ( DataStorage.Local.HasItem( "sessionid" ) && Call.UseXSessionIDHeader )
		{
			httpRequest.setRequestHeader( "X-Session-ID", DataStorage.Local.GetItem( "sessionid" ) );
		}

		if ( document.body.hasAttribute( "data-csrf" ) && Call.UseXCSRFTokenHeader )
		{
			var csrf = document.body.getAttribute( "data-csrf" );

			if ( "NONE" != csrf )
			{
				httpRequest.setRequestHeader( "X-CSRF-Token", csrf );
			}
		}

		httpRequest.send( command );
	}
}

Helper functions

Call.Post
=
function ( endpoint, command, handler, timeout, timeouts )
{
	return Call.CreateXMLHttpRequest( "POST", endpoint, command, handler, timeout, timeouts );
}

Call.Get
=
function ( endpoint, command, handler, timeout, timeouts )
{
	return Call.CreateXMLHttpRequest( "GET", endpoint, command, handler, timeout, timeouts );
}

Call.CreateXMLHttpRequest
=
function( method, endpoint, command, handler, timeout, timeouts )
{
	var httpRequest = new XMLHttpRequest();
		httpRequest.open( method, endpoint, true );
		httpRequest.timeout           = timeout;
		httpRequest.timeouts          = timeouts;
		httpRequest.myEndpoint        = endpoint;
		httpRequest.myCommand         = command;
		httpRequest.myResponseHandler = handler;

		httpRequest.withCredentials = false;

		httpRequest.onreadystatechange
		=
		function()
		{
			Call.OnReadyStateChange( httpRequest, endpoint, handler );
		}

		httpRequest.ontimeout
		=
		function()
		{
			alert( "Giving up! Connection to the API server has timed out. Try again later." );
		}

		httpRequest.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );

		return httpRequest;
}

Call.OnReadyStateChange
=
function( self, endpoint, handler )
{
	var status = self.status;

	switch ( self.readyState )
	{
	case 0:
	case 1:
	case 2:
	case 3:
		break;
		
	case 4:
		switch ( self.status )
		{
		case 200:
			console.log( "Called: " + endpoint );
			if ( handler ) handler( self.responseText );
			break;

		case 404:
			console.log( "Invalid API endpoint: " + endpoint );
			break;

		case 501:
			console.log( "Required SQL Stored Procedure Not Implemented" );
			break;

		case 503:
			console.log( "Sorry, the API server is currently unavailable. Please try again later. (503)" );
			break;

		case 0:
			console.log( "The network timed out... (0)" );
			break;
			
		default:
			console.log( "Got status: " + status );
		}
		self.onreadystatechange = null;
		break;
		
	default:
		console.log( "Unexpected httpRequest ready state: " + self.readyState );
		self.onreadystatechange = null;
	}
}

Call.EncodeToString
=
function( parameters )
{
	var string = "";
	var sep    = "";

	for ( const member in parameters )
	{
		if ( "" != member )
		{
			string += sep;
			string += encodeURIComponent( member );
			string += "=";
			string += encodeURIComponent( parameters[member] );

			sep = "&";
		}
	}
	return string;
}

Call.DoNothing
=
function ()
{}