/*
The job is to decorate a syntax block.
The behavior works in two modes: decorate the current element, or decorate all PREs marked with the AUTOHILITE expando.

The advantage to the latter mode is that only a single instance of the behavior need be attached to process the entire document.
This works well when the document uses consistent markup for sample code. Attaching one behavior per element wastes resources unnecessarily.

The behavior exposes TOPICNAME and PERSISTENTNAME properties so that the author doesn't have to specify these in the HILITE expando of every
element to be decorated. It's up to the user to set these expandos prior to attaching the behavior.

Future enhancements:
1) In batch mode, rather than processing PREs only, expose a property ELEMENTSTODECORATE that specifies a
	semi-colon delimited list of tags that should be inspected for the AUTOHILITE attribute
2) Expose distinct properties for the different marker/text colors
*/

var g_dbg = false;				// Set to true for debug mode
var g_err;						// When in debug mode, write info to error log

var m_oRng = null; 				// TextRange representing the current element
var m_sBM = null; 				// TextRange bookmark
var m_bAutoHilite = false; 		// indicates that we should decorate the current element only
var m_sMarkerColor = "green";	// color for comment delimeters
var m_sTextColor = "green"; 	// color for text within comments
var m_bScriptPage = false;		// indicates if the page is a script page


var COMMAND	 = 'CMD';
var ICENODE	 = 'ICE';
var OBJECT	 = 'OBJ';
var METHOD	 = 'MTH';
var EVENT	 = 'EVT';
var PROPERTY = 'PTY';
var CELLBODY_CSS_CLASS = 'cCellBody';
var CELLBODY_BGCOLOR = '#ffffc0';		// Background color for non-inherited members


/*
	============================
		ONLOAD EVENT HANDLER
	============================
*/
window.onload = function () 
{
	// Set up error log if in debug mode
	if ( g_dbg ) { 
		var l_fso = new ActiveXObject("Scripting.FileSystemObject");
		g_err = l_fso.CreateTextFile("errors.log");
	}

	do_tables();

	var sKeywords = "";
	var arrKeywords = new Array();
	
	// Determine the type of page and get the command/object/method/property name.
	var sFlag = '';
	var sObjectName = '';
	var sMemberName = '';
	var sCommandName = '';
	var sICENodeName = '';

	var collAnchors = document.anchors;
	for (var i=0; i<collAnchors.length; i++ ) {
		// Does it have a type attribute (use it to determine
		// type of reference entry)
		if ( collAnchors[i].type ) {
			sFlag = collAnchors[i].type;
			var sInnerText = collAnchors[i].name;
			
			switch ( sFlag ) {
				case OBJECT :
					sObjectName = sInnerText;
					break;
				case COMMAND :
					sCommandName = sInnerText;
					break;
				case ICENODE :
					sICENodeName = sInnerText;
					break;
				case METHOD :
				case PROPERTY :
					/(\w+) /.test(sInnerText);
					sMemberName = RegExp.$1;
					break;
			}
			break;
		}
	}

	// Change the background color of non-inherited properties and methods.
	// And build a list of keywords for highlighting in code samples on the Object page.
	if ( sFlag == OBJECT ) {
		var collLinks = document.links;
	
		// Trap cases where there's a space at the end (it may have been introduced 
		// by the HTML::Element processing in ReplaceDoxyLinks.pl)
		var respace = new RegExp(" $");
		if ( sObjectName.match(respace) ) {
			sObjectName = sObjectName.replace(respace, "");
		}

		for (var i=0; i<collLinks.length; i++ ) {
			var oLink = collLinks[i];
			var sHref = oLink.href;
			var s = sHref.indexOf(sObjectName + '_');
			if ( s > 0 ) {
				if ( g_dbg ) { g_err.WriteLine("\n"+sHref); }
				if ( oLink.parentNode.className == CELLBODY_CSS_CLASS ) {
					oLink.parentNode.parentNode.style.background=CELLBODY_BGCOLOR;
					var t = s + sObjectName.length + 1;
					var u = sHref.indexOf( '.htm' );
					if ( g_dbg ) { g_err.WriteLine( "\t"+sHref.substr(t, u-t) ); }
					arrKeywords.push(sHref.substr( t, u-t ));
				}
			}
		}

	}

	// If the method or property can be used by scripted operators and particle events,
	// replace the asterisk in the main heading with an icon that has a fancy tooltip
	if ( sFlag == METHOD ||  sFlag == PROPERTY ) {
		insertImage( sFlag );
	}
	
	switch ( sFlag ) {
		case OBJECT:
			sKeywords = arrKeywords.join(';');
			break;
		case METHOD:
		case PROPERTY:
			sKeywords = sMemberName;
			break;
		
		case COMMAND:
			sKeywords = sCommandName;

		case ICENODE:
			sKeywords = sICENodeName;
	}
	
	if ( g_dbg ) { g_err.WriteLine("\tsending these keywords to handle_docready: "+sKeywords); }
	handle_docready( sKeywords );
}
// end of OnLoad Event


