/* **********************************************************************
	Job Shop Control System
	Client Side routines

Copyright 2007 (c) FlowTech Solutions, Inc.		(www.FlowTech-Solutions.com)
************************************************************************ */
/** -----------------------------------------------------------------------------

	FILE:		WebService.js
	PURPOSE:	Javascript facility to retrieve data from the server

	Copyright 2007,2008,2009 (c) FlowTech Solutions, Inc.		(www.FlowTech-Solutions.com)

	Permission is hereby granted, free of charge, to any person obtaining
	a copy of this software and associated documentation files (the
	"Software"), to deal in the Software without restriction, including
	without limitation the rights to use, copy, modify, merge, publish,
	distribute, sublicense, and/or sell copies of the Software, and to
	permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


	PUBLIC METHODS					DESCRIPTION
	-----------------------------	---------------------------------------------------
	Initialize()								Initializes the control by collecting the security token
	ExecuteTransaction(options)	Initiates an Ajax request to execute an information retreival transaction

	DEPENDENCIES
	------------
	prototype 1.6.0.3
	common.js (for clone)
	XMLDocument.js

	JSON REPSONSE
	datatype=Boolean,Byte,Char,DateTime,Decimal,Double,Int16,Int32,Int64,SByte,Single,String,TimeSpan,UInt16,UInt32,UInt64
	---------------------------------------------------------------------------------------------------------------
	{
	columns:[
		{name:'MaterialType_ID',datatype:'Int32',maxlength:-1,ordinal:0,allowdbnull:true},
		...
		],
	rows:[
		{MaterialType_ID:'123',MaterialType:'_',MaterialTypeDescription:'',Raw_SW:'1',Finished_SW:'0',Steel_SW:'',GalvanizedSteel_SW:'',StainlessSteel_SW:'',MildSteel_SW:'',Aluminum_SW:'',Zinc_SW:'',Brass_SW:'',Copper_SW:''},
		...
		]
	}

	JSN REPSONSE
	datatype=Boolean,Byte,Char,DateTime,Decimal,Double,Int16,Int32,Int64,SByte,Single,String,TimeSpan,UInt16,UInt32,UInt64
	---------------------------------------------------------------------------------------------------------------
	{
	columns:[
		{name:'MaterialType_ID',datatype:'Int32',maxlength:-1,ordinal:0,allowdbnull:true},
		...
		],
	rows:[
		{'123','_','','1',0','description','','','','','','',''},
		...
		]
	}

----------------------------------------------------------------------------- */
var WebService = function()
{
	// Singleton class
	if (window.WebService) return window.WebService;



	/* -------------------------------------- P U B L I C ---------------------------------------------- */
	var _public =
	{
		// Initializes the WebService object, executed automatically when the DOM is constructed
		Initialize:	function()
		{
			if ((!_private._options.token  || _private._options.token =='') && $("TOKEN") && $("TOKEN").innerHTML) {
				_private._options.token = $("TOKEN").innerHTML;
				}
			if (!_private._options.token  || _private._options.token =='') {
				_private._options.token  = (typeof(Token) != 'undefined' && Token != '') ? Token : (typeof(token) != 'undefined' && token != '') ? token : null;
				}
		},

		// Return False for any bad parameter else returns a TransactionIdentifier
		ExecuteTransaction: function(TransactionID, options)
		{
			if ( !TransactionID || typeof(TransactionID) != 'string' || TransactionID.trim() == '' ) return(false);
			options.transactionId = TransactionID;
			return(_private.ExecuteTransaction(options));
		},

		// public method for url encoding
		encode : function (string)
		{
			return escape(_public.utf8_encode(string));
		},

		// public method for url decoding
		decode : function (string)
		{
			return _public.utf8_decode(unescape(string));
		},

		// UTF-8 encoding
		//	Ref: http://en.wikipedia.org/wiki/UTF-8
		utf8_encode : function (string)
		{
			string = string.replace(/\r\n/g,"\n");
			var utftext = "";

			for (var n = 0; n < string.length; n++)
			{
				var	c = string.charCodeAt(n);

				if (c < 128)
				{
					utftext += String.fromCharCode(c);
				}
				else if((c > 127) && (c < 2048))
				{
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else
				{
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}
			}

			return utftext;
		},

		// UTF-8 decoding
		utf8_decode : function (utftext)
		{
			var	string = "", i = 0, c = c1 = c2 = 0;

			while ( i < utftext.length )
			{
				c = utftext.charCodeAt(i);
				if (c < 128)
				{
					string += String.fromCharCode(c);
					i++;
				}
				else if((c > 191) && (c < 224))
				{
					c2 = utftext.charCodeAt(i+1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else
				{
					c2 = utftext.charCodeAt(i+1);
					c3 = utftext.charCodeAt(i+2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}
			}

			return string;
		},


		// Encodes string to character set A-Z, a-z, 0-9, +, /, or =
		//	Ref: http://en.wikipedia.org/wiki/Base64
		encode64: function(input)
		{
			input = escape(input);
			var	output = "", chr1, chr2, chr3 = "", enc1, enc2, enc3, enc4 = "", i = 0;

			do
			{
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2))
				{
				   enc3 = enc4 = 64;
				}
				else if (isNaN(chr3))
				{
				   enc4 = 64;
				}

				output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
				chr1 = chr2 = chr3 = "";
				enc1 = enc2 = enc3 = enc4 = "";
			} while (i < input.length);

			return output;
		},

		// Decode previously encoded base64 string
		decode64: function(input)
		{
			var	output = "", chr1, chr2, chr3 = "", enc1, enc2, enc3, enc4 = "", i = 0;

			// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
			var base64test = /[^A-Za-z0-9\+\/\=]/g;
			if (base64test.exec(input)) {
			alert("There were invalid base64 characters in the input text.\n" +
				  "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
				  "Expect errors in decoding.");
			}
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			do
			{
				enc1 = keyStr.indexOf(input.charAt(i++));
				enc2 = keyStr.indexOf(input.charAt(i++));
				enc3 = keyStr.indexOf(input.charAt(i++));
				enc4 = keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64)
				{
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64)
				{
					output = output + String.fromCharCode(chr3);
				}

				chr1 = chr2 = chr3 = "";
				enc1 = enc2 = enc3 = enc4 = "";

			} while (i < input.length);

			return unescape(output);
		},

		// Encode special characters to HTML
		xmlEncode: function(unencodedXml)
		{
			return( unencodedXml.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;") );
		},

		// Decode XML special characters
		xmlDecode: function(encodedXml)
		{
			return( encodedXml.replace(/&amp\;/g, "&").replace(/&quot\;/g, '"').replace(/&apos\;/g, "'").replace(/&lt\;/g, "<").replace(/&gt\;/g, ">") );
		}

	};


	/* -------------------------------------- P R I V A T E ---------------------------------------------- */
	var _private =
	{
		_options :	{
						parameters:		null,									// Object of parameters to pass to the server
						log:					"1",
						format:				"JSON",								// must be JSON, XML, HTML, or DATA
						project:			window.Project||"SMF",	// default to SMF project
						section:			"COMMON",						// default to COMMON module/meta block/Section
						requestUrl:		"ServerRequest.aspx",		// server side URL to request transaction execution
						token:				null,									// validation token must be provided by the page
						onSuccess:		null,									// function to call when server response successful
						onError:			null,									// function to call when an exception occurred or bad transaction
						forceBusy:		false,
						busyMessage:	"Retrieving information..."
						},


		// ExecuteTransaction - initiates execution of the web service transaction
		ExecuteTransaction: function (options)
		{
			var	req,
					qsArgs,
					params,
					optns = clone(_private._options);

			optns = Object.extend(optns, options||{} );
			if (!optns || !optns.token || optns.token=='') return(false);
			if (typeof(Busy)!='undefined' && (Busy.display || optns.forceBusy)) Busy.showBusy(optns.busyMessage||"Retrieving information ...");

			params = ( typeof(optns.parameters) == 'string' ||  typeof(optns.parameters) == 'number' )? optns.parameters+'' :  (optns.parameters == null)? '' : Object.toQueryString(optns.parameters);
			params = params.split('&').join( (optns.argumentDelimiter ? optns.argumentDelimiter : '|') );
			//params = encodeURIComponent(params);

			if ( (optns.project||'') == '' || (optns.section||'') ==''  || (optns.transactionId||'') =='')
			{
				alert("ERROR: WebService.ExecuteTransaction, missing project, section, and/or field.", optns.toQueryString());
				if ( typeof(Busy)!='undefined' ) Busy.removeBusy();
				return(false);
			}

			//http://localhost/WT/ServerRequest.asp?tkn=AG384JW372NDHR&fmt=JSON&prjct=smf&sctn=COMMON&fld=_MATERIALPURCHASEHISTORY&arg=value|value2|...
			qsArgs = 'tkn='+(optns.token||'') +'&log='+(optns.log||'1') +'&fmt='+(optns.format||'DATA') + '&prjct='+(optns.project||'') + '&sctn='+(optns.section||'') +
				'&fld='+(optns.transactionId||'') + '&arg='+params +
				(optns.argumentDelimiter ? '&del=' + optns.argumentDelimiter : '');

			// Establish the Ajax method as  post or get
			optns.method = (optns.method||'get').toLowerCase();

			//alert('WebService.ExecuteTransaction=>'+optns.requestURL+'?'+qsArgs);
			if ('get' == optns.method)
			{	// GET
				req = new Ajax.Request(optns.requestUrl,
					{	method: optns.method,
						parameters: qsArgs,
						requestHeaders: ['TKN',optns.token, 'LOG',optns.log, 'FMT',optns.format, 'PRJCT',optns.project, 'SCTN',optns.section, 'FLD',optns.transactionId, 'Connection','close'],
						onSuccess: function(transport){_private.onSuccess(transport,optns);},
						onFailure: function(transport){_private.onFailure(transport,optns);},
						onException: _private.onException
						}  );
			}
			else
			{	// POST	 ( required for .NET 3.5 Web Service )
				req = new Ajax.Request(optns.requestUrl,
					{	method: optns.method,
						postBody: qsArgs,
						requestHeaders: ['TKN',optns.token, 'FMT',optns.format, 'PRJCT',optns.project, 'SCTN',optns.section, 'FLD',optns.transactionId],
						onSuccess: function(transport){_private.onSuccess(transport,optns);},
						onFailure: function(transport){_private.onFailure(transport,optns);},
						onException: _private.onException,
						onComplete: function(){if ( typeof(Busy)!='undefined' ) Busy.removeBusy();}
						}  );
			}
			return(false);
			//return(req);
		},


		// Capture the transaction results
		onSuccess: function(transport, optns)
		{
			if ( typeof(Busy)!='undefined' )	Busy.removeBusy();
			var result = '';

			var ResponseType = transport.getHeader('Content-type');
			ResponseType = (!ResponseType || ResponseType=='') ? 'application/data' : ResponseType;
			ResponseType = ResponseType.toLowerCase();
			if (ResponseType.indexOf('application/xml') >= 0)
				result = (Prototype.Browser.IE) ? loadXMLString(transport.responseText) : transport.responseXML;
			else if (ResponseType.indexOf('application/json') >= 0)
				result = transport.responseJSON;
			else
				result = transport.responseText;

			//alert("WebService:onSuccess  Ajax Success =>"+Object.inspect(result));
			if (typeof(optns.onSuccess) == 'function')
			{
				try
				{
					optns.onSuccess(transport,optns,result);
				}
				catch (err)
				{
					alert("WebService:onSuccess  eval function exception =>"+Object.inspect(err));
				}
			}
			$(document).fire("WebService:AjaxSuccess", {result:result, transport:transport, options:optns});
			transport = null;
			optns = null;
			result = null;
			return(false);
		},


		// Handle any transaction request failure (generally triggered by comm failures, backend webservice unavailable)
		onFailure: function (transport, optns)
		{
			if ( typeof(Busy)!='undefined' ) Busy.removeBusy();
			if (typeof(optns.onError) == 'function')
			{
				try
				{
					optns.onError(transport,optns);
				}
				catch (err)
				{
					alert("WebService:onFailure  eval function exception =>"+Object.inspect(err));
				}
			}
			else
				alert("WebService:onFailure  Ajax failure =>"+Object.inspect(transport.responseText));
			$(document).fire("WebService:AjaxFailure", transport, optns);
			transport = null;
			optns = null;
			return(false);
		},


		// Handle any tranaction exceptions (generally triggered by invalid/malformed JSON results)
		onException: function (ajaxRequest, exceptionObj)
		{
			if ( typeof(Busy)!='undefined' ) Busy.removeBusy();
			alert('WebService:onException:  '+Object.inspect(exceptionObj));
			$(document).fire("WebService:AjaxException", ajaxRequest, exceptionObj);
			ajaxRequest = null;
			exceptionObj = null;
			return(false);
		},


		// Retrieves an XML element value
		getXmlElementValue : function(argXmlDoc, argEleName)
		{
			var ele = argXmlDoc.getElementsByTagName(argEleName)[0];
			return ((ele && ele.firstChild) ? ele.firstChild.data : null);
		}
	};


	return _public;

}();

/// When the DOM is constructed, make sure the formMgr initialization is executed.
Event.observe(document, "dom:loaded", WebService.Initialize);


