//~****************************************************
// addCSS.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 appropriate CSS CLASS attributes to an HTML table, based on the Office of Policy Web style.
//
// This script is provided without warranty.
// This script is the third 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();
	
	// Note:  Before running this script, it is imperative that word-wrap be turned off.  For example, the reference to line 11 below is to the 11th line physically showing on the screen, not to the line that is actually numbered 11.
	
	// turn off screen updating
	doc.BeginUpdate();
	
	// determine the number of heading rows, the number of stub columns, the total number of columns in the table, and the number of data columns in the table (i.e., total - stub columns)
		// heading rows
			// set default variable value
			var headingRowCount = 1;
			// select line 11 from document
			doc.SelectLine(11);
			// assign it to a string
			strText = doc.SelText;
			// if there is a ROWSPAN attribute in the selected line, set headingRowCount to its value
			if (strText.match(/rowspan="([0-9]+)"/) != null) {
				headingRowCount = RegExp.$1;
			}
	
		// stub columns
			// set default variable value
			var stubColCount = 1;
			// if there is a COLSPAN attribute in the selected line (same line 11 already selected above for heading rows), set stubColCount to its value
			if (strText.match(/colspan="([0-9]+)"/) != null) {
				stubColCount = RegExp.$1;
			}

		// total number of columns in table
			// select entire doucment
			doc.SelectAll();
			// assign it to a string;
			strText = doc.SelText;
			// look for a blank row (i.e., only one TD with a colspan) and use value of its COLSPAN attribute to determine table width
			strText.match(/<tr>\r\n<td colspan="([0-9]+)"><\/td>\r\n<\/tr>/);
			// set variable to current width of table
			var totalBegColCount = RegExp.$1;
			// set variable to width table should be after all stubs are reduced to a single column with appropriate CSS class
			var totalEndColCount = totalBegColCount - stubColCount +1;

		// data columns 			
			var	totalDataColCount = totalEndColCount - 1;

	// clean up column headings
		//set heading row counter
		var currentHeadingRow = 1;

		// put in <thead> tag
		doc.SelectLine(10)
		doc.SelText = "<thead>\r\n<tr>";

		// clean up stub heading
			var currentLine = 12;
			// select stub heading	
			doc.SelectLine(currentLine);
			// assign it to a string
			strText = doc.SelText;
			// replace TD with TH, preserving any ROWSPAN attribute, and add appropriate CSS class
			strText = strText.replace(/<td(?: colspan="[0-9]+")?( rowspan="[0-9]+")?>(.*)<\/td>/i, "<th class=\"stubHeading\"$1>$2<\/th>");
			// replace selected text in current document with revised text
			doc.SelText = strText;
			// increment line counter
			currentLine++;
	
		//loop through remaining column heading rows and clean up
		while (currentHeadingRow <= headingRowCount) {
			doc.SelectLine(currentLine);
			strText = doc.SelText;
			// if this is the end of the row
			if (strText == "</tr>") {
				// increment heading row counter
				currentHeadingRow++;
				// increment line counter by 2 to skip <tr> on next line
				currentLine = currentLine + 2;
			}
			// else it is a spanner or a column heading
			else {
				// if this is a spanner
				if (strText.match(/colspan="([0-9]+)"/) != null) {
					// replace TD with TH, preserving COLSPAN and any ROWSPAN attributes, and add appropriate CSS class
					strText = strText.replace(/<td( colspan="[0-9]+")?( rowspan="[0-9]+")?>(.*)<\/td>/, "<th class=\"spanner\"$1$2>$3</th>");
					// replace selected text in current document with revised text
					doc.SelText = strText;
					// increment line counter
					currentLine++;
				}
				// else this is a column heading
				else {
					// replace TD with TH, preserving any ROWSPAN attribute, and add appropriate CSS class
					strText = strText.replace(/<td( rowspan="[0-9]+")?>(.*)<\/td>/, "<th$1>$2<\/th>");
					// replace selected text in current document with revised text
					doc.SelText = strText;
					// increment line counter
					currentLine++;
				}
			}
		} // end while

		// put in </thead> tag
			doc.SelectLine(currentLine - 2);
			doc.SelText = "</tr>\r\n</thead>\r\n<tbody>";

	// select entire document
	doc.SelectAll();

	// assign it to a string
	strText = doc.SelText;

	// clean up body of table
		// format panels and subpanels
			// check for subpanels
				// create RE search string to check for the existance of subpanels
				var panelWithSubRE = new RegExp("<tr>\r\n<td colspan=\"" + stubColCount +"\"><\/td>\r\n<td colspan=\"" + totalDataColCount + "\">(.*)<\/td>\r\n<\/tr>\r\n<tr>\r\n<td colspan=\"" + stubColCount + "\"><\/td>\r\n<td colspan=\"" + totalDataColCount + "\">(.*)<\/td>\r\n<\/tr>", "gi");
				// if there are subpanels
				if (strText.match(panelWithSubRE) != null) {
					// create replacement text for rows where there is a panel heading followed immediately by a subpanel heading
					var panelWithSubReplaceText = "<tr>\r\n<td></td>\r\n<th class=\"panelWithSub\" colspan=\"" + totalDataColCount + "\">$1</th>\r\n</tr>\r\n<tr>\r\n<td></td>\r\n<th class=\"subWithPanel\" colspan=\"" + totalDataColCount + "\">$2</th>\r\n</tr>";
					// do panel/subpanel replacement
					strText = strText.replace(panelWithSubRE, panelWithSubReplaceText);

					// create RE search string for just subpanels
					var subpanelRE = new RegExp("<tr>\r\n<td colspan=\"" + stubColCount +"\"><\/td>\r\n<td colspan=\"" + totalDataColCount + "\">(.*)<\/td>\r\n<\/tr>", "gi");
					// create replacement text for just subpanels
					var subpanelReplaceText = "<tr>\r\n<td></td>\r\n<th class=\"subpanel\" colspan=\"" + totalDataColCount + "\">$1</th>\r\n</tr>";
					// do subpanel replacement
					strText = strText.replace(subpanelRE, subpanelReplaceText);
				}
				// else, check for panels
				else {
					// create RE search string for just panels
					var panelRE = new RegExp("<tr>\r\n<td colspan=\"" + stubColCount + "\"><\/td>\r\n<td colspan=\"" + totalDataColCount + "\">(.*)<\/td>\r\n<\/tr>", "gi");
					// create replacement text for just panels
					var panelReplaceText = "<tr>\r\n<td></td>\r\n<th class=\"panel\" colspan=\"" + totalDataColCount + "\">$1</th>\r\n</tr>";
					// do panel replacement
					strText = strText.replace(panelRE, panelReplaceText);
				}

		// format blank rows
			// create RE search string for blank rows
			var blankRowRE = new RegExp("<tr>\r\n<td colspan=\"" + totalBegColCount + "\"></td>\r\n</tr>", "gi");
			// create replacement text for blank rows
			var blankRowReplaceText = "<tr>\r\n<td class=\"blankRow\" colspan=\"" + totalEndColCount + "\"></td>\r\n</tr>";
			// do blank row replacement
			strText = strText.replace(blankRowRE, blankRowReplaceText);
	

	// format table footer
		// get rid of blank row that Excel puts at the bottom of each table and put in closing TFOOT tag
			// create RE search string for blank Excel row
			var terminalRowRE = new RegExp("<tr>\r\n(<td></td>\r\n){" + totalBegColCount + "}</tr>", "gi");
			// replace blank Excel row with </tfoot>
			strText = strText.replace(terminalRowRE, "</tfoot>");
	
		// join cells of any lettered footnotes
			// create RE search string for lettered footnotes
			var footnoteRE = new RegExp("<tr>\r\n<td>([a-z]){1}.</td>\r\n<td colspan=\"" + (totalBegColCount - 1) + "\">(.+)</td>\r\n</tr>", "gi");
			// create replacement text for lettered footnotes
			var footnoteReplaceText = "<tr>\r\n<td colspan=\"" + totalBegColCount + "\">$1. $2</td>\r\n</tr>";
			// do lettered footnote replacement
			strText = strText.replace(footnoteRE, footnoteReplaceText);
	
		// find out how many notes are at the end of the table
			// create RE search string for tables notes (existance of text between TD tags separates these from blank rows)
			var allNotesRE = new RegExp("<tr>\r\n<td colspan=\"" + totalBegColCount + "\">(.+)</td>\r\n</tr>", "gi");
			// assing number of notes found to a variable
			var numberOfNotes = strText.match(allNotesRE);

		// create RE for an individual note to be used in IF...ELSE
		var singleNoteRE = "<tr>\r\n<td colspan=\"" + totalBegColCount + "\">(.+)</td>\r\n</tr>";

		// create RE for blank row to be used in IF...ELSE
		var singleBlankRowRE = "<tr>\r\n<td class=\"blankRow\" colspan=\"" + totalEndColCount + "\"></td>\r\n</tr>\r\n";

		// if there are notes at the end of the table
		if (numberOfNotes != null) {
			// if there is just one note
			if (numberOfNotes.length == 1) {
				// create RE search string for table note (this is two blank lines and then the actual note)
				var tfootRE = new RegExp(singleBlankRowRE + singleBlankRowRE + singleNoteRE, "i");
				// do note replacement
				strText = strText.replace(tfootRE, "</tbody>\r\n<tfoot>\r\n<tr>\r\n<td class=\"onlyNote\" colspan=\"" + totalEndColCount + "\">$1</td>\r\n</tr>");
			}
			// else, there is more than one note
			else {
				// create RE search string for first table note (this is two blank lines and then then actual note)
				var tfootRE = new RegExp(singleBlankRowRE + singleBlankRowRE + singleNoteRE, "i");
				// do note replacement
				strText = strText.replace(tfootRE, "</tbody>\r\n<tfoot>\r\n<tr>\r\n<td class=\"firstNote\" colspan=\"" + totalEndColCount + "\">$1</td>\r\n</tr>"); 

				// if there are more than two notes, process all but last note
				// if there are only two notes, this FOR block won't execute
				for (i=2; i < numberOfNotes.length; i++) {
					// create RE search string for notes
					var noteRE = new RegExp(singleNoteRE, "i");
					// do notes replacement
					strText = strText.replace(noteRE, "<tr>\r\n<td class=\"note\" colspan=\"" + totalEndColCount + "\">$1</td>\r\n</tr>");
				}

				// create RE search string for last table note
				var lastNoteRE = new RegExp(singleNoteRE, "i");
				// do note replacement
				strText = strText.replace(lastNoteRE, "<tr>\r\n<td class=\"lastNote\" colspan=\"" + totalEndColCount + "\">$1</td>\r\n</tr>");
			}
		}
		// else, there are no notes at the end of the table, just need to put in coding to get bottom rule before </tfoot>
		else {
			// create RE search string
			var tfootRE = new RegExp(singleBlankRowRE + "</tfoot>", "i");
			// do replacement
			strText = strText.replace(tfootRE, "</tbody>\r\n<tfoot>\r\n<tr>\r\n<td class=\"noNotes\" colspan=\"" + totalEndColCount + "\">&nbsp;</td>\r\n</tr>\r\n</tfoot>");
		}
	
	// format row stubs
	// replace blank cells + row stub with TH and appropriate CSS class, working in decreasing order (from most indented stub back to left margin)
		// set counter to max value, i.e., stubColCount		
		var currentColCount = stubColCount;
		for (i = 0; i < stubColCount; i++) {
			// create RE search string for row stubs
			stubRE = new RegExp("<tr>\r\n(?:<td></td>\r\n){"+ (stubColCount - currentColCount) +"}<td(?: colspan=\"" + currentColCount + "\"){0," + (currentColCount - 1) + "}>(.+)</td>", "gi");
			// do row stub replacement
			strText = strText.replace(stubRE, "<tr>\r\n<th class=\"stub" + (stubColCount - currentColCount) + "\">$1</th>");
			// decrement counter
			currentColCount--;
		}

	// add nonbreaking spaces back in before <sup>--this prevents screen readers from running them in with text
	strText = strText.replace(/<sup>/gi, "&nbsp;<sup>");

	// replace selected text in current doucment with revised text
	doc.SelText = strText;
	
	// position cursor at start of document
	doc.CursorDocStart(false);
	
	// turn screen updating back on	
	doc.EndUpdate();

	// sets status bar text
	app.SetStatusText("Done");
}