// Try to create the global pop-up window
g_bMozilla = false;
try {
	g_oPopup = window.createPopup();
} catch (e) {
	g_bMozilla = true;
}




/*
	============================
		  HELPER FUNCTIONS
	============================
*/
function do_tables()
{
	var cloTables = document.getElementsByTagName('TABLE');
	
	for ( var i=0; i<cloTables.length; i++ ) {
		// Color table headings light blue
		if ( cloTables.item(i).className == 'TableColumn' || cloTables.item(i).className == 'TablePage' || cloTables.item(i).className == 'Parameters' ) {
			var cloRows = cloTables(i).rows;
			for ( var j=0; j<cloRows.length; j++ ) {
				var cloCells = cloRows(j).cells;
				if ( cloCells.length > 0 && cloCells(0).children(0).className == "chCellHeading" ) {
					for (var k=0; k<cloCells.length; k++ ) {
						cloCells.item(k).style.background = "#B7C7EF";
					}
				}
			}
		} 
		
		// Resize all parameter table cells to equal widths
		if ( cloTables.item(i).className == 'Parameters' ) {
			var cloRows = cloTables.item(i).rows;
			for ( var j=0; j<cloRows.length; j++ ) {
				if ( cloRows.item(j).cells.length ) {
					cloRows.item(j).cells(0).width = 210;
				}
			}
		}
	}
}



// ----------------------------------
function hideTooltip()
{
	g_oPopup.hide();
}



// ----------------------------------
function showTooltip()
{
	g_oPopup.document.body.style.fontFamily="Verdana,Arial";
	g_oPopup.document.body.style.fontSize="11px";

	var sOnClick = " onclick='parent.location.href=this.href'";
	var sTitle = " title='スクリプトオペレータまたはパーティクルイベントから呼び出せるメソッドおよびプロパティ'";
	var s = "<div style='background:#ffffd0;padding:6px;border:1px solid black;height:55;'>";
	s += "<p>このメンバは、スクリプトオペレータまたはパーティクルイベントで使用できます。";
	s += "スクリプトオペレータまたはパーティクルイベントでは、オブジェクトモデルの<a href='";
	s += "ScriptedOperatorLimitations.htm'" + sOnClick + sTitle ;
	s += ">サブセット</a>のみを使用できます。使用できるメソッドおよびプロパティには記号（";
	s += "<img src='op.gif' style='position:relative;bottom:-2px'>）またはアスタリスク（*）が表示されます。";
	s += "</p>";
	s += "</div>";

	g_oPopup.document.body.innerHTML = s;

	// This code is copied from
	// http://msdn.microsoft.com/library/default.asp?url=/workshop/author/om/popup_overview.asp
	//
	var popupBody = g_oPopup.document.body;
	// The following popup object is used only to detect what height the 
	// displayed popup object should be using the scrollHeight property. 
	// This is important because the size of the popup object varies 
	// depending on the length of the definition text. This first 
	// popup object is not seen by the user.
	g_oPopup.show(0, 0, 300, 0);
	var realHeight = popupBody.scrollHeight;
	// Hides the dimension detector popup object.
	g_oPopup.hide();
	// Shows the actual popup object with correct height.
	g_oPopup.show(15, 15, 300, realHeight, event.srcElement);
	//
	// End copied code
}



