// JavaScript Document

/**
*    Copyright (C) Duml.org (support@duml.org)  
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation; either
*    version 2.1 of the License, or (at your option) any later version.
*
*    This library is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public
*    License along with this library; if not, write to the Free Software
*    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

//----- BEGIN JAS STUFF
String.prototype.trim = function()
{
    return this.replace(/^\s*|\s*$/g,"");
}

function getHttpRequest()
{
	var req;

	if ( typeof XMLHttpRequest != 'undefined' ) { req = new XMLHttpRequest(); }
	else { try { req = new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) { req = new ActiveXObject('Microsoft.XMLHTTP'); };	};
	return req;
}

document.getJasThrobber =
	function()
	{
		var throbber = document.getElementById( 'jasThrobber' );

		if( throbber == null )
		{
			var throbber = 
				JasDomUtil.elFromString( '<div id="jasThrobber" style="background-color:red;color:white;'
					+ 'z-index=10000;position:absolute;top:0px; padding:2px; margin: 0px; display:none;">'
					+ 'Loading..</div' );
			document.body.appendChild( throbber );
			throbber.style.left = ( document.body.offsetWidth - throbber.offsetWidth - 40 ) + 'px';
		}
		return throbber;
	}

document.showJasThrobber = function() { var throbber = document.getJasThrobber(); throbber.style.display = 'block'; throbber.style.left = ( document.body.offsetWidth - throbber.offsetWidth - 40 ) + 'px'; }
document.hideJasThrobber = function() { var throbber = document.getJasThrobber().style.display = 'none'; }



function JasMap()
{
   this.map=new Object();
   this.putVal = function( key, val ){ this.map[key] = val; };
   this.getVal = function( key ){ return ( typeof this.map[key] != 'undefined' ) ? this.map[key] : null };
   this.removeVal = function( key ){ delete this.map[key]; };
   this.clear = function(){ this.map = new Object(); };
}

function JasPropertiesManager(){}
JasPropertiesManager.properties = new JasMap();
JasPropertiesManager.putElProps = function( elId, elProps ){ JasPropertiesManager.properties.putVal( elId, elProps ); };
JasPropertiesManager.getElProps = function( elId, createIfNone ){ var elProps = JasPropertiesManager.properties.getVal( elId ); if( ! elProps && createIfNone ){ JasPropertiesManager.putElProps( elId, new JasMap() ); }; return JasPropertiesManager.properties.getVal( elId ); };
JasPropertiesManager.putElProperty = function( elId, propName, propVal ){ var elProps = JasPropertiesManager.getElProps( elId, true ); elProps.putVal( propName, propVal ); };
JasPropertiesManager.getElProperty = function( elId, propName ){ var elProps = JasPropertiesManager.getElProps( elId, false ); if( elProps ){ return elProps.getVal( propName ); }; return null; };
new JasPropertiesManager();

function JasEventManager(){}
JasEventManager.evts = new JasMap();
JasEventManager.cNotId = '';
JasEventManager.add = function( id, handler, payload, cancelDefault ){ var o = new Object(); o.handler=handler; o.payload=payload; o.cancelDefault = ( cancelDefault == null ) ? true : cancelDefault; JasEventManager.evts.putVal( id, o ); };
JasEventManager.fire = function( evt )
{ 
    evt.targetEl = ( document.all ) ? event.srcElement : evt.target;  
    var o = null;
    if( ( ! evt.targetEl ) || ( JasEventManager.cNotId != evt.targetEl.id ) )
    {
		o = JasEventManager.evts.getVal ( 'nextunmatched' ); 
		if( o )
		{ 
			evt.payload  = ( o.payload != null ) ? o.payload : null; o.handler( evt ); 
		};
		
	}
	o = JasEventManager.evts.getVal ( evt.targetEl.id );
    if( o ) { 
    	evt.payload  = ( o.payload != null ) ? o.payload : null; o.handler( evt ); 
    	if( o.cancelDefault )
    	{ 
    		if( !document.all){evt.preventDefault()}; 
    		return false; 
    	};
    }
};
new JasEventManager();

function JasCleanupThread()
{
    setInterval( JasCleanupThread.cleanup, 20000 );
}
JasCleanupThread.cleanup = function(){ 
	for( key in JasPropertiesManager.properties.map ){ var el = document.getElementById(key);if(!el){ JasPropertiesManager.properties.removeVal( key ); JasLogger.trace( 'run props cleanup for: ' + key ); }; }; 
	for( key in JasEventManager.evts.map ){  if( key != 'nextunmatched' ){ var el = document.getElementById(key);if(!el){ JasEventManager.evts.removeVal( key ); JasLogger.trace( 'run evts cleanup for: ' + key );}; }; }; 
	};
new JasCleanupThread();

if( typeof JasLogger == 'undefined' ) 
{
	eval( 'function JasLogger(){};' );
	JasLogger.trace = function(){};
	JasLogger.debug = function(){};
	JasLogger.error = function(){};
	JasLogger.info = function(){};
}

function JasRemoteRequest(url, requestMethod, handler, args, showDefaultThrobber )
{
    if( arguments.length == 0 ){ return };
    if( ( typeof showDefaultThrobber == "undefined" ) || ( showDefaultThrobber == null ) ) { showDefaultThrobber = true; };
    var http = getHttpRequest();
    var mode = ( ( typeof requestMethod == 'undefined' ) || ( requestMethod == null ) || ( requestMethod.toUpperCase() == "POST" ))?"POST":"GET";
    var queryStringMarkerIndex = url.indexOf( "?" );
    JasLogger.debug( 'requesting(' + mode + '):' + url );
    var openUrl = (requestMethod=="GET" || queryStringMarkerIndex < 0 ) ?  url : url.substring( 0, queryStringMarkerIndex );
    http.open(mode,openUrl,true);

    if(mode=="POST"){ http.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');}

    http.onreadystatechange =   
    	function()
	{
		if( http.readyState==4 )
		{
			var status;
			try
			{
				status = http.status;
				if ( status == 200 )
				{
					JasLogger.debug( 'received response:' + http.responseText );
					document.hideJasThrobber();
					handler( http.responseText.trim(), args );
				}
				else
				{
					JasLogger.error( 'Status returned: ' + status );
				}

			}
			catch(e)
			{
				JasLogger.error( 'Problem in response: ' + e );
			}

		}
	};


    try
    {
        var postArgs = url.substring( queryStringMarkerIndex + 1 );
        if( showDefaultThrobber ) { document.showJasThrobber(); }
        http.send( postArgs );
    }
    catch( e )
    {
    	JasLogger.error( 'problem during send: ' + e );
    }

}


JasRemoteRequest.toGetStyleString = 
	function ( frm )
	{
		var queryString = '?';
		for ( var i = 0; i < frm.elements.length; i++ )
		{

			var elm = frm.elements[i];
			if (!elm.name || typeof(elm.value) == 'undefined') { }
			else
			{
				var buff = '';
				var okToInclude = true;
				if ( elm.tagName != 'SELECT' )
				{
					if( ( elm.type == 'radio' || elm.type == 'checkbox' ) && ( ! elm.checked ) ){ okToInclude = false; };
				}
				else if ( elm.multiple )
				{
					for( var x=0; x<elm.options.length; x++ )
					{
					    if( elm.options[x].selected ){ queryString += elm.name + "=" + encodeURIComponent( elm.options[x].value ) + "&"; };
					}
					okToInclude = false;
				}
				if( okToInclude )
				{
					buff += elm.value;
					queryString += elm.name + "=" + encodeURIComponent( buff ) + "&";
				}
			}
		}
	    return queryString;
	 }
new JasRemoteRequest();




//----- BEGIN DUML LOADER 
/**
 * Universal helper object for updating the DOM
 * @constructor
 */
