/*
 * This code is property of Inmedius Inc.
 * Unauthorized redistribution, re-engineering or modificaiton is prohibited.
 * This software is distributed on an "AS IS" BASIS WITHOUT ANY WARRANTIES.
 * Copyright (c) 2006,2007 Inmedius Inc.
 */

isNetscape = false;
isExplorer = false;
isOpera = false;
isFirefox = false;
doctypeRe = new RegExp('<!DOCTYPE\\s+[^\\s]+\\s+[^\\s]+\\s+[\'\"]([^\'\"]+)[\'\"]\\s+[\'\"]([^\'\"]+)[\'\"][^>]*>',"m");
systemIdRe = new RegExp('^(.*[/\\\\])[^/\\\\]+$',"m");

function detectBrowser()
{
	var agent = navigator.userAgent.toLowerCase();
	if( agent.indexOf('opera') >= 0 ) isOpera = true;
	else if( agent.indexOf('firefox') >= 0  ) isFirefox = true;
	else if( agent.indexOf('msie') >= 0 ) isExplorer = true;
	else isNetscape = true;
}

function DitaStorm()
{
	detectBrowser();
	this.fileToLoad = null;
    this.savedHeight = null;
    this.savedWidth = null;
    this.cssStyleFile = null;
    this.templateStyleFile = null;
    this.toolbarConfigFile = 'ToolbarConfig.js';
    this.modelJsFile = null;
    this.attachedForm = null;
    this.attachedField = null;    
    this.serviceFrame = null;
    this.fileOpenHandler = null;
    this.fileSaveHandler = null;
    this.doctypePath = null;
    this.currentDoctypePath = null;
    this.extensions = new Array();
    this.outputFormatted = true;
    this.localAgentLoaded = false;
    this.enableConrefProcessing = false;
    this.ditaStormHome = null;
}

/** Enables PHP file open/save operations */
DitaStorm.prototype.enablePhpFileOperations = function(dsPath)
{
	this.serviceFrame = dsPath+'/php/DITAStormFF.html';
    this.fileOpenHandler = DitaStorm_phpFileOpen;
    this.fileSaveHandler = DitaStorm_phpFileSave;
}

/** Enables local resource access */
DitaStorm.prototype.enableLocalResourceAccess = function()
{
    this.fileOpenHandler = DitaStorm_localAgentFileOpen;
    this.fileSaveHandler = DitaStorm_localAgentFileSave;
}

/** Set location of toolbar configuration file */
DitaStorm.prototype.setToolbarConfigFile = function(fileName)
{
	this.toolbarConfigFile = fileName;
}

/** 
 * Enables generation of DOCTYPE header for specfied location of the
 * definition files.
 */ 
DitaStorm.prototype.enableDoctype = function(path)
{
    this.doctypePath = path;
}

/** Registers specified extension */
DitaStorm.prototype.registerExtension = function(extensionName,argument)
{
	this.extensions.push(extensionName,argument);
}

/** Returns extension by its name */
DitaStorm.prototype.getExtension = function(extensionName)
{
	for(var i=0; i<this.extensions.length; i+=2)
	{
		if( this.extensions[i] == extensionName )		
			return this.extensions[i+1];
	}
	return null;	
}

/** Loads XML file into the editor */
DitaStorm.prototype.loadXmlFile = function(argPath)
{
    this.fileToLoad = argPath;
    ditaStormLoad();
}

/** Loads XML text into the editor */
DitaStorm.prototype.setXml= function(argXml)
{
	this.loadXml( argXml );
}

/** Loads XML text into the editor */
DitaStorm.prototype.loadXml= function(argXml)
{
	var ds = window.frames.ditaStormFrame;
	ds.loadXmlText( argXml );
	ds.setDirty(false);
}

/** Returns XML from editor */
DitaStorm.prototype.getXml = function()
{
	var xml = window.frames.ditaStormFrame.retrieveXml( this.outputFormatted );
	return xml;
}

