/*
   AJAX/JSON communication with Ruby On Rails app

   Version:    1.0.3
   Created by: M@ McCray
               http://www.mattmccray.com
   History:
		1.0.0 - Initial Release
		1.0.1 - Updated asynchronous support to work with Safari and Firefox
		        it may work with IE -- but it's untested
		1.0.2 - Removed trailing '&' from params sent to server. Added more
		        documentation. Added an example of Async usage.
		        Tested and working in IE 6.
		1.0.3 - Updated for Routes support. Passing a controller and action
		        no longer makes sense. Now, you just send the URI in the
		        constructor.

   Usage:
   <script src="path/to/connectors.js"></script>
   <script>

		// We only need one connector instance for each rails action
		// we want to call.
		var myConnector;
		var myAsyncConnector;
		
		function OnWindowLoad()
		{
			// Create a connector to a specific Controller/Action that will return
			// JSON results
			myConnector = new JsonConnector('/my_controller/my_action');
			
			// For an Async connector, pass a function handle as the last param...
			myAsyncConnector = new JsonConnector('/my_controller/my_other_action', callback);
			
			// Using Rails, the right way to do this is to use url_for:
			myRailsConnector = new RailsConnector('<%= url_for :controller=>'my_controller', :action=>'my_action' %>')
			
			
			// By the way, a RailsConnector will return whatever the server returns...
			// It's good for use when the server results are not something you want to 
			// parse into JSON... Like using the server to transform Textile into HTML.
			
			// A JsonConnector works the same as a RailsConnector (in fact, it just
			// wraps it), except that it will parse the results before returning them.
			// The results returned from the class, or sent to the callback, will be
			// native JavaScript objects that have been parsed from JSON.
		}

		function UseConnector()
		{
			// Add params to call
			myConnector.set( 'id', '10' )

			// Get results from server
			var res = myConnector.execute();

			// Do whatever with the server response
			alert( res );
		}	
		
		function UseAsyncConnector()
		{
			// Add params to call
			myAsyncConnector.set( 'id', '15' )

			// Execute call to server... Since this connector is asynchronous,
			// it won't return any results, it will call the function passed
			// to the constructor with the server results once they are parsed.
			// (in this case, it will call the function 'callback')
			myAsyncConnector.execute();
		}	
		
		// This method is called after the results have been returned from
		// the server and parsed as JSON (JavaScript Object Notation).
		function callback(data)
		{
			//	The param 'data' is the parsed results from the server
			alert( data );
		}
		
   </script>


   Example of returning JSON from a RAILs action using JSON for Ruby.

   require 'json/lexer'
   require 'json/objects'

   class JsonController < ApplicationController
   	
   	layout nil
   	
   	def index
   		return_data = Hash.new

   		return_data["Stuff_for_client"] = "Stuff"

   		# You can access the parameters sent from the javascript connector using @params
   		return_data["Sent_Value"] = @params["id"]

   		# JSON for Ruby adds a method to objects making it really simple to
   		# create a json string... simplly call Object#to_json()
   		return_data.to_json
   	end

   end	


   Reference:
		- Ruby on Rails	http://www.rubyonrails.org
		- JSON for Ruby	http://sourceforge.net/projects/json
		- JSON				http://www.crockford.com/JSON/index.html

*/

// A connector for send and retrieving data from Rails
function RailsConnector(controller_uri, callback)
{	
	this.add = addParam;
	this.set = addParam;
	this.remove = removeParam;
	this.get = getParam;
	this.getParamList = getParamList;
	this.clear = clearParams;
	this.send = send;
	this.execute = send;

	this.__callback = callback || false;
	this.__params = [];
	this.__xmlHttp = new XMLHttpRequest();	
	this.__url = controller_uri;
	this.__async = (typeof this.__callback != 'boolean') ? true : false;


	// Add a parameter to send to the server... On the server side, this
	// will act just like a form that's been POSTed.
	//
	// NOTE: Currently, a param isn't removed from the connector when it's
	//       executed. To remove params, you will need to either call 
	//       the .remove(key) or .clear() method.
	function addParam(key, value)
	{
		this.__params[key] = value;
	}
	
	// Remove a previously set parameter
	function removeParam(key)
	{
		var u;
		this.__params[key] = u;
		delete this.__params[key];
	}
	
	// Retrieve a parameter that was previously set
	function getParam(key)
	{
		return this.__params[key];
	}
	
	// Clears out all of the parameters 
	function clearParams()
	{
		this.__params = []
	}

	// Used internally
	function getParamList()
	{
		var params = '';
		
		// Create paramlist in a POST format (e.g. key=value&key2=value2)
		for( key in this.__params )
		{
			if( typeof this.__params[key] != 'undefined' )
			{
				params += escape(key) +'='+ escape(this.__params[key]) +'&';
			}
		}
		
		// Remove the trailing '&'
		params = params.substring( 0,  params.lastIndexOf('&') );
		
		return params;
	}
	
	function send()
	{
		if( this.__async )
		{
			// This looks odd, but it has to do with scoping... trust me
			var callback = this.__callback;
			var xmlHttp = this.__xmlHttp;
			this.__xmlHttp.onload = function() { 
				callback( xmlHttp.responseText ); 
			};
		}

		// Initialize connection to server...
		this.__xmlHttp.open("POST", this.__url, this.__async);
		
		// Send it the data
		this.__xmlHttp.send( this.getParamList() );
		
		if( !this.__async )
			return this.__xmlHttp.responseText;
	}

}

// A wrapper of the Rails connector that will parse the results as JSON into JavaScript objects
function JsonConnector(controller_uri, callback)
{
	var __callback = callback || false;
	var uid = createUID();
	this.uid = uid;

	if( typeof __callback != 'boolean' )
	{
		this._doCallback =  function(data) {
			try
			{
				eval( 'window.'+ uid +' = '+ data );
			}
			catch(ex)
			{
				var msg = ex.description || ex.message || ex;
				alert("JsonConnector Parse Exception:\n\n"+ msg);
				
				window[uid] = null;
			}
			__callback( window[uid] ); 
		}
	}
	else
	{
		this._doCallback = false;
	}	
		
	// Wrapped RailsConnector
	this.__conn = new RailsConnector(controller_uri, this._doCallback)
	
	this.add = function(key, value){ this.__conn.add(key, value); }
	this.set = function(key, value){ this.__conn.add(key, value); }
	this.remove = function (key) { this.__conn.remove(key); }
	this.get = function (key) { return this.__conn.get(key); }
	this.getParamList = function () { return this.__conn.getParamList(); }
	this.clear = function () { this.__conn.clear(); }
	this.send = send;
	this.execute = send;
		
	function send()
	{
		var results = this.__conn.send();
		if( !this.__conn.__async )
		{
			try
			{
				eval( 'window.'+ this.uid +' = '+ results );
			}
			catch(ex)
			{
				var msg = ex.description || ex.message || ex;
				alert("JsonConnector Parse Exception:\n\n"+ msg);
				
				window[uid] = null;
			}
			return window[this.uid];
		}
	}
}


function createUID()
{
	window.__UID__SEED++;
	return '_uid_'+ window.__UID__SEED;
}
window.__UID__SEED = 100;

if (window.ActiveXObject && !window.XMLHttpRequest) {
  window.XMLHttpRequest = function() {
    return new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
  };
}