//~****************************************************
// add508.js
//
// Written by Laurie Brown, Webmaster - Office of Policy, SSA,
// and presented at the FedStats 508/Accessibility Workshop, June 24, 2002.
//
// This script was designed to run in ColdFusion Studio 5.
// It adds IDs and HEADERS attributes based on the visual hierarchy of an HTML table that is created by CSS CLASS attributes.
//
// This script is provided without warranty.
// This script is the last in a series of four scripts that are provided to show logic that can be used to help automate the tagging of tables for Section 508 compliance.
// Individual solutions will vary based on source program generating the HTML, table structure, and CSS coding.
// Please include appropriate attribution in any future work based on these scripts.
//~****************************************************

function Main() 
{
	var app = Application;
	var doc = app.ActiveDocument;
	var strText = new String();
	
	// turn off screen updating
	doc.BeginUpdate();
	
	// select the entire document
	doc.SelectAll();

	//assign it to a string
	strText = doc.SelText;

	// find the width of the table from a notes row
	tableWidthRE = 	strText.match("<td class=\".*Notes*\" colspan=\"([0-9]+)\"", "i");
	var tableWidth = parseInt(RegExp.$1);

	// find	the number of column heading rows from the stubHeader
	colHeadingRows = 1;
	colHeadingRowsRE = strText.match("<th class=\"stubHeading\" rowspan=\"([0-9]+)\">", "i");
	if (colHeadingRowsRE != null) {
		colHeadingRows = parseInt(RegExp.$1);
	}

	// initialize column HEADERS arrays
	var colHeaders = new Array(colHeadingRows + 1);
	for (i=1; i <= colHeadingRows; i++) {
		colHeaders[i] = new Array(tableWidth + 1);
	}
	
	// process column headings
		// set initial values
		var currentLine = 10;
		var colID = 1;

		// process each heading row
		for (curRow=1; curRow <= colHeadingRows; curRow++) {
			// set or increment variables appropriately
			var endOfRow = "false";
			var curCol = 1;
			currentLine = currentLine + 2;
			
			// select current line
			doc.SelectLine(currentLine);

			// assign it to a string
			strText = doc.SelText;	
			
			// while current line is not the end of the row (i.e. </tr>) process line
			while (endOfRow == "false") {
				// determine if cell spans multiple columns
				colSpan = 1;
				if (strText.match("colspan=\"([0-9]+)\"", "i") != null) {
					colSpan = parseInt(RegExp.$1);
				}
				
				// determine if cell spans multiple rows
				rowSpan = 1;
				if (strText.match("rowspan=\"([0-9]+)\"", "i") != null) {
					rowSpan = parseInt(RegExp.$1);
				}
				
				// assign ID attribute
				strText = strText.replace(/<th(.*?)>/i, "<th$1 id=\"c" + colID + "\">");
			
				// add ID to arrays
					// loop through array for curRow to find first blank element
					for (col = curCol; col <= tableWidth; col++) {
						if (colHeaders[curRow][col] == null) {
							curCol = col;
							break;
						}
					}

					// for each row and col that cell spans, add ID to appropriate array
					for (row = curRow; row <= (curRow + rowSpan - 1); row++) {
						for (col = curCol; col <= (curCol + colSpan -1); col++) {
							colHeaders[row][col] = "c" + colID;
						}
					}
					
				// add appropriate HEADERS attribute
				if (curRow > 1){
					// create variable to hold applicable values from colHeaders array
					var tempColHeaders = new String();
					// add data from the given curCol of the first row of colHeaders array
					tempColHeaders = colHeaders[1][curCol];
					// loop through remaining rows to see if any other value should be added to tempColHeaders
					for (row=2; row < curRow; row++) {
						if (colHeaders[row][curCol] != colHeaders[row-1][curCol]) {
							tempColHeaders = tempColHeaders + " " + colHeaders[row][curCol];
						}
					}
					// add tempColHeaders to TH tag
					strText = strText.replace(/<th(.*?)>/i, "<th$1 headers=\"" + tempColHeaders + "\">");
				}

				// replace selected text in current document with revised text
				doc.SelText = strText;
			
				// increment variables
				currentLine++;
				colID++;
				curCol = curCol + colSpan

				// select current line
				doc.SelectLine(currentLine);

				// assign it to a string
				strText = doc.SelText;
		
				// if end of the row, done processing row
				if (strText == "</tr>") {
					// set flag appropriately
					endOfRow = "true";
				}
			}  // end while
		} // end for
		
	// collapse colHeaders array
		finalColHeaders = new Array (tableWidth + 1);
		// populate array with first row of col header data
		for (col=1; col <= tableWidth; col++) {
			finalColHeaders[col] = colHeaders[1][col];
		}
		// loop through remaining rows and add data to array as necessary
		for (row=2; row <= colHeadingRows; row++) {
			// for each column in the row
			for (col=1; col <= tableWidth; col++) {
				// check to see if value is the same as previous value (same column, previous row) already in the final array
				if (colHeaders[row][col] != colHeaders[row-1][col]) {
					// if not, add it to array
					finalColHeaders[col] = finalColHeaders[col] + " " + colHeaders[row][col];
				}
			}
		}

	// increment current line to start of body of table
	currentLine = currentLine+4
	
	// add IDs and HEADERS to table body
		// set initial values
		var tableBodyFlag = "true";
		var rowID = 1;
		var rowHeaders = new Array();
		var abnormalTerm = "false";
		
		// select current line
		doc.SelectLine(currentLine);

		// assign it to a string
		strText = doc.SelText;
	
		// while current row is not the start of the table footer
		while (tableBodyFlag == "true") {
			// row will be one of three things:  a row of data, a blank row, or a panel or subpanel
			// following if...else statements work through the possibilities in order of frequency, with a final else in case nothing matches
			// the final else is necessary to keep the script from hanging in case an unexpected tag is encountered
			
			// if row of data or blank row (both will have class attribute)
			if (strText.match("class=\"([a-zA-Z0-9]+)\"", "i") != null) {
				// get CSS class
				var rowClass = RegExp.$1;
			
				// if blank row
				if (rowClass == "blankRow") {
					// move to appropiate place in next row of table
					currentLine = currentLine + 3;
				}
				// else, should be a row stub
				else {
					// if cell is a row stub
					if (rowClass.match("stub([0-9]+)","i") != null) {
						// figure out the level of indent from the number following "stub" in the class attribute value
						// adjust it to the appropriate array index value and assign it to a variable
						var stubArrayIndex = parseInt(RegExp.$1) + 2;

						// create variable to hold applicable values from rowHeaders array
						var tempRowHeaders = new String();
	
						// loop through array and add appropriate values (i.e., not null) to tempRowHeaders
						for (i=0; i < stubArrayIndex; i++) {
							if (rowHeaders[i] != null) {
								tempRowHeaders = tempRowHeaders + rowHeaders[i] + " ";
							}
						}
						
						// add tempRowHeaders to TH tag
						strText = strText.replace(/<th(.*?)>/i, "<th$1 id=\"r" + rowID + "\" headers=\"" + tempRowHeaders + "c1\">");
						
						// replace selected text in current document with revised text
						doc.SelText = strText;

						// add current ID to rowHeaders array
						rowHeaders[stubArrayIndex] = "r" + rowID;
						
						// delete rest of array
						for (i=stubArrayIndex+1; i <=rowHeaders.length-1; i++) {
							rowHeaders[i] = null;
						}
						
						// add current ID to tempRowHeaders for used with data in the row
						tempRowHeaders = tempRowHeaders + "r" + rowID + " ";
				
						// loop through data rows and add headers
							// increment variable
							currentLine = currentLine + 1;
							// select current line
							doc.SelectLine(currentLine);
							// assign it to a string
							strText = doc.SelText;
							// check that there is actually data in the row
							// if <td> does not contain a colspan attribute, then there is data in row
							if (strText.match("colspan", "i") == null) {
								for (i=2; i <= tableWidth; i++) {
									// add tempRowHeaders to TD tag
									strText = strText.replace(/<td>/i, "<td headers=\"" + tempRowHeaders + finalColHeaders[i] + "\">");
									// replace selected text in current document with revised text
									doc.SelText = strText;
									// increment variable
									currentLine = currentLine + 1;
									// select current line
									doc.SelectLine(currentLine);
									// assign it to a string
									strText = doc.SelText;
								}
							}
							// else, skip line
							else {
							currentLine = currentLine + 1;
							}
						// increment variables
						currentLine = currentLine + 2;
						rowID++;
					}
					// else, unexpected tag
					else {
						app.MessageBox("Unexpected tag encountered. Script will terminate.", 'Information', 0);
						tableBodyFlag = "false";
						abnormalTerm = "true";
					}
				}
			}
			// else, check other possibilities
			else {
				// if panel or subpanel
				if (strText == "<td></td>") {
					// skip blank row stub
					currentLine++;

					// select actual panel or subpanel heading
					doc.SelectLine(currentLine);

					// assign it to a string
					strText = doc.SelText;

					// get its CSS class			
					panelLevelRE = strText.match("class=\"([a-z]+)\"", "i");
					var panelLevel = RegExp.$1;
			
					// if it is a panel
					if (panelLevel = "panel") {
						// assign ID attribute
						strText = strText.replace(/<th(.*?)>/i, "<th$1 id=\"r" + rowID + "\">");

						// add ID to rowHeaders array
						rowHeaders[0] = "r" + rowID;

						// delete rest of array
						for (i=1; i <=rowHeaders.length-1; i++) {
							rowHeaders[i] = null;
						}

					}
					// else, it is a subpanel
					else {
						// assign ID attribute
						strText = strText.replace(/<th(.*?)>/i, "<th$1 id=\"r" + rowID + "\" headers=\"" + rowHeaders[0] + "\">");

						// add ID to rowHeaders array
						rowHeaders[1] = "r" + rowID;

						// delete rest of array
						for (i=2; i <=rowHeaders.length-1; i++) {
							rowHeaders[i] = null;
						}

					}

					// replace selected text in current document with revised text						
					doc.SelText = strText;

					// increment variables
					currentLine = currentLine + 3;
					rowID++;
				}
				// else, unexpected tag
				else {
					app.MessageBox("Unexpected tag encountered. Script will terminate.", 'Information', 0);
					tableBodyFlag = "false";
					abnormalTerm = "true";
				}
			}

			// select current line
			doc.SelectLine(currentLine);
			
			// assign it to a string
			strText = doc.SelText;
			
			// if beginning of table footer, done processing table body
			if (strText == "<tfoot>") {
				// set flag appropriately
				tableBodyFlag = "false";
			}
		}	// end while

	// if script ran normally, position cursor at start of document; otherwise leave it at place script terminated abnormally
	if (abnormalTerm == "false") {
		doc.CursorDocStart(false);
	}
	
	// turn screen updating back on
	doc.EndUpdate();

	// set status bar text
	app.SetStatusText("Done");
}