function DumlLoader( arg1, arg2, arg3, arg4, arg5, arg6 )
{
    if( arguments.length > 0 ) //needed for initialization of static methods
    {
		this.errorDescription = null;
		var onError = arg3;
		var beforeBegin = arg4;
		var afterComplete = arg5;
		this.isJs = ( ( typeof arg6 != 'undefined' ) && ( arg6 == true ) )?true:false;
		if( arg1.tagName == 'FORM' )
		{
		   this.method = arg1.getAttribute( 'METHOD' ).toUpperCase();
		   this.url = arg1.getAttribute( 'ACTION' ) + JasRemoteRequest.toGetStyleString( arg1 );
		   onError = arg2;
		   beforeBegin = arg3;
		   afterComplete = arg4;
		}
		else
		{
			this.url = arg1;
			this.method = arg2;
		}

		if( ( typeof onError != "undefined" ) && ( onError != null ) )
		{
			this.onError = onError;
		}
		if( ( typeof beforeBegin != "undefined" ) && ( beforeBegin != null ) )
		{
			this.beforeBegin = beforeBegin;
		}
		if( ( typeof afterComplete != "undefined" ) && ( afterComplete != null ) )
		{
			this.afterComplete = afterComplete;
		}
	}
}


//Public methods
DumlLoader.prototype.execute = DumlLoader_execute;
DumlLoader.prototype.beforeBegin = DumlLoader_beforeBegin;
DumlLoader.prototype.afterComplete = DumlLoader_afterComplete;
DumlLoader.prototype.onError = DumlLoader_onError;
DumlLoader.prototype.postInterpret = DumlLoader_postInterpret;
DumlLoader.prototype.interpret = function DumlLoader_interpret( duml )
  				 {
					this.errorDescription = DumlInterpreter.apply( duml );
					if ( this.errorDescription != null ) { this.onError(); };					
				 }
				 