/** Returns formatted XML from editor */
DitaStorm.prototype.getFormattedXml = function()
{
	var xml = window.frames.ditaStormFrame.retrieveXml(true);
	return xml;
}

/** 
 * Returns true if contents of the editor has been modified since last
 * load file or load xml operation.
 */
DitaStorm.prototype.isModified = function()
{
	return window.frames.ditaStormFrame.dirty;
}

/** Sets 'modified' flag to a specified true/false value */
DitaStorm.prototype.setModified = function(dirty)
{
	window.frames.ditaStormFrame.setDirty(dirty);
}

/** 
 * Enables special treatment of 'conref' attribute.
 * 1) When generating XML contents of element containing 'conref' attribute will be stripped.
 *  
 */
DitaStorm.prototype.setConrefProcessing = function(enable)
{
	this.enableConrefProcessing = enable;
}

/** Updates attached field */
DitaStorm.prototype.updateField = function()
{
	var txt = this.getXml();
	document.forms[this.attachedForm].elements[this.attachedField].value = txt;
}

/** Enables or disables formatting for XML output */
DitaStorm.prototype.setOutputFormatted = function(formatted)
{
	this.outputFormatted = formatted;
}

/** 
 * Pastes specified chunk of XML content at the current position of the cursor
 */
DitaStorm.prototype.pasteXml = function(xmlText)
{
	window.frames.ditaStormFrame.pasteText(xmlText);
}

/** Attaches editor to the field */
DitaStorm.prototype.attachField = function(argForm,argField)
{
	this.attachedForm = argForm;
	this.attachedField = argField;

    this.fileToLoad = 'field:'+argForm+','+argField;
    ditaStormLoad();
}

/** Saves CSS and XML file names for further rendering */
DitaStorm.prototype.setup = function(argEditorPath,argCssFileName,argXslFileName,modelJsFileName)
{
	this.ditaStormHome = argEditorPath;
    this.cssStyleFile = argCssFileName;
    this.templateStyleFile = argXslFileName;
    this.modelJsFile = modelJsFileName;
	var path = this.getAbsoluteUrl(window.location.href,argEditorPath);
    this.path = path;
}

/** Loads XML file into the editor */
DitaStorm.prototype.render = function(argPath,argWidth,argHeight,argCssStyle,argTemplateStyle,argModelJs)
{
	this.ditaStormHome = argPath;
	
	if( !argModelJs ) argModelJs = 'model.js';	
	
	var txt = this.getImage();
	txt = txt.replace(/{width}/g,argWidth);
    txt = txt.replace(/{height}/g,argHeight);
    txt = txt.replace(/{cssStyle}/g,argCssStyle);
    txt = txt.replace(/{templateStyle}/g,argTemplateStyle);
    txt = txt.replace(/{modelJs}/g,argModelJs);
    txt = txt.replace(/{path}/g,argPath);
    document.write( txt );
}

/** 
 * Enables editor to register onbeforeunload event and whe page gets
 * unloaded display confirmation 
 */
DitaStorm.prototype.enableUnloadConfirmation = function()
{
	window.onbeforeunload = DitaStorm_confirmUnload;
}

ditaStorm = new DitaStorm();

function ditaStormLoad()
{
	if(	window.frames.ditaStormFrame.loadXmlFile )
    {
		window.frames.ditaStormFrame.loadXmlFile(ditaStorm.fileToLoad);
    }
    else
    {
        setTimeout('ditaStormLoad()',100);
    }
}


