// Copyright 2009: Thomson Reuters Global Resources. All Rights Reserved. Proprietary and Confidential information of TRGR. Disclosure, Use or Reproduction without the written authorization of TRGR is prohibited.
/*************************************************************
* leaderCharacters_v503.js
* 2004.10.22
*
* enhanced: 2005.01.31
*
* This file will add support for leaders in text.
* Leaders should be added to the html as such
* <span name="leader">.</span>
* The '.' can be changed to any desired leader character
* These leaders should only be added in a valid containg element
* such as <p> <td> <th> <div> etc... NOT directly in the <body> element
*
* Note: the leaders will be filled up to 511 characters (2^9-1). If there is an extreme case that requires
* more leader characters, then the 'charfillspower' can be changed, which will sacrifice the performance of
* every document containing leader characters.
*
*
*************************************************************/
var debugLeaderChars = false;


var useTimeout = false;
if (window.attachEvent)
{
	useTimeout = true;
	// This will register the event handlers for IE and Opera
	window.attachEvent("onresize", startFillingLeaderChars);
	window.attachEvent("onload", startFillingLeaderChars);
}
else if (window.addEventListener)
{
	// Using a timeout with Gecko flickers real bad!
	useTimeout = false;
	// This will register the event handlers for Netscape and Mozilla
	window.addEventListener("resize", startFillingLeaderChars, true);
	window.addEventListener("load", startFillingLeaderChars, true);
}

var charfills = [""];        // array of strings containing the leader character repeated for powers of 2.
var allSpanNodes = null;     // array of span elements in the html.
var totalSpanNodes = 0;
var leaderTextWidth = null;  // array of widths for each single leader character.
var parentWidth = null;      // array of widths for each span node parent container.
var fixedWidthFill = new Array();  // array of strings that are prefilled with leader characters for a specific width.
var lastDocumentWidth = 0;   // the last browser display width

// The maximum power of 2 to use when prebuilding strings to concatinate when filling a node.
var charfillspower = 8;  // Keep this as low as reasonable!


// Declare global debug variables.
var debug_startTime;
var debug_totalProcessed;  // number of spans that are actually leader characters.
var debug_totalUpdates;    // number of browser "paints" required.

// Array of invalid parent tags to use in determining correct parent widths
var invalidParentTags = new Array("SPAN", "STRONG", "B", "I", "UNDERLINE");

function startFillingLeaderChars() {
	if (debugLeaderChars)
	{
		// Reset debug variables.
		debug_startTime = new Date();
		debug_totalProcessed = 0;
		debug_totalUpdates = 0;
	}

	// Only re-fill the leader characters if the document width changed.
	if (document.body)
	{
		if (lastDocumentWidth==document.body.offsetWidth)
		{
			// window size did not change, so do nothing!
			return;
		}
		else
		{
			// keep track of the last document width.
			lastDocumentWidth = document.body.offsetWidth;
		}
	}

	// Gather the span nodes from the document only once.
	if (allSpanNodes == null)
	{
		allSpanNodes = document.getElementsByTagName("span");
		// Save the total count of span nodes.
		totalSpanNodes = allSpanNodes.length;
		// Prepare the array of leaderText widths.
		leaderTextWidth = new Array(totalSpanNodes);
		// Prepare the array of parent container widths.
		parentWidth = new Array(totalSpanNodes);
	}

	// Get the parent widths.
	// Need to explicitly loop through each node.
	for (var spanNodeIndex=0;spanNodeIndex<=totalSpanNodes;spanNodeIndex++)
	{
		getParentWidths(spanNodeIndex);
	}

 	if (useTimeout)
	{
		// The timeout mechanism automatically continues to the next node.
		fillLeaderChars(0);
	}
	else
	{
		// Need to explicitly loop through each node.
		for (var spanNodeIndex=0;spanNodeIndex<=totalSpanNodes;spanNodeIndex++)
		{
			fillLeaderChars(spanNodeIndex);
		}
	}
}