function DumlLoader_onError( errorDescription ){}
function DumlLoader_beforeBegin(){}
function DumlLoader_postInterpret(){}
function DumlLoader_afterComplete(){}
function DumlLoader_execute( showDefaultThrobber )
{
    this.beforeBegin();
    new JasRemoteRequest( this.url,
		this.method,
		function( xml, ref )
		{  
			if( xml.indexOf( '<duml' ) != 0 ) { document.write( xml ); document.close(); }
			else
			{
				if( ref.isJs ){ xml='<duml version="1.0"><instruction manipulation="executeScript"><!-- '+xml+' --></instruction></duml>' };
				ref.interpret(xml);
				ref.postInterpret();
				JasLogger.debug("about to run afterComplete()");		        
				ref.afterComplete();
				JasLogger.debug("afterComplete() complete");
			}
				

		}, this, showDefaultThrobber );
}
new DumlLoader();


//----- BEGIN DUML INTERPRETER 
function DumlInterpreter(){}	
DumlInterpreter.importNode = 
	function ( xml )
	{
		//Strip duml tags and massage into iterable html nodes
		xml = xml.substring( xml.indexOf('>') + 1, xml.lastIndexOf('</duml>' ) ).trim();
		var xmlInstructions = xml.split( /<instruction/ );
		xml = '<div>start</div>';
		for( var i=0; i<xmlInstructions.length; i++ )
		{
			var instruction = ( '<div  ' + xmlInstructions[i] ).trim();
			var beginCharIndex = instruction.indexOf( '>' );
			var endCharIndex = instruction.lastIndexOf( '</instruction' );
			var start = instruction.substring( 0, beginCharIndex + 1 );
			var end = instruction.substring( endCharIndex );
			var update = instruction.substring( beginCharIndex + 1, endCharIndex ).trim();
			if( update.indexOf( '<tr' ) == 0 )
			{ 
				start = start.replace( '<div ', '<div tableManipulationType="0"' );
				update = '<table><tbody>' + update + '</tbody></table>'; 
			}
			else if( update.indexOf( '<td' ) == 1 )
			{ 
				start = start.replace( '<div ', '<div tableManipulationType="1"' );
				update = '<table><tbody><tr>' + update + '</tr></tbody></table>'; 
			}
			else if( update.indexOf( '<th' ) == 2 )
			{ 
				start = start.replace( '<div ', '<div tableManipulationType="2"' );
				update = '<table><thead><tr>' + update + '</tr></thead></table>'; 
			}
			xml += start + update + '</div>';
			
		}
		var temp = document.createElement( 'div' );
		temp.innerHTML = xml;
		return temp;
	}
							