function DitaStorm_maximize()
{
    var t = document.getElementById('ditaStormContainer');

    var di = getWindowGeometry();
	if( isFirefox )
	{
	    ditaStorm.savedHeight = t.style.height;
		ditaStorm.savedWidth = t.style.width;

		t.style.position = 'absolute';
		t.style.height = di.height - 10;
		t.style.width = di.width - 10;
		t.style.top = '0px';
		t.style.left = '0px';
	}
	else
	{
		var width = document.documentElement.offsetWidth;
		
        t.style.position = 'absolute';
        t.style.top = '0px';
        t.style.left = '0px';
        t.style.width = (width-23)+'px';
        t.style.height = '100%';
        t.style.zIndex = 1;

        ditaStorm.savedHeight = t.height;
		ditaStorm.savedWidth = t.width;
	}

    document.getElementById('ditaMaximizeButton').style.display = 'none';
    document.getElementById('ditaMinimizeButton').style.display = '';
    
	DitaStorm_postGeometryChange();    
}

function DitaStorm_minimize()
{
    var t = document.getElementById('ditaStormContainer');

    t.style.position = '';

    t.style.top = '';
    t.style.left = '';

	if( isFirefox )
	{
	    t.style.height = ditaStorm.savedHeight;
		t.style.width = ditaStorm.savedWidth;
	}
	else
	{
        //t.height = ditaStorm.savedHeight;
        //t.width = ditaStorm.savedWidth;

        t.style.height = ditaStorm.savedHeight;
		t.style.width = ditaStorm.savedWidth;
    }

    document.getElementById('ditaMaximizeButton').style.display = '';
    document.getElementById('ditaMinimizeButton').style.display = 'none';

	DitaStorm_postGeometryChange();    
}

/****************************************************************/

var remote_uri = null;

/**
 * Executes remote request
 * When done returns executed request or calls
 * asynch response handler passing the request
 */
function remote_executeRequest(argRequestUri,argAsynchResponseHandler)
{
	remote_uri = argRequestUri;

    var request = remote_createXMLHttpRequest();
    request.open("GET",argRequestUri, argAsynchResponseHandler != null );
    if( argAsynchResponseHandler != null )
    {
		request.onreadystatechange = function() {
			if( request.readyState == 4 )
			{
			    window.status = 'Done';
			    argAsynchResponseHandler(request);
			}
		}
	}
    try
    {
        request.send("");
        if( argAsynchResponseHandler )
        {
            return null;
        }
        else
        {
            return request;
        }
    }
    catch(e)
    {
        throw 'Unable request '+argRequestUri+'\nCause: '+e;
    }
}

/** Creates and returns XMLHTTPREQUEST */
function remote_createXMLHttpRequest()
{
    var request = null;
    if(window.XMLHttpRequest)
    {
        try
        {
			request = new XMLHttpRequest();
        }
        catch(e)
        {
            request = null;
        }
    }
    else if(window.ActiveXObject)
    {
        try
        {
            request = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e)
        {
        	try
            {
                request = new ActiveXObject("Microsoft.XMLHTTP");
        	}
            catch(e)
            {
                request = null;
        	}
		}
    }

    if( request == null )
    {
        throw 'Unable to create XML Http Request';
    }
    return request;
}

/** Returns XML response (ignore|strip) */
function remote_getResponseXML(request,dt)
{
	if( request.status != 200 && request.status != 0 )
	{
		alert('Unable to load XML file ['+remote_uri+'] - '+request.status+': '
				+request.statusText+'\n\n'+request.responseText);
		return null;
	}

	var result = null;
	var systemId = null;
	if( window.XMLHttpRequest )
	{
		if( !dt )
		{
			if( request.responseXML && request.responseXML.doctype )
			{
				systemId = request.responseXML.doctype.systemId;
			}
			processSystemId(systemId);
		}
		result = request.responseXML;
	}
	else
	{
		// Remove DOCTY
		var txt =
            !dt ? processDoctype( request.responseText ) :
            dt == 'ignore' ? request.responseText
            : processDoctype( request.responseText,true);

        var xml = new ActiveXObject("Microsoft.XMLDOM");
		xml.async = "false";
		xml.loadXML( txt );
		result = xml;
	}

	return result;
}

/**
 * Returns either XML DOM if one provided or requests
 * content from specified URI
 */