// ----------------------------------
function insertImage( sFlag )
{
	switch ( sFlag ) {
		case METHOD:
			var sMemberType = "method";
			break;
		case PROPERTY:	// Property
			var sMemberType = "property";
			break;
	}
	
	var collLinks = document.links;
	for ( var i=0; i<collLinks.length; i++ ) {
		if ( collLinks(i).href.match(/ScriptedOperatorLimitations/i) ) {
			var sTmp =  "<img src='op.gif' border='0' onmouseover='showTooltip();'>";
			collLinks(i).innerHTML = sTmp;
			break;
		}
	}
}



// ----------------------------------
function handle_docready( keywords )
{
	if ( g_dbg ) { g_err.WriteLine("\tnow in handle_docready....."); }
	m_oRng = window.document.body.createTextRange();
	if ( !m_oRng ) {
		if ( g_dbg ) { g_err.WriteLine("\t\tcouldn't create text range"); }
		return;
	}
	var colMeta=window.document.getElementsByTagName("META");
	if ( colMeta ) {
		for ( var iMETAs=0; iMETAs<colMeta.length; iMETAs++ ) {
			if ( colMeta[iMETAs].getAttribute("content")=="'scr'" && colMeta[iMETAs].getAttribute("name")=="devlang" ) {
				if ( g_dbg ) { g_err.WriteLine("\t\tcurrent page is a script page"); }
				m_bScriptPage=true;
			}
		}
	}
	
	if (m_bAutoHilite) {
		// we're attached directly to the element we've been asked to hilite
		if ( g_dbg ) { g_err.WriteLine("\t\tattached directly to the element to hilite"); }
		DecorateElement(element);
	} else {
		// walk the entire topic, and decorate special block elements
		if ( g_dbg ) { g_err.WriteLine("\t\tmust walk the entire topic......."); }
		var oPREs = window.document.getElementsByTagName("PRE");
		var iPREs = oPREs.length;
		if ( iPREs == 0 ) {
			if ( g_dbg ) { g_err.WriteLine("\t\t\tno script code in this page"); }
			return;
		}

		for ( var iPRE=0; iPRE<iPREs; iPRE++ ) {
			var oPRE = oPREs[iPRE];
			var sClassName = oPRE.className;
			var re = /Python|JScript|VBScript|Cplusplus|CSharp|^cwC$|PerlScript|coCommand|cwCommandWide/i;
			var arrTmp = sClassName.match(re);
			var sLang = ( arrTmp == null ) ? "" : arrTmp[0];

			if ( g_dbg ) { g_err.WriteLine("\t\t\tnow calling DecorateElement with keywords + language: "+keywords+", "+sLang); }
			DecorateElement(oPRE, keywords, sLang );
		}
	}
}



// ----------------------------------
function DecorateElement(oElem, keywords, sLang )
{
	if ( g_dbg ) { g_err.WriteLine("\t\t\tnow calling DecorateElement with keywords + language: "+keywords+", "+sLang); }
	m_oRng.moveToElementText(oElem);
	m_sBM = m_oRng.getBookmark(); // save our place in case we have to move the range
	if ( g_dbg ) { g_err.WriteLine("\t\t\tthrowing range down to HiliteComments"); }
	if ( sLang.match(/Command/) ) {
		// NB: At some point in the future, we could use js/c++ style comments 
		// as generic for coCommand and cwCommandWide tags, but for now we'll ignore them
		HiliteComments( m_oRng, "" );
	} else {
		HiliteComments( m_oRng, sLang );
	}
	
	if( sLang.length > 0 ) {
		if ( g_dbg ) { g_err.WriteLine("\t\t\tthrowing range down to HiliteTokens with these keywords: "+keywords); }
		HiliteTokens(oElem, m_oRng, keywords );
	}
	
    if ( oElem.previousSibling && oElem.previousSibling.ShowHideType ) {
		toggle(oElem.previousSibling);
    }
}