function getParentWidths(spanNodeIndex)
{
	if (spanNodeIndex>=totalSpanNodes)
	{
		// Do nothing when reached the end of the array of span nodes.
		return;
	}

	var spanNode = allSpanNodes.item(spanNodeIndex);
	if (spanNode != null && spanNode.getAttribute("name") == "leader" && spanNode.childNodes.length == 1)
	{

		// Iterate to check if parent node is an invalid parent tag
		var isInvalidParentTag = false;
		for(var arrIndex = 0; arrIndex < invalidParentTags.length; arrIndex++)
		{
			if(spanNode.parentNode.nodeName.toUpperCase() == invalidParentTags[arrIndex])
			{
			    isInvalidParentTag = true;
				break;
			}
		}
		
		if (isInvalidParentTag)
		{
		    var parentChildNodeLength;

		    // Try and get the parent's child node's length to prevent script error
		    try {
		        parentChildNodeLength = spanNode.parentNode.childNodes[0].nodeValue.length;
		    }
		    catch (err) {
		        // Parent's child node's length can't be found since the parent doesn't have a child node so default to zero
		        parentChildNodeLength = 0;
		    }
		    		
			// We need to move up one level and grab the current node's grandparent width
			parentWidth[spanNodeIndex] = spanNode.parentNode.parentNode.offsetWidth - (spanNode.parentNode.parentNode.nodeName.length + parentChildNodeLength);
		}
		else
		{
			// Current node's parent is valid so grab parent's width
			parentWidth[spanNodeIndex] = spanNode.parentNode.offsetWidth;
		}
	}
}