function remote_getDom(argObject)
{
    if( typeof( argObject ) == 'string' )
    {
        var request = remote_executeRequest(argObject,null);
        var xml = remote_getResponseXML(request);
        return xml;
    }
    else
    {
        return argObject;
    }
}

/**
 * Converts text to DOM.
 * By default shows error to the user (unless specified to ignore)
 * @param ignoreErrors ignore errors indicator
 */
function remote_textToDom(argText,ignoreErrors,dontRegisterDoctype)
{	
	argText = processDoctype(argText,dontRegisterDoctype);		

	// Do special processing for included-domains here
	// TODO Fix this and implement normal DTD processing	
	argText = argText.replace(/&included-domains;/gi,'included-domains');

	if( isFirefox )
	{
        var objDOMParser = new DOMParser();
        var dom = objDOMParser.parseFromString(argText, "text/xml");
        if( dom.documentElement.nodeName == 'parsererror' ) 
        {
        	if( !ignoreErrors )
        	{
	        	alert('ERROR parsing XML\n\n'
	        			+dom.documentElement.firstChild.nodeValue);
        	}
        	return null;
        }
        else
        {
        	return dom;
        }
	}        
    else
    {
        var xml = new ActiveXObject("Microsoft.XMLDOM");
		xml.async = "false";
		xml.loadXML( argText );
		if( xml.parseError.errorCode )
		{
			if( ! ignoreErrors )
			{
				alert( 'ERROR parsing XML\n\n'
					+ xml.parseError.reason +'\n'
					+ 'Line: ' + xml.parseError.line 
					+ ' Pos: '+ xml.parseError.linepos + '\n\n'
					+ 'XML: ' + xml.parseError.srcText );
			}
			return null;
		}
		else
		{		
        	return xml;
		}
    }
}

function getWindowGeometry()
{
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } 
  
  // Now here is one strange behavior
  // following code does NOT work if page contains IFRAME
  
  else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;    
  }
   else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  return { width:myWidth, height:myHeight };
}