DumlInterpreter.apply = 
	function( xml )
	{
		try
		{
			var instructionNodes = null;
			var doc = DumlInterpreter.importNode( xml );
			
			JasLogger.trace( 'imported document: ' + doc.innerHTML ); 
			instructionNodes = doc.childNodes;
			var scriptBlocks = new Array();
			JasLogger.debug( 'found ' + instructionNodes.length + ' instructionNodes' ); 
			for( var i=1; i<instructionNodes.length; i++ )
			{
				JasLogger.debug( 'begin interpreting instruction node#' + i ); 
				//we need to gather info on all the object tags in this instruction node
				//because those are 'special' and need to be handled seperately
				var objTags = new Array();
				if( instructionNodes[i].getElementsByTagName )
				{
					objTags = instructionNodes[i].getElementsByTagName( 'OBJECT' );
				}
				JasLogger.trace( 'Found ' + objTags.length + ' OBJECT tags in instruction node' );
				var objParams = new Array();
				for( var p=0; p<objTags.length; p++ )
				{
					JasLogger.trace( ' --pushing objParam # ' + p )
					var params = '';
					var oldNodeParams = null;
					oldNodeParams = objTags[p].childNodes;
					for( var z=0; z<oldNodeParams.length; z++ )
					{
						params += oldNodeParams[z].outerHTML.replace( /\&amp;/g, '\&' );
					}
					objParams.push( params );
					JasLogger.trace( ' --pushed objParams: ' + objParams );
				}

				var instructionNode = instructionNodes[i];

				var updateNode = instructionNode;
				
				for( var ss = 0; ss < instructionNode.childNodes.length; ss++ )
				{
					if( instructionNode.childNodes[ ss ].nodeType == 1 || instructionNode.childNodes[ ss ].nodeType == 11 )
					{
						updateNode = instructionNode.childNodes[ ss ];
						break;
					}
				}
				JasLogger.trace( 'updateNode: ' + updateNode + ' tagName: ' + updateNode.tagName + ' - node type: ' + updateNode.nodeType + ":" + updateNode.outerHTML);

				if( updateNode.nodeType == 11 || updateNode.nodeType == 1 )
				{
					JasLogger.trace( 'NodeType:' + updateNode.nodeType );
					var scriptCode = '';
					if(  instructionNode.getAttribute( 'manipulation' ) == 'executeScript' )
					{
						JasLogger.debug( 'This is an execute script node. '  );
						scriptCode = ( document.all ) ? instructionNodes[i].innerHTML : instructionNode.firstChild.nodeValue;
						scriptCode = scriptCode.replace( /\<!--/, '' ).replace( /--\>/, '' );
						eval( scriptCode );
					}
					else if( instructionNode.getAttribute( 'manipulation' ) == 'queueScript' )
					{
						JasLogger.debug( 'This is an execute script node. '  );
						scriptCode = ( document.all ) ? instructionNodes[i].innerHTML : instructionNode.firstChild.nodeValue;
						scriptCode = scriptCode.replace( /\<!--/, '' ).replace( /--\>/, '' );
						scriptBlocks.push( scriptCode );
					}
					else
					{
						JasLogger.debug( 'This is a DOM update instruction: ' + instructionNode.outerHTML );
						var originNodeId = instructionNode.getAttribute( 'originNodeId' );
						var originNode = ( originNodeId == '~body~' ) ? document.body : document.getElementById( originNodeId );
						var continueOnNoOriginStr = instructionNode.getAttribute( 'continueOnNoOrigin' );
						JasLogger.trace( 'continueOnNoOriginStr: ' + continueOnNoOriginStr );
						var continueOnNoOrigin = ( continueOnNoOriginStr !=null && continueOnNoOriginStr.toLowerCase() == 'true' )?true:false;
						JasLogger.trace( 'continueOnNoOrigin: ' + continueOnNoOrigin );
						try
						{
							var type = instructionNode.getAttribute( 'manipulation' );
							var replaceIfExists = instructionNode.getAttribute( 'replaceIfExists' );
							if( ( replaceIfExists != null ) && replaceIfExists.toLowerCase() == 'true' )
							{
								if( document.getElementById( updateNode.getAttribute( 'id' ) ) != null )
								{
									type = 'replace';
									originNode = document.getElementById( updateNode.getAttribute( 'id' ) );
								}
							}
							
							{
							    var tIndex = 0;
							    tmt = instructionNode.getAttribute( 'tableManipulationType' );
							    var newRow = originNode;
							    if( tmt == 0 )
							    {
								updateNode = updateNode.tBodies[0].rows[0];
								if( type != 'replace' )
								{
									if( type == 'appendChild' ){ tIndex = originNode.rows.length }
									else if( type == 'insertBefore' ){ tIndex = originNode.rowIndex - 1 }
									else if( type == 'insertAfter' ){ tIndex = originNode.rowIndex }
									newRow = originNode.insertRow( tIndex );							    
									type = 'replace';								    
								}
								originNode = newRow;
							    }
							    else if( tmt == 1 || tmt == 2 )
							    {
							        if( tmt == 1 )
							        {
									updateNode = updateNode.tBodies[0].rows[0].cells[0];
								}
								else if ( tmt == 2 )
								{
									updateNode = updateNodes.tHead.cells[0];
								}
								if( type != 'replace' )
								{
									if( type == 'appendChild' ){ tIndex = originNode.cells.length }
									else if( type == 'insertBefore' ){ tIndex = originNode.cellIndex - 1 }
									else if( type == 'insertAfter' ){ tIndex = originNode.cellIndex }
									newRow = originNode.insertCell( tIndex );							    
									type = 'replace';								    
								}
								originNode = newRow;
							    }
							}
							JasLogger.trace( 'manipulation=' + type + ';' + originNode );
							var containerNode = originNode.parentNode;
							updateNode.removeAttribute( 'manipulation' );

							if ( type == 'set-attribute' )
							{
								var keyStr = instructionNode.getAttribute( 'attName' ).toLowerCase();
								var valStr = instructionNode.getAttribute( 'attValue' );
								JasLogger.debug( 'this is an attribute setter asking to set ' + keyStr + ' to ' + valStr );
								if( keyStr == 'style' )
								{
									var pairs = valStr.split( ';' );
									for( var pi=0; pi < pairs.length; pi++ )
									{
										var pair = pairs[pi].split( ':' );
										if( pair.length > 1 )
										{
											var styleStr =  pair[0].trim();

											if( styleStr.indexOf( '-' ) != -1 )
											{
												styleStr = pair[0].split( '-' );
												styleStr = styleStr[0] + styleStr[1].charAt(0).toUpperCase() + styleStr[1].substring( 1 );
											}
											eval( 'originNode.style.' + styleStr + '="' + pair[1].trim() + '"' );
										}
									}

								}
								else if( keyStr == 'class' )
								{
									originNode.className = valStr;
								}
								else
								{
									
									if( keyStr.indexOf( 'on' ) == 0 )
									{
										JasLogger.trace( 'originNode.' + keyStr + '=function(){' +  valStr + ';}' );
										eval( 'originNode.' + keyStr + '=function(){' +  valStr + ';}' );
									}
									else if( keyStr == 'action')
									{
									        JasLogger.trace( 'originNode.setAttribute( keyStr, valStr );' );
										originNode.setAttribute( keyStr, valStr );
									}
									else
									{
										JasLogger.trace( 'originNode.' + keyStr + '=' +  valStr );
										eval( 'originNode.' + keyStr + '=' +  valStr );
									}
								}
							}
							else if ( type == 'replaceContent' )
							{
								originNode.innerHTML = updateNode.parentNode.innerHTML;							
							}
							else if ( type == 'replace' )
							{
							    containerNode.replaceChild( updateNode, originNode );
							}
							else if( type == 'remove' )
							{
								containerNode.removeChild( originNode );
							}
							else if( type == 'appendChild' )
							{
								originNode.appendChild( updateNode );
							}
							else if( type == 'insertAfter' )
							{
								var insertBefore = originNode.nextSibling;
								containerNode.insertBefore( updateNode, insertBefore );
							}
							else if( type == 'insertBefore' )
							{
								containerNode.insertBefore( updateNode, originNode );
							}
							else
							{
								JasLogger.error(  'Unknown manupulation attribute in duml: ' + type );
								return 'Unknown manupulation attribute in duml: ' + type;
							}
							var objectTags = instructionNode.getElementsByTagName( 'object' );
							for( var q=0; q<objectTags.length; q++ )
							{
								JasLogger.trace( 'copying objParams #' + q );
								var objHTML = objectTags[q].outerHTML;
								var start = objHTML.substring( 0, objHTML.indexOf( '>' ) + 1 );
								objHTML = start + objParams[q] + '</OBJECT>';
								var placeholder = document.createElement( 'div' );
								placeholder.innerHTML = objHTML;
								objectTags[q].parentNode.replaceChild( placeholder.firstChild, objectTags[q] );
							}
						}
						catch( noOriginException )
						{
							if( ( ! continueOnNoOrigin ) && ( originNode == null ) )
							{
								var errStr = 'Origin not found and no continueOnNoOrigin attribute set to true in duml for id: ' + originNodeId;
								JasLogger.error( errStr );
								return errStr;
							}
						}
					}
				}
			}
			for( var c=0; c< scriptBlocks.length; c++ )
			{
				JasLogger.trace( 'evaling: ' + scriptBlocks[c] );
				eval( scriptBlocks[c] );
			}
			return null;

		}
		catch(e)
		{
			JasLogger.error( 'Document update failed, check syntax: ' + e.message );
			return e.message;
		}

	}
new DumlInterpreter();


IamLogger = JasLogger;