// ----------------------------------
// restore our place
function RestoreRange()
{
	m_oRng.moveToBookmark(m_sBM);
}



// ----------------------------------
// Dispatch calls to the various routines that hilite different code comment types
function HiliteComments(oRng, sLang )
{
	if ( sLang.length == 0 ) return;
	if ( g_dbg ) { g_err.WriteLine("\t\t\tnow down in HiliteComments with language "+sLang); }
	switch ( sLang.toLowerCase() ) {
		case "csharp":
		case "cplusplus":
		case "cwc":
		case "jscript" :
			HiliteCStyleComments(oRng);
			HiliteSingleLineComment(oRng, "//");
			break;
		case "vbscript" :
			HiliteSingleLineComment(oRng, "'");
			break;
		case "python" :
		case "perlscript" :
			HiliteSingleLineComment(oRng, "#");
			break;
	}
	
	if (m_bScriptPage) {
		HiliteHTMLComments(oRng);
	}
}



// ----------------------------------
// Hilites comments of the following form
//      // C++, C# or JScript comment
//      '  VB(S) comment
//      # Perl(Script) or Python comment
function HiliteSingleLineComment(oRng, sToken)
{
	if (typeof(sToken) != "string") {
		return;
	}

	var oRngStart = oRng.duplicate();
	var oRngEnd;
	
	while ( oRngStart.findText(sToken, 1000000) && oRng.inRange(oRngStart) ) {
		// handle exceptions
		if (sToken == "//") {
			var sBM = oRngStart.getBookmark();
			oRngStart.move("character", -1);
			oRngStart.moveEnd("character", 1);
			var sPrecedingChar = oRngStart.text;
			oRngStart.moveToBookmark(sBM);
			if (sPrecedingChar == ":") { 		// the token is likely part of an http (or some other protocol) link
			   oRngStart.collapse(false);
			   continue;
			} else if (sPrecedingChar == "-") {	// part of a DOCTYPE decl?
				oRngStart.move("character", -2);
				oRngStart.moveEnd("character", 1);
				var sQuote = oRngStart.text;
				if (/(["'])/.test(sQuote)) {	// <=ignore this, it's for syntax highlighting"
					oRngStart.collapse(false);
					if (oRngStart.findText(RegExp.$1)) {		// look for matching quote at the end of the DOCTYPE
						oRngStart.collapse(false);
						continue;
					} else {
						// bad news
						return;
					}
				} else {
					oRngStart.moveToBookmark(sBM); // reset and proceed
				}
			}
		}

		oRngStart.execCommand("ForeColor", false, m_sMarkerColor);
		oRngStart.collapse(false);
		oRngEnd = oRngStart.duplicate();
		if ( oRngEnd.findText("\r", 1000000) ) {
			oRngStart.setEndPoint("EndToStart", oRngEnd);
		} else {
			// comment is the last line in the block
			oRngStart.setEndPoint("EndToEnd", oRng);
		}
		oRngStart.execCommand("ForeColor", false, m_sTextColor);
	}

}



// ----------------------------------
// Hilite C-style comments in the specified range
function HiliteCStyleComments(oRng)
{
	var oRngStart = oRng.duplicate();
	var oRngEnd;

	while ( oRngStart.findText("/*", 1000000) && oRng.inRange(oRngStart) ) {
		oRngStart.execCommand("ForeColor", false, m_sMarkerColor);
		oRngStart.collapse(false);
		oRngEnd = oRngStart.duplicate();
		if (!oRngEnd.findText("*/", 1000000)) {
			break; // bad news; couldn't find closing comment. Bail.
		}
		oRngEnd.execCommand("ForeColor", false, m_sMarkerColor);
		oRngStart.setEndPoint("EndToStart", oRngEnd);
		oRngStart.execCommand("ForeColor", false, m_sTextColor);
	}
}



// ----------------------------------
// Hilite HTML-style comments in the specified range
function HiliteHTMLComments(oRng)
{
	var oRngStart = oRng.duplicate();
	var oRngEnd;
	while ( oRngStart.findText("<!--") && oRng.inRange(oRngStart) ) {
		// Check for a conditional IE comment
		oRngStart.setEndPoint( "EndToEnd", oRng );
		if ( /<!--\s*\[\s*if/.test( oRngStart.text ) ) {
			oRngEnd = oRngStart.duplicate();
			if ( oRngEnd.findText( ">" ) ) {	// Skip conditional comment
				oRngStart.setEndPoint( "StartToEnd", oRngEnd );
			}
			continue;
		} else {	
			// Reset text range
			oRngStart.findText("<!--");
		}

		oRngStart.moveEnd("character", -2); // back up to exclude the --
		oRngStart.execCommand("ForeColor", false, m_sMarkerColor);
		oRngStart.collapse(false);
		oRngEnd = oRngStart.duplicate();
		if (!oRngEnd.findText("-->")) {
			break; // bad news; couldn't find closing comment. Bail.
		}
		oRngEnd.moveStart("character", 2); // roll forward to exclude the --
		oRngEnd.execCommand("ForeColor", false, m_sMarkerColor);
		oRngStart.setEndPoint("EndToStart", oRngEnd);
		oRngStart.execCommand("ForeColor", false, m_sTextColor); // hilite the comment text
	}
}



// ----------------------------------
// Apply hiliting to the keywords identified in the semi-colon delimited string associated with the HILITE attribute.
function HiliteTokens(oElement, oRng, keywords )
{
	if ( g_dbg ) { g_err.WriteLine("\t\t\tarrived in HiliteTokens with these keywords: "+keywords); }
	var oRegEmptyStr = new RegExp("^\\s*$");

	// process any author-defined tokens
	var sTokens = keywords;
	if (!sTokens) {
		if ( g_dbg ) { g_err.WriteLine("\t\t\tnothing to hilite in HiliteTokens"); }
		return; // nothing to hilite
	}

	var aTokens = sTokens.split(";"); // array of tokens to search for
	var ahTokens = new Array(); // hash of tokens we've already searched for to prevent accidental toggling

	var oRngTemp = oRng.duplicate();
	if ( g_dbg ) { g_err.WriteLine("\t\t\treading over tokens.........."); }
	for ( var iToken=0; iToken<aTokens.length; iToken++ ) {
		var sToken = aTokens[iToken];
		// just make sure there are no trailing spaces
		sToken = sToken.replace(/\s$/g,"");
		if ( g_dbg ) { g_err.WriteLine("\t\t\t\tcurrent token: "+sToken); }
		// watch out for dupe tokens
		if (oRegEmptyStr.test(sToken) || ahTokens[sToken]) {
			if ( g_dbg ) { g_err.WriteLine("\t\t\t\tskipping to next token (duplicate)"); }
			continue;
		}

		ahTokens[sToken] = 1;

		if ( g_dbg ) { 
			g_err.WriteLine("\t\t\t\treading through range"); 
			if ( oRngTemp.findText(sToken, 1000000, 6) ) {
				g_err.WriteLine("\t\t\t\ttoken '"+sToken+"' exists in snippet"); 
			} else {
				g_err.WriteLine("\t\t\t\ttoken '"+sToken+"' does NOT exist in snippet"); 
			}
			if ( oRng.inRange(oRngTemp) ) {
				g_err.WriteLine("\t\t\t\ttmp range is inside the snippet"); 
			} else {
				g_err.WriteLine("\t\t\t\ttmp range is NOT inside the snippet"); 
			}
		}
		while ( oRngTemp.findText(sToken, 1000000, 6) && oRng.inRange(oRngTemp) ) {
			if ( g_dbg ) { g_err.WriteLine("\t\t\t\t\tforecolor = "+oRngTemp.queryCommandValue("ForeColor")); }
			// Hilight the token if it is not in a comment ("Green" = 32768)
			if ( oRngTemp.queryCommandValue("ForeColor") != 32768 )		
			{
				oRngTemp.execCommand("BackColor", false, "Yellow");
				oRngTemp.execCommand("ForeColor", false, "Black");
			}
			oRngTemp.collapse(false);
		}


		oRngTemp.moveToBookmark(m_sBM); // reset the temporary range for the next iteration

	}
}



/*
	==========================================
		DUAL LINK SUPPORT HELPER FUNCTIONS
	==========================================
*/
// Handler function for onClick event on mixed-type hyperlinks
function DisplayChoices( in_arr, in_X, in_Y )
{
	// Decide which function to use based on the browser
	if (g_bMozilla) {
		showHTMLPop( in_arr, in_X, in_Y );
	} else {
		showPop( in_arr );
	}
}

// ----------------------------------
// Display choices in HTML mini-window on Mozilla browsers
function showHTMLPop( in_arr, in_X, in_Y )
{
	var oPopUpList = window.open( "", "_popup", "width=300,height=100,resizable,screenX="+in_X+",screenY="+in_Y );
	var oBlankSlate = oPopUpList.document;
	oBlankSlate.writeln( "<html><head>" );
	oBlankSlate.writeln( "<title>Redirect</title>" );
	oBlankSlate.writeln( "<script type=\"text/javascript\">" + completeJump.toString() + "</script>" );
	oBlankSlate.writeln( "</head><body><div style=\"font-family:Arial;font-size:smaller\">" );
	while ( sCurrHref = in_arr.shift() ) {
		var sDisplay = in_arr.shift();
		oBlankSlate.writeln( "<a href=\"#\" onClick=\"completeJump('" + sCurrHref + "');\">" + sDisplay + "</a><br/>" );
	}
	oBlankSlate.writeln( "</div></body></html>" );
}

// ----------------------------------
// For use in pop-up window only
function completeJump( in_loc )
{
	window.opener.location.href = in_loc;
	window.close();
}

// ----------------------------------
// Display choices in IE DOM pop-up window
function showPop( in_arr )
{
	g_oPopup.document.body.style.fontFamily="Verdana,Arial";
	g_oPopup.document.body.style.fontSize="11px";

	var sOnClick = " onclick='parent.location.href=this.href'";
	var sTitle = " title='Redirect'";
	var sBlankSlate = "<div style='background:#ffffd0;padding:6px;border:1px solid black;height:55;'><p>";
	while ( sCurrHref = in_arr.shift() ) {
		var sDisplay = in_arr.shift();
		sBlankSlate += "<a href='" + sCurrHref + "'" + sOnClick + sTitle + ">" + sDisplay + "</a><br>";
	}
	sBlankSlate += "</p></div>";

	g_oPopup.document.body.innerHTML = sBlankSlate;

	// This code is copied from
	// msdn.microsoft.com/library/default.asp?url=/workshop/author/om/popup_overview.asp
	var popupBody = g_oPopup.document.body;
	g_oPopup.show(0, 0, 300, 0);
	var realHeight = popupBody.scrollHeight;
	g_oPopup.hide();
	g_oPopup.show(15, 15, 300, realHeight, event.srcElement);
}

// ----------------------------------
// For use in IE DOM pop-up window only
function hidePop()
{
	g_oPopup.hide();
}