/** Implement URL translation here */
DitaStorm.prototype.getAbsoluteUrl = function(base,rel)
{	
    if(/[^\/]+:[\/\\]/.test(rel))
    {
        // rel is absolute url
        return rel;
    }
    
    // If there are query parameters - remove them
    // "/abra/kadabra?lkasdjf" -> "/abra/kadabra"
    var idx = base.indexOf('?');    
    if( idx >= 0 ) {
    	base = base.substring(0,idx);
    }
        
    var m=/([^\/]+:\/\/[^\/]*)(.*)$/.exec(base);
    if(m.length<2) 
    {
    	alert('Invalid Base URL: '+base);
        return null;
    }
    
    // We probably have path there in m[2]
    // If last element of URI is non-file treat it as a directory
    // "/abc/def.html" -> "/abc/def.html"
    // "/abc/def" -> "/abc/def/" 
    if( m.length > 2 ) 
    {
    	var uri = m[2];
    	if( ! /\.[^\.]*$/.test(uri) ) 
    	{
    		m[2] = uri + '/';    		
    	}
    }    
    
   
    var site=m[1];
    var path;
    if(m.length>2 && rel.indexOf("/")!=0) 
    {
        m=/(.*\/)[^\/]*$/.exec(m[2]);
        path=m[1];
    }
    else
    {
        path="/";
    }
        
    while(/^\.\.\//.test(rel)) 
    {
        if(path=="/")
	    {
	    	alert('Invalid Base URL: '+base);
	        return null;
	    }
            
        path=/(.*\/)[^\/]+\//.exec(path)[1];
        rel=/\.\.\/(.*)$/.exec(rel)[1];
    }
    
    var url=site+path+rel;
    url=url.replace(/\/\.\//g,"/");
    
    return url;
}

/** Handles file open operation assuming service frame is loaded properly */
function DitaStorm_phpFileOpen()
{
	window.service.openFile();
}


/** Handles file save operation assuming service frame is loaded properly */
function DitaStorm_phpFileSave()
{
	window.service.saveFile();
}

/** 
 * Remove DOCTYPE from the textual representation of XML.
 * Also parses and regsiters systemId.
 */
function processDoctype(xmlText,dontRegisterDoctype)
{
	var systemId = null;
	if( xmlText.indexOf('DOCTYPE') > 0 )
	{
		var match = doctypeRe.exec(xmlText);
		if( match )
		{
			systemId = match[2];
			xmlText = xmlText.replace(doctypeRe,'');
		}
	}
	if( !dontRegisterDoctype )
	{
		processSystemId(systemId);
	}
	return xmlText;
} 

/** processes and registers full system ID */
function processSystemId(fullSystemId)
{
	var systemPath = null;
	if( fullSystemId )
	{
		var match = systemIdRe.exec(fullSystemId);
		systemPath = match
				? match[1].substring(0,match[1].length-1)
				: '.';
	}

	ditaStorm.currentDoctypePath = systemPath;
}

/** Internal. Windows onbeforeunload handler. Verifies isModified flag */
function DitaStorm_confirmUnload()
{
	if( ditaStorm.isModified() )
	{
		return 'DITA editor content has been modified.\nDo you want to discard the changes?';
	}
}

/**
 * Specific methods to handle results of the editing area geometry change
 */
function DitaStorm_postGeometryChange()
{
	// Prevent firefox from adding manual resizing controls for table
	// editing. This should have been done only once during intialization
	// but firefox forgets about it for some reason, so we have to do
	// it again
	if( isFirefox )
	{
		// Another Firefox oddity. Direct call to blockFirefoxManualObjectResizing()
		// does not work. It should be done after rending from a different thread
		// window.frames.ditaStormFrame.blockFirefoxManualObjectResizing();
		setTimeout('window.frames.ditaStormFrame.blockFirefoxObjectResizing()',1);
	}
}

/** Load local agent (if necesary) and open file using 'file open' dialog */
function DitaStorm_localAgentFileOpen()
{	
	if( !this.localAgentLoaded )	
		ditaStorm.loadLocalAgent();
	
	var content = document.DSApplet.openFile("");
	ditaStorm.loadXml(content);
	
}

/** Load local agent (if necesary) and save file using 'file save' dialog */
function DitaStorm_localAgentFileSave()
{
	if( !this.localAgentLoaded )	
		ditaStorm.loadLocalAgent();
	
	var content = ditaStorm.getXml();
	document.DSApplet.saveFile("",content);
}

/** Load local agent */
DitaStorm.prototype.loadLocalAgent = function()
{
	var href = window.frames.ditaStormFrame.document.location.href;
	var idx = href.lastIndexOf('/');
	var base = idx >= 0 ? href.substring(0,idx) : 'DITAStorm';

	document.getElementById('localAgentSpan').innerHTML =
		"<applet name='DSApplet' archive='"+base+"/lib/DitaStormFF.jar' code='com.ditastorm.DitaStormFF.class' width='1' height='1'></applet>"
}

DitaStorm.prototype.getImage = function() { return '<p>    <SCRIPT> ditaStorm.setup(\'{path}\',\'{cssStyle}\',\'{templateStyle}\',\'{modelJs}\'); </SCRIPT>    <TABLE id=\'ditaStormContainer\' width=\'{width}\' height=\'{height}\' cellspacing=\'1\' cellpadding=\'0\' class=\'ditaStormContainer\' border=\'0\'>    <TR id=\'ditaControlsRow\'>        <TD valign=\'top\' width=\'99%\'>        	<div style=\'margin:0px;padding:3px 0px 2px 75px;font-size:3px\'>&nbsp;        		        	<A id=\'dsFileTab\' href=\'#\' class=\'dsTab\' title=\'File Operations\'>File</A>	        	<A id=\'dsElementTab\' href=\'#\' class=\'dsTab\' title=\'Element\'>Element</A>	        	<A id=\'dsInsertTab\' href=\'#\' class=\'dsTab\' title=\'Insert Elements\'>Insert</A>	        	<A id=\'dsTableTab\' href=\'#\' class=\'dsTab\' title=\'Table Operations\'>Table</A>	        	<A id=\'dsModeTab\' href=\'#\' class=\'dsTab\' title=\'Editing Mode\'>View</A>				</div>        	<div id=\'dsTabPanel\' style=\'border:1px solid #B0B0B0;background-color:#E0E0E0;padding:5px;font-size:10px\'>	        	<span id=\'dsFileToolbar\' style=\'display\'></span>	        	<span id=\'dsElementToolbar\' style=\'display:none\'></span>				<span id=\'dsInsertToolbar\' style=\'display:none\'></span>				<span id=\'dsTableToolbar\' style=\'display:none\'></span>				<span id=\'dsModeToolbar\' style=\'display:none\'></span>			</div>        </TD>        <TD valign=\'top\' align=\'right\' width=\'10\'>            <a id=\'ditaMaximizeButton\' title=\'Maximize\' href=\'#\' onclick=\'DitaStorm_maximize();return false;\' style=\'font-family:arial;font-size:9px;color:#002050;text-decoration:none;padding:0px;margin:0px\'><img src=\'{path}/images/tMaximize.gif\' border=\'0\' hspace=\'1\' vspace=\'1\'></a>            <a id=\'ditaMinimizeButton\' title=\'Normalize\' href=\'#\' onclick=\'DitaStorm_minimize();return false;\' style=\'font-family:arial;font-size:9px;color:#002050;text-decoration:none;padding:0px;margin:0px;display:none\'><img src=\'{path}/images/tNormalize.gif\' border=\'0\' hspace=\'1\' vspace=\'1\'></a>            <DIV id="popupMenuDiv" class=\'ditaMenu\'>.</DIV>			<SPAN id=\'localAgentSpan\'></SPAN>			'+(ditaStorm.serviceFrame ? '<IFRAME id=\'service\' name=\'service\' src=\''+ditaStorm.serviceFrame+'\' width=\'1\' height=\'1\' frameborder=\'0\'></IFRAME>' : '')+'			<input id=\'dsPopupMenuFocusField\' style=\'width:1px;height:1px;border:0px;background:#E0E0E0\' type=\'text\'/> 		</TD>    </TR>        <TR id=\'ds_editTr\'>    	<TD id=\'dsEditPanel\' colspan=\'2\' style=\'padding:2px;font-size:12px\'>			<span id=\'dsEditToolbar\'></span>			 			<span style=\'font-size:11px;font-family:Arial;padding:1px 0px 0px 0px;margin:0px;color:#707070\'>Quick Insert:</span>			<INPUT id=\'dsQuickInsertInput\' type=\'text\' class=\'ds_qInsert\' value=\'Ctrl+Space\' onFocus=\'window.frames.ditaStormFrame.handleQuickInsertFocus()\'>		</TD>    </TR>    <TR id=\'ds_pathTr\'>    	<TD colspan=\'2\' style=\'padding:0px\'>			<div id=\'ditaPath\' style=\'font-family:arial;font-size:11px;padding:1px 3px 0px 3px;background: #F5F5F5;border: 1px solid #707070\'>&nbsp;</div>		</TD>    </TR>    <TR height=\'99%\'>       <TD colspan=\'2\' style=\'border:1px solid #707070; background: white\' height=\'100%\'>            <IFRAME id=\'ditaStormBody\' name=\'ditaStormFrame\' src=\'{path}/body.html\' width=\'100%\' height=\'100%\' FRAMEBORDER=0></IFRAME>			<TEXTAREA id=\'bodySource\' class=\'sourceArea\' style=\'display:none\' wrap=\'off\'> </TEXTAREA>        </TD>    </tr>    </TABLE>'; }