// This function will spin through all the spans named "leader"
// and fill them with the leader character until the line is completely full.
function fillLeaderChars(spanNodeIndex)
{
	if (spanNodeIndex>=totalSpanNodes)
	{
		if (debugLeaderChars)
		{
			// Display debug results.
			var enddate = new Date();
			var elapsed = (enddate.getTime() - debug_startTime.getTime()) / 1000;
			alert("span: "+totalSpanNodes+"\nleaderchar: "+debug_totalProcessed+"\nupdates: "+debug_totalUpdates+" (~"+Math.round(debug_totalUpdates/debug_totalProcessed)+"/leaderchar)\ntime: "+elapsed+" seconds ("+Math.round((elapsed*1000)/debug_totalProcessed)+"ms/leaderchar)\nwidth: "+lastDocumentWidth);
		}

		// Do nothing when reached the end of the array of span nodes.
		return;
	}

	var spanNode = allSpanNodes.item(spanNodeIndex);
	if (spanNode != null && spanNode.getAttribute("name") == "leader" && spanNode.childNodes.length == 1)
	{
		debug_totalProcessed++;

		var textNode = spanNode.childNodes[0];
		var leaderText = textNode.nodeValue;
		if (leaderText==null || leaderText.length==0)
		{
			// Skip this one because of invalid content.
			if (useTimeout)
			{
				return setTimeout("fillLeaderChars("+(spanNodeIndex+1)+")",0);
			}
			return;
		}
		if (leaderText.length!=1)
		{
			// Only use the first character.
			leaderText = leaderText.charAt(0);
			// Save the width of one leader character (if necessary).
			if (leaderTextWidth[spanNodeIndex]==null)
			{
				// Need to get an accurate cell width,
				// so set the single leaderText into the nodeValue.
				textNode.nodeValue = leaderText;
				debug_totalUpdates++;
			}
		}
		if (leaderTextWidth[spanNodeIndex]==null)
		{
			// Save the width of one leader character for calculating on resize.
			leaderTextWidth[spanNodeIndex] = spanNode.offsetWidth;
		}

		// grab the width of the container
		var originalParentWidth = parentWidth[spanNodeIndex];

		// If the leaderchar is the only thing in the parent node, then fill to width.
		if (spanNode.parentNode.childNodes.length==1)
		{
			// See if this leader character has already been prefilled for the given width.
			var prefill = fixedWidthFill[leaderText+originalParentWidth];
			if (prefill==null)
			{
				// The leader character has not been prefilled.
				// Loop to create the string with the desired number of characters.
				// NOTE: Using a binary fill technique is not faster due to the extra processing
				// needed; so this is a simple character concatination technique instead.
				prefill = leaderText;
				if (leaderTextWidth[spanNodeIndex] != null && leaderTextWidth[spanNodeIndex] != 0)
				{
				    for (var remainingCharsToFill = Math.floor(originalParentWidth/leaderTextWidth[spanNodeIndex])-1; remainingCharsToFill>0; remainingCharsToFill--)
				    {
					    prefill += leaderText;
				    }
				}
				// Save the prefilled string for later reference.
				fixedWidthFill[leaderText+originalParentWidth] = prefill;
			}
			// Set the prefilled string into the node.
			textNode.nodeValue = prefill;
			debug_totalUpdates++;

			// Continue with the next span to process.
			if (useTimeout)
			{
				return setTimeout("fillLeaderChars("+(spanNodeIndex+1)+")",0);
			}
			return;
		}
		// This probably indicates that the current span node has an invalid parent tag
		else
		{
			// Iterate to check if parent node is an invalid parent tag
			var isInvalidParentTag = false;
			for(var arrIndex = 0; arrIndex < invalidParentTags.length; arrIndex++)
			{
				if(spanNode.parentNode.nodeName.toUpperCase() == invalidParentTags[arrIndex])
				{
					isInvalidParentTag = true;
					break;
				}
			}
			
			if(isInvalidParentTag)
			{
			    var prefill = leaderText;
			    var parentChildNodeLength;

                // Try and get the parent's child node's length to prevent script error
			    try {
			        parentChildNodeLength = spanNode.parentNode.childNodes[0].nodeValue.length;
			    }
			    catch (err) {
			        // Parent's child node's length can't be found so default to zero
			        parentChildNodeLength = 0;
			    }
			    
			    if (leaderTextWidth[spanNodeIndex] != null && leaderTextWidth[spanNodeIndex] != 0) 
			    {
			        var charsToFill;
			        var parentWidthFloor = Math.floor((originalParentWidth / leaderTextWidth[spanNodeIndex]) * .6);

			        if (parentWidthFloor < parentChildNodeLength) {
			            charsToFill = (originalParentWidth / leaderTextWidth[spanNodeIndex]);
			        }
			        else {
			            charsToFill = parentWidthFloor - parentChildNodeLength;
			        }
			        
			        for (var remainingCharsToFill = charsToFill; remainingCharsToFill > 0; remainingCharsToFill--) {
			            prefill += leaderText;
			        }
			    }
			    
				textNode.nodeValue = prefill;
				debug_totalUpdates++;

				// Continue with the next span to process.
				if (useTimeout)
				{
					return setTimeout("fillLeaderChars("+(spanNodeIndex+1)+")",0);
				}
				return;
			}
		}

		// A safe speedup will be to only populate the charfills array for unique leader chars
		// (most of them will be the same on a given page, the '.' for instance)
		if(charfills[0] != leaderText)
		{
			//Start with one leader character (2^0)
			charfills[0] = leaderText;
			// Build up the binary dot sets, up to the predetermined largest power
			for(var power = 1; power <= charfillspower; power++)
			{
				charfills[power] = charfills[power-1] + charfills[power-1];
			}
		}

		// 2^?
		// index=power=characters=maxfill
		//   0  =  0  =     1    =    1
		//   1  =  1  =     2    =    3
		//   2  =  2  =     4    =    7
		//   3  =  3  =     8    =   15
		// etc...

		// Determine what the highest power would be to fill the entire parent node.
		var adjustedcharfillspower = charfillspower;
		var maxtofill = Math.ceil(originalParentWidth/leaderTextWidth[spanNodeIndex]);
		for (power = 2; power<=charfillspower; power++)
		{
			if (maxtofill<Math.pow(2,power))
			{
				adjustedcharfillspower = power-1;
				break;
			}
		}

		// save the current width of the container
		var saveParentWidth = spanNode.parentNode.offsetWidth;

		// save the current height of the container
		var saveParentHeight = spanNode.parentNode.offsetHeight;

		// save the current height offset of the span
		var saveSpanHeight = spanNode.offsetHeight;

		// Start with nothing filled.
		var knownWorkingFill = "";

		// Indicates if the current value of textNode
		// is not the right number of leader characters.
		var badNodeValue = false;

		// Try to add the largest string, then attempt to add each smaller string, only keeping the ones that fit.
		// This is effectively adding the number of leader characters in binary.
		for (var power = adjustedcharfillspower; power >= 0; power--)
		{
			// attempt to add the next smaller string
			//
			//   NOTE: THIS CAUSES THE BROWSER TO RE-RENDER THE PAGE each time
			//
			textNode.nodeValue = charfills[power] + knownWorkingFill;
			debug_totalUpdates++;

			// check for overflow
			// back up if the parent container's height increases (wrapping has occured)
			// or the span has moved position (it is in a shorter cell)
			if (saveParentWidth < spanNode.parentNode.offsetWidth ||
				saveParentHeight < spanNode.parentNode.offsetHeight ||
				saveSpanHeight < spanNode.offsetHeight)
			{
				// Make note of setting a bad nodeValue.
				badNodeValue = true;
				continue;
			}

			// The content fit, so this is our new known-working fill.
			knownWorkingFill = textNode.nodeValue;
			// Make note of the good nodeValue;
			badNodeValue = false;
		}
		if (badNodeValue)
		{
			// One final update with the last known working fill.
			textNode.nodeValue = knownWorkingFill;
			debug_totalUpdates++;
		}
	}

	// Continue to the next node.
	if (useTimeout)
	{
		return setTimeout("fillLeaderChars("+(spanNodeIndex+1)+")",0);
	}
}

if(typeof(Sys) !== "undefined") {
	Sys.Application.notifyScriptLoaded();
}