diff --git a/xtrn/DDAreaChoosers/DDFileAreaChooser.cfg b/xtrn/DDAreaChoosers/DDFileAreaChooser.cfg
index 206bf56598396128d2339969efeded52975b9836..00a7aaeaecaf130710d50f44f3c1e0bd3ea8dec8 100644
--- a/xtrn/DDAreaChoosers/DDFileAreaChooser.cfg
+++ b/xtrn/DDAreaChoosers/DDFileAreaChooser.cfg
@@ -1,5 +1,7 @@
 [BEHAVIOR]
 useLightbarInterface=true
+areaChooserHdrFilenameBase=fileAreaChgHeader
+areaChooserHdrMaxLines=5
 
 [COLORS]
 ; Area number
diff --git a/xtrn/DDAreaChoosers/DDFileAreaChooser.js b/xtrn/DDAreaChoosers/DDFileAreaChooser.js
index 9ab6ebf00aa07cf36f0564647036ea51b97d2f0e..63ee3f94496754d9e51147501661b9eb58edf8a5 100644
--- a/xtrn/DDAreaChoosers/DDFileAreaChooser.js
+++ b/xtrn/DDAreaChoosers/DDFileAreaChooser.js
@@ -80,8 +80,8 @@ if (system.version_num < 31400)
 }
 
 // Version & date variables
-var DD_FILE_AREA_CHOOSER_VERSION = "1.09";
-var DD_FILE_AREA_CHOOSER_VER_DATE = "2016-01-17";
+var DD_FILE_AREA_CHOOSER_VERSION = "1.10 Beta 1";
+var DD_FILE_AREA_CHOOSER_VER_DATE = "2016-02-14";
 
 // Keyboard input key codes
 var CTRL_M = "\x0d";
@@ -97,6 +97,14 @@ var KEY_PAGE_DOWN = "\1PgDn";
 var UP_ARROW = ascii(24);
 var DOWN_ARROW = ascii(25);
 
+// Determine the script's startup directory.
+// This code is a trick that was created by Deuce, suggested by Rob Swindell
+// as a way to detect which directory the script was executed in.  I've
+// shortened the code a little.
+var gStartupPath = '.';
+try { throw dig.dist(dist); } catch(e) { gStartupPath = e.fileName; }
+gStartupPath = backslash(gStartupPath.replace(/[\/\\][^\/\\]*$/,''));
+
 // 1st command-line argument: Whether or not to choose a file library first (if
 // false, then only choose a directory within the user's current library).  This
 // can be true or false.
@@ -161,6 +169,10 @@ function DDFileAreaChooser()
 	this.areaNumLen = 4;
 	this.descFieldLen = 67; // Description field length
 
+	// Filename base of a header to display above the area list
+	this.areaChooserHdrFilenameBase = "fileAreaChgHeader";
+	this.areaChooserHdrMaxLines = 5;
+
 	// Set the function pointers for the object
 	this.ReadConfigFile = DDFileAreaChooser_ReadConfigFile;
 	this.SelectFileArea = DDFileAreaChooser_selectFileArea;
@@ -183,9 +195,10 @@ function DDFileAreaChooser()
 	this.ShowHelpScreen = DDFileAreaChooser_showHelpScreen;
 	// Misc. functions
 	this.NumFilesInDir = DDFileAreaChooser_NumFilesInDir;
-
 	// Function to build the directory printf information for a file lib
 	this.BuildFileDirPrintfInfoForLib = DDFileAreaChooser_buildFileDirPrintfInfoForLib;
+	// Function to display the header above the area list
+	this.DisplayAreaChgHdr = DDFileAreaChooser_DisplayAreaChgHdr;
 
 	// Read the settings from the config file.
 	this.ReadConfigFile();
@@ -268,6 +281,10 @@ function DDFileAreaChooser()
 	// created on the fly the first time the user lists directories for
 	// a file library.
 	this.fileDirListPrintfInfo = new Array();
+
+	// areaChangeHdrLines is an array of text lines to use as a header to display
+	// above the message area changer lists.
+	this.areaChangeHdrLines = loadTextFileIntoArray(this.areaChooserHdrFilenameBase, this.areaChooserHdrMaxLines);
 }
 
 // For the DDFileAreaChooser class: Lets the user choose a file area.
@@ -316,6 +333,9 @@ function DDFileAreaChooser_selectFileArea_Traditional(pChooseLib)
 			bbs.command_str = "";
 
 			console.clear("\1n");
+			this.DisplayAreaChgHdr(1);
+			if (this.areaChangeHdrLines.length > 0)
+				console.crlf();
 			this.ListFileLibs();
 			console.print("\1n\1b\1h� \1n\1cWhich, \1hQ\1n\1cuit, or [\1h" + +(bbs.curlib+1) + "\1n\1c]:\1h ");
 			// Accept Q (quit) or a file library number
@@ -352,47 +372,50 @@ function DDFileAreaChooser_selectFileArea_Traditional(pChooseLib)
 // Return value: Boolean - Whether or not the user chose a file area.
 function DDFileAreaChooser_selectDirWithinFileLib_Traditional(pLibNumber, pSelectedDir)
 {
-   var userChoseAnArea = false;
+	var userChoseAnArea = false;
 
-   // If the file library number is valid, then
-   // set it and let the user choose a file directory
-   // within the library.
-   if (pLibNumber > 0)
-   {
-      // Ensure that the file directory printf information is created for
-      // this file library.
-      this.BuildFileDirPrintfInfoForLib(pLibNumber-1);
+	// If the file library number is valid, then
+	// set it and let the user choose a file directory
+	// within the library.
+	if (pLibNumber > 0)
+	{
+		// Ensure that the file directory printf information is created for
+		// this file library.
+		this.BuildFileDirPrintfInfoForLib(pLibNumber-1);
 
-      // Set the default directory #: The current directory, or if the
-      // user chose a different file library, then this should be set
-      // to the first directory.
-      var defaultDir = bbs.curdir + 1;
-      if (pLibNumber-1 != bbs.curlib)
-         defaultDir = 1;
+		// Set the default directory #: The current directory, or if the
+		// user chose a different file library, then this should be set
+		// to the first directory.
+		var defaultDir = bbs.curdir + 1;
+		if (pLibNumber-1 != bbs.curlib)
+			defaultDir = 1;
 
-      console.clear("\1n");
-      this.ListDirsInFileLib(pLibNumber - 1, defaultDir - 1);
-      console.print("\1n\1b\1h� \1n\1cWhich, \1hQ\1n\1cuit, or [\1h" + defaultDir +
-                    "\1n\1c]: \1h");
-      // Accept Q (quit) or a file directory number
-      var selectedDir = console.getkeys("Q", file_area.lib_list[pLibNumber - 1].dir_list.length);
-
-      // If the user just pressed enter (selectedDir would be blank),
-      // default the selected directory.
-      if (selectedDir.toString() == "")
-         selectedDir = defaultDir;
-
-      // If the user chose a directory, then set bbs.curlib &
-      // bbs.curdir and quit the file library loop.
-      if ((pLibNumber.toString() != "Q") && (selectedDir > 0))
-      {
-         bbs.curlib = pLibNumber - 1;
-         bbs.curdir = selectedDir - 1;
-         userChoseAnArea = true;
-      }
-   }
+		console.clear("\1n");
+		this.DisplayAreaChgHdr(1);
+		if (this.areaChangeHdrLines.length > 0)
+			console.crlf();
+		this.ListDirsInFileLib(pLibNumber - 1, defaultDir - 1);
+		console.print("\1n\1b\1h� \1n\1cWhich, \1hQ\1n\1cuit, or [\1h" + defaultDir +
+		              "\1n\1c]: \1h");
+		// Accept Q (quit) or a file directory number
+		var selectedDir = console.getkeys("Q", file_area.lib_list[pLibNumber - 1].dir_list.length);
+
+		// If the user just pressed enter (selectedDir would be blank),
+		// default the selected directory.
+		if (selectedDir.toString() == "")
+			selectedDir = defaultDir;
+
+		// If the user chose a directory, then set bbs.curlib &
+		// bbs.curdir and quit the file library loop.
+		if ((pLibNumber.toString() != "Q") && (selectedDir > 0))
+		{
+			bbs.curlib = pLibNumber - 1;
+			bbs.curdir = selectedDir - 1;
+			userChoseAnArea = true;
+		}
+	}
 
-   return userChoseAnArea;
+	return userChoseAnArea;
 }
 
 // For the DDFileAreaChooser class: Traditional user interface for listing
@@ -566,7 +589,7 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 		if ((bbs.curlib != null) && (typeof(bbs.curlib) == "number"))
 			selectedLibIndex = bbs.curlib;
 
-		var listStartRow = 2;      // The row on the screen where the list will start
+		var listStartRow = 2+this.areaChangeHdrLines.length; // The row on the screen where the list will start
 		var listEndRow = console.screen_rows - 1; // Row on screen where list will end
 		var topFileLibIndex = 0;    // The index of the message group at the top of the list
 
@@ -612,14 +635,15 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 			}
 		}
 
-		// Clear the screen, write the help line and group list header, and output
+		// Clear the screen, write the header, help line, and group list header, and output
 		// a screenful of message groups.
 		console.clear("\1n");
+		this.DisplayAreaChgHdr(1);
 		this.WriteKeyHelpLine();
 
 		var curpos = new Object();
 		curpos.x = 1;
-		curpos.y = 1;
+		curpos.y = 1+this.areaChangeHdrLines.length;
 		console.gotoxy(curpos);
 		this.WriteLibListHdrLine(numPages, pageNum);
 		this.ListScreenfulOfFileLibs(topFileLibIndex, listStartRow, listEndRow, false, false);
@@ -729,7 +753,7 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 					{
 						// An area was not chosen, so we'll have to re-draw
 						// the header and list of message groups.
-						console.gotoxy(1, 1);
+						console.gotoxy(1, 1+this.areaChangeHdrLines.length);
 						this.WriteLibListHdrLine(numPages, pageNum);
 						this.ListScreenfulOfFileLibs(topFileLibIndex, listStartRow, listEndRow, false, true);
 					}
@@ -784,6 +808,7 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 						selectedLibIndex = topIndexForLastPage;
 					}
 					break;
+				case KEY_ESC: // Quit
 				case 'Q': // Quit
 					continueChoosingFileArea = false;
 					break;
@@ -791,8 +816,9 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 					this.ShowHelpScreen(true, true);
 					console.pause();
 					// Refresh the screen
+					this.DisplayAreaChgHdr(1);
 					this.WriteKeyHelpLine();
-					console.gotoxy(1, 1);
+					console.gotoxy(1, 1+this.areaChangeHdrLines.length);
 					this.WriteLibListHdrLine(numPages, pageNum);
 					this.ListScreenfulOfFileLibs(topFileLibIndex, listStartRow, listEndRow, false, true);
 					break;
@@ -876,347 +902,348 @@ function DDFileAreaChooser_selectFileArea_Lightbar(pChooseLib)
 //                             Will be -1 if none chosen.
 function DDFileAreaChooser_selectDirWithinFileLib_Lightbar(pLibIndex, pHighlightIndex)
 {
-   // Create the return object.
-   var retObj = new Object();
-   retObj.fileDirChosen = false;
-   retObj.fileLibIndex = -1;
-
-   var libIndex = 0;
-   if (typeof(pLibIndex) == "number")
-      libIndex = pLibIndex;
-   else if ((bbs.curlib != null) && (typeof(bbs.curlib) == "number"))
-      libIndex = bbs.curlib;
-   // Double-check libIndex
-   if (libIndex < 0)
-      libIndex = 0;
-   else if (libIndex >= file_area.lib_list.length)
-      libIndex = file_area.lib_list.length - 1;
-
-   var highlightIndex = 0;
-   if ((pHighlightIndex != null) && (typeof(pHighlightIndex) == "number"))
-      highlightIndex = pHighlightIndex;
-   else if ((bbs.curdir != null) && (typeof(bbs.curdir) == "number") &&
-             (bbs.curlib == pLibIndex))
-   {
-      highlightIndex = bbs.curdir;
-   }
-   // Double-check highlightIndex
-   if (highlightIndex < 0)
-      highlightIndex = 0;
-   else if (highlightIndex >= file_area.lib_list[libIndex].dir_list.length)
-      highlightIndex = file_area.lib_list[libIndex].dir_list.length - 1;
-
-   // If there are no sub-boards in the given message group, then show
-   // an error and return.
-   if (file_area.lib_list[libIndex].dir_list.length == 0)
-   {
-      console.clear("\1n");
-      console.print("\1y\1hThere are no directories in the chosen library.\r\n\1p");
-      return retObj;
-   }
-   
-   // Ensure that the file directory printf information is created for
-   // this file library.
-   this.BuildFileDirPrintfInfoForLib(libIndex);
-
-   // Returns the index of the bottommost directory that can be displayed on
-   // the screen.
-   //
-   // Parameters:
-   //  pTopDirIndex: The index of the topmost directory displayed on screen
-   //  pNumItemsPerPage: The number of items per page
-   function getBottommostDirIndex(pTopDirIndex, pNumItemsPerPage)
-   {
-      var bottomDirIndex = pTopDirIndex + pNumItemsPerPage - 1;
-      // If bottomDirIndex is beyond the last index, then adjust it.
-      if (bottomDirIndex >= file_area.lib_list[libIndex].dir_list.length)
-         bottomDirIndex = file_area.lib_list[libIndex].dir_list.length - 1;
-      return bottomDirIndex;
-   }
+	// Create the return object.
+	var retObj = new Object();
+	retObj.fileDirChosen = false;
+	retObj.fileLibIndex = -1;
 
+	var libIndex = 0;
+	if (typeof(pLibIndex) == "number")
+		libIndex = pLibIndex;
+	else if ((bbs.curlib != null) && (typeof(bbs.curlib) == "number"))
+		libIndex = bbs.curlib;
+	// Double-check libIndex
+	if (libIndex < 0)
+		libIndex = 0;
+	else if (libIndex >= file_area.lib_list.length)
+		libIndex = file_area.lib_list.length - 1;
+
+	var highlightIndex = 0;
+	if ((pHighlightIndex != null) && (typeof(pHighlightIndex) == "number"))
+		highlightIndex = pHighlightIndex;
+	else if ((bbs.curdir != null) && (typeof(bbs.curdir) == "number") &&
+			(bbs.curlib == pLibIndex))
+	{
+		highlightIndex = bbs.curdir;
+	}
+	// Double-check highlightIndex
+	if (highlightIndex < 0)
+		highlightIndex = 0;
+	else if (highlightIndex >= file_area.lib_list[libIndex].dir_list.length)
+		highlightIndex = file_area.lib_list[libIndex].dir_list.length - 1;
+
+	// If there are no sub-boards in the given message group, then show
+	// an error and return.
+	if (file_area.lib_list[libIndex].dir_list.length == 0)
+	{
+		console.clear("\1n");
+		console.print("\1y\1hThere are no directories in the chosen library.\r\n\1p");
+		return retObj;
+	}
 
-   // Figure out the index of the user's currently-selected sub-board.
-   var selectedDirIndex = 0;
-   if ((bbs.curdir != null) && (typeof(bbs.curdir) == "number"))
-   {
-      if ((bbs.curlib != null) && (typeof(bbs.curlib) == "number") &&
-          (bbs.curlib == pLibIndex))
-      {
-         selectedDirIndex = bbs.curdir;
-      }
-   }
+	// Ensure that the file directory printf information is created for
+	// this file library.
+	this.BuildFileDirPrintfInfoForLib(libIndex);
 
-   var listStartRow = 3;      // The row on the screen where the list will start
-   var listEndRow = console.screen_rows - 1; // Row on screen where list will end
-   var topDirIndex = 0;      // The index of the message group at the top of the list
-   // Figure out the index of the last message group to appear on the screen.
-   var numItemsPerPage = listEndRow - listStartRow + 1;
-   var bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-   // Figure out how many pages are needed to list all the sub-boards.
-   var numPages = Math.ceil(file_area.lib_list[libIndex].dir_list.length / numItemsPerPage);
-   var pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-   // Figure out the top index for the last page.
-   var topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage;
-
-   // If the highlighted row is beyond the current screen, then
-   // go to the appropriate page.
-   if (selectedDirIndex > bottomDirIndex)
-   {
-      var nextPageTopIndex = 0;
-      while (selectedDirIndex > bottomDirIndex)
-      {
-         nextPageTopIndex = topDirIndex + numItemsPerPage;
-         if (nextPageTopIndex < file_area.lib_list[libIndex].dir_list.length)
-         {
-            // Adjust topDirIndex and bottomDirIndex, and
-            // refresh the list on the screen.
-            topDirIndex = nextPageTopIndex;
-            pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-            bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-         }
-         else
-            break;
-      }
+	// Returns the index of the bottommost directory that can be displayed on
+	// the screen.
+	//
+	// Parameters:
+	//  pTopDirIndex: The index of the topmost directory displayed on screen
+	//  pNumItemsPerPage: The number of items per page
+	function getBottommostDirIndex(pTopDirIndex, pNumItemsPerPage)
+	{
+		var bottomDirIndex = pTopDirIndex + pNumItemsPerPage - 1;
+		// If bottomDirIndex is beyond the last index, then adjust it.
+		if (bottomDirIndex >= file_area.lib_list[libIndex].dir_list.length)
+			bottomDirIndex = file_area.lib_list[libIndex].dir_list.length - 1;
+		return bottomDirIndex;
+	}
 
-      // If we didn't find the correct page for some reason, then set the
-      // variables to display page 1 and select the first message group.
-      var foundCorrectPage =
-          ((topDirIndex < file_area.lib_list[libIndex].dir_list.length) &&
-           (selectedDirIndex >= topDirIndex) && (selectedDirIndex <= bottomDirIndex));
-      if (!foundCorrectPage)
-      {
-         topDirIndex = 0;
-         pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-         bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-         selectedDirIndex = 0;
-      }
-   }
 
-   // Clear the screen, write the help line and group list header, and output
-   // a screenful of message groups.
-   console.clear("\1n");
-   this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
-   this.WriteKeyHelpLine();
-
-   var curpos = new Object();
-   curpos.x = 1;
-   curpos.y = 2;
-   console.gotoxy(curpos);
-   printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
-   this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow, listEndRow,
-                               false, false);
-   // Start of the input loop.
-   var highlightScrenRow = 0; // The row on the screen for the highlighted group
-   var userInput = "";        // Will store a keypress from the user
-   var continueChoosingFileDir = true;
-   while (continueChoosingFileDir)
-   {
-      // Highlight the currently-selected message group
-      highlightScrenRow = listStartRow + (selectedDirIndex - topDirIndex);
-      curpos.y = highlightScrenRow;
-      if ((highlightScrenRow > 0) && (highlightScrenRow < console.screen_rows))
-      {
-         console.gotoxy(1, highlightScrenRow);
-         this.WriteFileLibDirLine(libIndex, selectedDirIndex, true);
-      }
+	// Figure out the index of the user's currently-selected sub-board.
+	var selectedDirIndex = 0;
+	if ((bbs.curdir != null) && (typeof(bbs.curdir) == "number"))
+	{
+		if ((bbs.curlib != null) && (typeof(bbs.curlib) == "number") && (bbs.curlib == pLibIndex))
+			selectedDirIndex = bbs.curdir;
+	}
 
-      // Get a key from the user (upper-case) and take action based upon it.
-      userInput = getKeyWithESCChars(K_UPPER | K_NOCRLF);
-      switch (userInput)
-      {
-         case KEY_UP: // Move up one message group in the list
-            if (selectedDirIndex > 0)
-            {
-               // If the previous group index is on the previous page, then
-               // display the previous page.
-               var previousSubIndex = selectedDirIndex - 1;
-               if (previousSubIndex < topDirIndex)
-               {
-                  // Adjust topDirIndex and bottomDirIndex, and
-                  // refresh the list on the screen.
-                  topDirIndex -= numItemsPerPage;
-                  pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-                  bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-                  this.updatePageNumInHeader(pageNum, numPages, false, false);
-                  this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                              listEndRow, false, true);
-               }
-               else
-               {
-                  // Display the current line un-highlighted.
-                  console.gotoxy(1, curpos.y);
-                  this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
-               }
-               selectedDirIndex = previousSubIndex;
-            }
-            break;
-         case KEY_DOWN: // Move down one message group in the list
-            if (selectedDirIndex < file_area.lib_list[libIndex].dir_list.length - 1)
-            {
-               // If the next group index is on the next page, then display
-               // the next page.
-               var nextGrpIndex = selectedDirIndex + 1;
-               if (nextGrpIndex > bottomDirIndex)
-               {
-                  // Adjust topDirIndex and bottomDirIndex, and
-                  // refresh the list on the screen.
-                  topDirIndex += numItemsPerPage;
-                  pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-                  bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-                  this.updatePageNumInHeader(pageNum, numPages, false, false);
-                  this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                              listEndRow, false, true);
-               }
-               else
-               {
-                  // Display the current line un-highlighted.
-                  console.gotoxy(1, curpos.y);
-                  this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
-               }
-               selectedDirIndex = nextGrpIndex;
-            }
-            break;
-         case KEY_HOME: // Go to the top message group on the screen
-            if (selectedDirIndex > topDirIndex)
-            {
-               // Display the current line un-highlighted, then adjust
-               // selectedDirIndex.
-               console.gotoxy(1, curpos.y);
-               this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
-               selectedDirIndex = topDirIndex;
-               // Note: curpos.y is set at the start of the while loop.
-            }
-            break;
-         case KEY_END: // Go to the bottom message group on the screen
-            if (selectedDirIndex < bottomDirIndex)
-            {
-               // Display the current line un-highlighted, then adjust
-               // selectedDirIndex.
-               console.gotoxy(1, curpos.y);
-               this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
-               selectedDirIndex = bottomDirIndex;
-               // Note: curpos.y is set at the start of the while loop.
-            }
-            break;
-         case KEY_ENTER: // Select the currently-highlighted sub-board; and we're done.
-            continueChoosingFileDir = false;
-            retObj.fileDirChosen = true;
-            retObj.fileLibIndex = selectedDirIndex;
-            break;
-         case KEY_PAGE_DOWN: // Go to the next page
-            var nextPageTopIndex = topDirIndex + numItemsPerPage;
-            if (nextPageTopIndex < file_area.lib_list[libIndex].dir_list.length)
-            {
-               // Adjust topDirIndex and bottomDirIndex, and
-               // refresh the list on the screen.
-               topDirIndex = nextPageTopIndex;
-               pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-               bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-               this.updatePageNumInHeader(pageNum, numPages, false, false);
-               this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                            listEndRow, false, true);
-               selectedDirIndex = topDirIndex;
-            }
-            break;
-         case KEY_PAGE_UP: // Go to the previous page
-            var prevPageTopIndex = topDirIndex - numItemsPerPage;
-            if (prevPageTopIndex >= 0)
-            {
-               // Adjust topDirIndex and bottomDirIndex, and
-               // refresh the list on the screen.
-               topDirIndex = prevPageTopIndex;
-               pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-               bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-               this.updatePageNumInHeader(pageNum, numPages, false, false);
-               this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                            listEndRow, false, true);
-               selectedDirIndex = topDirIndex;
-            }
-            break;
-         case 'F': // Go to the first page
-            if (topDirIndex > 0)
-            {
-               topDirIndex = 0;
-               pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-               bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-               this.updatePageNumInHeader(pageNum, numPages, false, false);
-               this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                            listEndRow, false, true);
-               selectedDirIndex = 0;
-            }
-            break;
-         case 'L': // Go to the last page
-            if (topDirIndex < topIndexForLastPage)
-            {
-               topDirIndex = topIndexForLastPage;
-               pageNum = calcPageNum(topDirIndex, numItemsPerPage);
-               bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
-               this.updatePageNumInHeader(pageNum, numPages, false, false);
-               this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                            listEndRow, false, true);
-               selectedDirIndex = topIndexForLastPage;
-            }
-            break;
-         case 'Q': // Quit
-            continueChoosingFileDir = false;
-            break;
-         case '?': // Show help
-            this.ShowHelpScreen(true, true);
-            console.pause();
-            // Refresh the screen
-            console.gotoxy(1, 1);
-            this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
-            console.cleartoeol("\1n");
-            this.WriteKeyHelpLine();
-            console.gotoxy(1, 2);
-            printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
-            this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                      listEndRow, false, true);
-            break;
-         default:
-            // If the user entered a numeric digit, then treat it as
-            // the start of the message group number.
-            if (userInput.match(/[0-9]/))
-            {
-               var originalCurpos = curpos;
-
-               // Put the user's input back in the input buffer to
-               // be used for getting the rest of the message number.
-               console.ungetstr(userInput);
-               // Move the cursor to the bottom of the screen and
-               // prompt the user for the message number.
-               console.gotoxy(1, console.screen_rows);
-               console.clearline("\1n");
-               console.print("\1cDir #: \1h");
-               userInput = console.getnum(file_area.lib_list[libIndex].dir_list.length);
-               // If the user made a selection, then set it in the
-               // return object and don't continue the input loop.
-               if (userInput > 0)
-               {
-                  continueChoosingFileDir = false;
-                  retObj.fileDirChosen = true;
-                  retObj.fileLibIndex = userInput - 1;
-               }
-               else
-               {
-                  // The user didn't enter a selection.  Now we need to
-                  // re-draw the screen due to everything being moved
-                  // up one line.
-                  console.gotoxy(1, 1);
-                  this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
-                  console.cleartoeol("\1n");
-                  this.WriteKeyHelpLine();
-                  console.gotoxy(1, 2);
-                  printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
-                  this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
-                                            listEndRow, false, true);
-               }
-            }
-            break;
-      }
-   }
+	var listStartRow = 3+this.areaChangeHdrLines.length;      // The row on the screen where the list will start
+	var listEndRow = console.screen_rows - 1; // Row on screen where list will end
+	var topDirIndex = 0;      // The index of the message group at the top of the list
+	// Figure out the index of the last message group to appear on the screen.
+	var numItemsPerPage = listEndRow - listStartRow + 1;
+	var bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+	// Figure out how many pages are needed to list all the sub-boards.
+	var numPages = Math.ceil(file_area.lib_list[libIndex].dir_list.length / numItemsPerPage);
+	var pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+	// Figure out the top index for the last page.
+	var topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage;
+
+	// If the highlighted row is beyond the current screen, then
+	// go to the appropriate page.
+	if (selectedDirIndex > bottomDirIndex)
+	{
+		var nextPageTopIndex = 0;
+		while (selectedDirIndex > bottomDirIndex)
+		{
+			nextPageTopIndex = topDirIndex + numItemsPerPage;
+			if (nextPageTopIndex < file_area.lib_list[libIndex].dir_list.length)
+			{
+				// Adjust topDirIndex and bottomDirIndex, and
+				// refresh the list on the screen.
+				topDirIndex = nextPageTopIndex;
+				pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+				bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+			}
+			else
+				break;
+		}
 
-   return retObj;
+		// If we didn't find the correct page for some reason, then set the
+		// variables to display page 1 and select the first message group.
+		var foundCorrectPage = ((topDirIndex < file_area.lib_list[libIndex].dir_list.length) &&
+		                        (selectedDirIndex >= topDirIndex) && (selectedDirIndex <= bottomDirIndex));
+		if (!foundCorrectPage)
+		{
+			topDirIndex = 0;
+			pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+			bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+			selectedDirIndex = 0;
+		}
+	}
+
+	// Clear the screen, write the header, help line, and group list header, and output
+	// a screenful of message groups.
+	console.clear("\1n");
+	this.DisplayAreaChgHdr(1);
+	if (this.areaChangeHdrLines.length > 0)
+		console.crlf();
+	this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
+	this.WriteKeyHelpLine();
+
+	var curpos = new Object();
+	curpos.x = 1;
+	curpos.y = 2+this.areaChangeHdrLines.length;
+	console.gotoxy(curpos);
+	printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
+	       this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow, listEndRow,
+	       false, false);
+	// Start of the input loop.
+	var highlightScrenRow = 0; // The row on the screen for the highlighted group
+	var userInput = "";        // Will store a keypress from the user
+	var continueChoosingFileDir = true;
+	while (continueChoosingFileDir)
+	{
+		// Highlight the currently-selected message group
+		highlightScrenRow = listStartRow + (selectedDirIndex - topDirIndex);
+		curpos.y = highlightScrenRow;
+		if ((highlightScrenRow > 0) && (highlightScrenRow < console.screen_rows))
+		{
+			console.gotoxy(1, highlightScrenRow);
+			this.WriteFileLibDirLine(libIndex, selectedDirIndex, true);
+		}
+
+		// Get a key from the user (upper-case) and take action based upon it.
+		userInput = getKeyWithESCChars(K_UPPER | K_NOCRLF);
+		switch (userInput)
+		{
+			case KEY_UP: // Move up one message group in the list
+				if (selectedDirIndex > 0)
+				{
+					// If the previous group index is on the previous page, then
+					// display the previous page.
+					var previousSubIndex = selectedDirIndex - 1;
+					if (previousSubIndex < topDirIndex)
+					{
+						// Adjust topDirIndex and bottomDirIndex, and
+						// refresh the list on the screen.
+						topDirIndex -= numItemsPerPage;
+						pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+						bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+						this.updatePageNumInHeader(pageNum, numPages, false, false);
+						this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+						                         listEndRow, false, true);
+					}
+					else
+					{
+						// Display the current line un-highlighted.
+						console.gotoxy(1, curpos.y);
+						this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
+					}
+					selectedDirIndex = previousSubIndex;
+				}
+				break;
+			case KEY_DOWN: // Move down one message group in the list
+				if (selectedDirIndex < file_area.lib_list[libIndex].dir_list.length - 1)
+				{
+					// If the next group index is on the next page, then display
+					// the next page.
+					var nextGrpIndex = selectedDirIndex + 1;
+					if (nextGrpIndex > bottomDirIndex)
+					{
+						// Adjust topDirIndex and bottomDirIndex, and
+						// refresh the list on the screen.
+						topDirIndex += numItemsPerPage;
+						pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+						bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+						this.updatePageNumInHeader(pageNum, numPages, false, false);
+						this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+						                         listEndRow, false, true);
+					}
+					else
+					{
+						// Display the current line un-highlighted.
+						console.gotoxy(1, curpos.y);
+						this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
+					}
+					selectedDirIndex = nextGrpIndex;
+				}
+				break;
+			case KEY_HOME: // Go to the top message group on the screen
+				if (selectedDirIndex > topDirIndex)
+				{
+					// Display the current line un-highlighted, then adjust
+					// selectedDirIndex.
+					console.gotoxy(1, curpos.y);
+					this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
+					selectedDirIndex = topDirIndex;
+					// Note: curpos.y is set at the start of the while loop.
+				}
+				break;
+			case KEY_END: // Go to the bottom message group on the screen
+				if (selectedDirIndex < bottomDirIndex)
+				{
+					// Display the current line un-highlighted, then adjust
+					// selectedDirIndex.
+					console.gotoxy(1, curpos.y);
+					this.WriteFileLibDirLine(libIndex, selectedDirIndex, false);
+					selectedDirIndex = bottomDirIndex;
+					// Note: curpos.y is set at the start of the while loop.
+				}
+				break;
+			case KEY_ENTER: // Select the currently-highlighted sub-board; and we're done.
+				continueChoosingFileDir = false;
+				retObj.fileDirChosen = true;
+				retObj.fileLibIndex = selectedDirIndex;
+				break;
+			case KEY_PAGE_DOWN: // Go to the next page
+				var nextPageTopIndex = topDirIndex + numItemsPerPage;
+				if (nextPageTopIndex < file_area.lib_list[libIndex].dir_list.length)
+				{
+					// Adjust topDirIndex and bottomDirIndex, and
+					// refresh the list on the screen.
+					topDirIndex = nextPageTopIndex;
+					pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+					bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+					this.updatePageNumInHeader(pageNum, numPages, false, false);
+					this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+					                         listEndRow, false, true);
+					selectedDirIndex = topDirIndex;
+				}
+				break;
+			case KEY_PAGE_UP: // Go to the previous page
+				var prevPageTopIndex = topDirIndex - numItemsPerPage;
+				if (prevPageTopIndex >= 0)
+				{
+					// Adjust topDirIndex and bottomDirIndex, and
+					// refresh the list on the screen.
+					topDirIndex = prevPageTopIndex;
+					pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+					bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+					this.updatePageNumInHeader(pageNum, numPages, false, false);
+					this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+					                         listEndRow, false, true);
+					selectedDirIndex = topDirIndex;
+				}
+				break;
+			case 'F': // Go to the first page
+				if (topDirIndex > 0)
+				{
+					topDirIndex = 0;
+					pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+					bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+					this.updatePageNumInHeader(pageNum, numPages, false, false);
+					this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+					                         listEndRow, false, true);
+					selectedDirIndex = 0;
+				}
+				break;
+			case 'L': // Go to the last page
+				if (topDirIndex < topIndexForLastPage)
+				{
+					topDirIndex = topIndexForLastPage;
+					pageNum = calcPageNum(topDirIndex, numItemsPerPage);
+					bottomDirIndex = getBottommostDirIndex(topDirIndex, numItemsPerPage);
+					this.updatePageNumInHeader(pageNum, numPages, false, false);
+					this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+					                         listEndRow, false, true);
+					selectedDirIndex = topIndexForLastPage;
+				}
+				break;
+			case KEY_ESC: // Quit
+			case 'Q': // Quit
+				continueChoosingFileDir = false;
+				break;
+			case '?': // Show help
+				this.ShowHelpScreen(true, true);
+				console.pause();
+				// Refresh the screen
+				this.DisplayAreaChgHdr(1);
+				console.gotoxy(1, 1+this.areaChangeHdrLines.length);
+				this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
+				console.cleartoeol("\1n");
+				this.WriteKeyHelpLine();
+				console.gotoxy(1, 2+this.areaChangeHdrLines.length);
+				printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
+				this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+				listEndRow, false, true);
+				break;
+			default:
+				// If the user entered a numeric digit, then treat it as
+				// the start of the message group number.
+				if (userInput.match(/[0-9]/))
+				{
+					var originalCurpos = curpos;
+
+					// Put the user's input back in the input buffer to
+					// be used for getting the rest of the message number.
+					console.ungetstr(userInput);
+					// Move the cursor to the bottom of the screen and
+					// prompt the user for the message number.
+					console.gotoxy(1, console.screen_rows);
+					console.clearline("\1n");
+					console.print("\1cDir #: \1h");
+					userInput = console.getnum(file_area.lib_list[libIndex].dir_list.length);
+					// If the user made a selection, then set it in the
+					// return object and don't continue the input loop.
+					if (userInput > 0)
+					{
+						continueChoosingFileDir = false;
+						retObj.fileDirChosen = true;
+						retObj.fileLibIndex = userInput - 1;
+					}
+					else
+					{
+						// The user didn't enter a selection.  Now we need to
+						// re-draw the screen due to everything being moved
+						// up one line.
+						console.gotoxy(1, 1);
+						this.WriteDirListHdr1Line(libIndex, numPages, pageNum);
+						console.cleartoeol("\1n");
+						this.WriteKeyHelpLine();
+						console.gotoxy(1, 2);
+						printf(this.fileDirHdrPrintfStr, "Dir #", "Description", "# Files");
+						this.ListScreenfulOfDirs(libIndex, topDirIndex, listStartRow,
+						                         listEndRow, false, true);
+					}
+				}
+				break;
+		}
+	}
+
+	return retObj;
 }
 
 // Displays a screenful of file libraries (for the lightbar interface).
@@ -1363,12 +1390,12 @@ function DDFileAreaChooser_updatePageNumInHeader(pPageNum, pNumPages, pFileLib,
 
   if (pFileLib)
   {
-    console.gotoxy(30, 1);
+    console.gotoxy(30, 1+this.areaChangeHdrLines.length);
     console.print("\1n" + this.colors.header + pPageNum + " of " + pNumPages + ")   ");
   }
   else
   {
-    console.gotoxy(67, 1);
+    console.gotoxy(67, 1+this.areaChangeHdrLines.length);
     console.print("\1n" + this.colors.fileAreaHdr + pPageNum + " of " + pNumPages + ")   ");
   }
 
@@ -1464,82 +1491,82 @@ function DDFileAreaChooser_writeKeyHelpLine()
 // For the DDFileAreaChooser class: Reads the configuration file.
 function DDFileAreaChooser_ReadConfigFile()
 {
-   // Determine the script's startup directory.
-   // This code is a trick that was created by Deuce, suggested by Rob Swindell
-   // as a way to detect which directory the script was executed in.  I've
-   // shortened the code a little.
-   var startup_path = '.';
-   try { throw dig.dist(dist); } catch(e) { startup_path = e.fileName; }
-   startup_path = backslash(startup_path.replace(/[\/\\][^\/\\]*$/,''));
-
-   // Open the configuration file
-   var cfgFile = new File(startup_path + "DDFileAreaChooser.cfg");
-   if (cfgFile.open("r"))
-   {
-      var settingsMode = "behavior";
-      var fileLine = null;     // A line read from the file
-      var equalsPos = 0;       // Position of a = in the line
-      var commentPos = 0;      // Position of the start of a comment
-      var setting = null;      // A setting name (string)
-      var settingUpper = null; // Upper-case setting name
-      var value = null;        // A value for a setting (string)
-      while (!cfgFile.eof)
-      {
-         // Read the next line from the config file.
-         fileLine = cfgFile.readln(2048);
+	// Open the configuration file
+	var cfgFile = new File(gStartupPath + "DDFileAreaChooser.cfg");
+	if (cfgFile.open("r"))
+	{
+		var settingsMode = "behavior";
+		var fileLine = null;     // A line read from the file
+		var equalsPos = 0;       // Position of a = in the line
+		var commentPos = 0;      // Position of the start of a comment
+		var setting = null;      // A setting name (string)
+		var settingUpper = null; // Upper-case setting name
+		var value = null;        // A value for a setting (string)
+		while (!cfgFile.eof)
+		{
+			// Read the next line from the config file.
+			fileLine = cfgFile.readln(2048);
 
-         // fileLine should be a string, but I've seen some cases
-         // where it isn't, so check its type.
-         if (typeof(fileLine) != "string")
-            continue;
+			// fileLine should be a string, but I've seen some cases
+			// where it isn't, so check its type.
+			if (typeof(fileLine) != "string")
+				continue;
 
-         // If the line starts with with a semicolon (the comment
-         // character) or is blank, then skip it.
-         if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0))
-            continue;
+			// If the line starts with with a semicolon (the comment
+			// character) or is blank, then skip it.
+			if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0))
+				continue;
 
-         // If in the "behavior" section, then set the behavior-related variables.
-         if (fileLine.toUpperCase() == "[BEHAVIOR]")
-         {
-            settingsMode = "behavior";
-            continue;
-         }
-         else if (fileLine.toUpperCase() == "[COLORS]")
-         {
-            settingsMode = "colors";
-            continue;
-         }
+			// If in the "behavior" section, then set the behavior-related variables.
+			if (fileLine.toUpperCase() == "[BEHAVIOR]")
+			{
+				settingsMode = "behavior";
+				continue;
+			}
+			else if (fileLine.toUpperCase() == "[COLORS]")
+			{
+				settingsMode = "colors";
+				continue;
+			}
 
-         // If the line has a semicolon anywhere in it, then remove
-         // everything from the semicolon onward.
-         commentPos = fileLine.indexOf(";");
-         if (commentPos > -1)
-            fileLine = fileLine.substr(0, commentPos);
-
-         // Look for an equals sign, and if found, separate the line
-         // into the setting name (before the =) and the value (after the
-         // equals sign).
-         equalsPos = fileLine.indexOf("=");
-         if (equalsPos > 0)
-         {
-            // Read the setting & value, and trim leading & trailing spaces.
-            setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true);
-            settingUpper = setting.toUpperCase();
-            value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true);
-
-            if (settingsMode == "behavior")
-            {
-               // Set the appropriate value in the settings object.
-               if (settingUpper == "USELIGHTBARINTERFACE")
-                  this.useLightbarInterface = (value.toUpperCase() == "TRUE");
-            }
-            else if (settingsMode == "colors")
-               this.colors[setting] = value;
-         }
-      }
-   
-      cfgFile.close();
-   }
+			// If the line has a semicolon anywhere in it, then remove
+			// everything from the semicolon onward.
+			commentPos = fileLine.indexOf(";");
+			if (commentPos > -1)
+				fileLine = fileLine.substr(0, commentPos);
+
+			// Look for an equals sign, and if found, separate the line
+			// into the setting name (before the =) and the value (after the
+			// equals sign).
+			equalsPos = fileLine.indexOf("=");
+			if (equalsPos > 0)
+			{
+				// Read the setting & value, and trim leading & trailing spaces.
+				setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true);
+				settingUpper = setting.toUpperCase();
+				value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true);
+
+				if (settingsMode == "behavior")
+				{
+					// Set the appropriate value in the settings object.
+					if (settingUpper == "USELIGHTBARINTERFACE")
+						this.useLightbarInterface = (value.toUpperCase() == "TRUE");
+					else if (settingUpper == "AREACHOOSERHDRFILENAMEBASE")
+						this.areaChooserHdrFilenameBase = value;
+					else if (settingUpper == "AREACHOOSERHDRMAXLINES")
+					{
+						var maxNumLines = +value;
+						if (maxNumLines > 0)
+							this.areaChooserHdrMaxLines = maxNumLines;
+					}
+				}
+				else if (settingsMode == "colors")
+					this.colors[setting] = value;
+			}
+		}
+
+		cfgFile.close();
+	}
 }
 
 // Misc. functions
@@ -1684,6 +1711,64 @@ function DDFileAreaChooser_buildFileDirPrintfInfoForLib(pLibIndex)
   }
 }
 
+// For the DDFileAreaChooser class: Displays the area chooser header
+//
+// Parameters:
+//  pStartScreenRow: The row on the screen at which to start displaying the
+//                   header information.  Will be used if the user's terminal
+//                   supports ANSI.
+//  pClearRowsFirst: Optional boolean - Whether or not to clear the rows first.
+//                   Defaults to true.  Only valid if the user's terminal supports
+//                   ANSI.
+function DDFileAreaChooser_DisplayAreaChgHdr(pStartScreenRow, pClearRowsFirst)
+{
+	if (this.areaChangeHdrLines == null)
+		return;
+	if (this.areaChangeHdrLines.length == 0)
+		return;
+
+	// If the user's terminal supports ANSI and pStartScreenRow is a number, then
+	// we can move the cursor and display the header where specified.
+	if (console.term_supports(USER_ANSI) && (typeof(pStartScreenRow) == "number"))
+	{
+		// If specified to clear the rows first, then do so.
+		var screenX = 1;
+		var screenY = pStartScreenRow;
+		var clearRowsFirst = (typeof(pClearRowsFirst) == "boolean" ? pClearRowsFirst : true);
+		if (clearRowsFirst)
+		{
+			console.print("\1n");
+			for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+			{
+				console.gotoxy(screenX, screenY++);
+				console.cleartoeol();
+			}
+		}
+		// Display the header starting on the first column and the given screen row.
+		screenX = 1;
+		screenY = pStartScreenRow;
+		for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+		{
+			console.gotoxy(screenX, screenY++);
+			console.print(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.putmsg(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.cleartoeol("\1n"); // Shouldn't do this, as it resets color attributes
+		}
+	}
+	else
+	{
+		// The user's terminal doesn't support ANSI or pStartScreenRow is not a
+		// number - So just output the header lines.
+		for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+		{
+			console.print(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.putmsg(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.cleartoeol("\1n"); // Shouldn't do this, as it resets color attributes
+			console.crlf();
+		}
+	}
+}
+
 // Removes multiple, leading, and/or trailing spaces
 // The search & replace regular expressions used in this
 // function came from the following URL:
@@ -1823,4 +1908,128 @@ function getKeyWithESCChars(pGetKeyMode)
    }
 
    return userInput;
+}
+
+// Loads a text file (an .ans or .asc) into an array.  This will first look for
+// an .ans version, and if exists, convert to Synchronet colors before loading
+// it.  If an .ans doesn't exist, this will look for an .asc version.
+//
+// Parameters:
+//  pFilenameBase: The filename without the extension
+//  pMaxNumLines: Optional - The maximum number of lines to load from the text file
+//
+// Return value: An array containing the lines from the text file
+function loadTextFileIntoArray(pFilenameBase, pMaxNumLines)
+{
+	if (typeof(pFilenameBase) != "string")
+		return new Array();
+
+	var maxNumLines = (typeof(pMaxNumLines) == "number" ? pMaxNumLines : -1);
+
+	var txtFileLines = new Array();
+	// See if there is a header file that is made for the user's terminal
+	// width (areaChgHeader-<width>.ans/asc).  If not, then just go with
+	// msgHeader.ans/asc.
+	var txtFileExists = true;
+	var txtFilenameFullPath = gStartupPath + pFilenameBase;
+	var txtFileFilename = "";
+	if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".ans"))
+		txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".ans";
+	else if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".asc"))
+		txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".asc";
+	else if (file_exists(txtFilenameFullPath + ".ans"))
+		txtFileFilename = txtFilenameFullPath + ".ans";
+	else if (file_exists(txtFilenameFullPath + ".asc"))
+		txtFileFilename = txtFilenameFullPath + ".asc";
+	else
+		txtFileExists = false;
+	if (txtFileExists)
+	{
+		var syncConvertedHdrFilename = txtFileFilename;
+		// If the user's console doesn't support ANSI and the header file is ANSI,
+		// then convert it to Synchronet attribute codes and read that file instead.
+		if (!console.term_supports(USER_ANSI) && (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS"))
+		{
+			syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
+			if (!file_exists(syncConvertedHdrFilename))
+			{
+				if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
+				{
+					var filenameBase = txtFileFilename.substr(0, dotIdx);
+					var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+								+ syncConvertedHdrFilename + "\"";
+					// Note: Both system.exec(cmdLine) and
+					// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
+					// execute the command, but system.exec() seems noticeably faster.
+					system.exec(cmdLine);
+				}
+				else
+					syncConvertedHdrFilename = txtFileFilename;
+			}
+		}
+		/*
+		// If the header file is ANSI, then convert it to Synchronet attribute
+		// codes and read that file instead.  This is done so that this script can
+		// accurately get the file line lengths using console.strlen().
+		var syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
+		if (!file_exists(syncConvertedHdrFilename))
+		{
+			if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
+			{
+				var filenameBase = txtFileFilename.substr(0, dotIdx);
+				var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+				            + syncConvertedHdrFilename + "\"";
+				// Note: Both system.exec(cmdLine) and
+				// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
+				// execute the command, but system.exec() seems noticeably faster.
+				system.exec(cmdLine);
+			}
+			else
+				syncConvertedHdrFilename = txtFileFilename;
+		}
+		*/
+		// Read the header file into txtFileLines
+		var hdrFile = new File(syncConvertedHdrFilename);
+		if (hdrFile.open("r"))
+		{
+			var fileLine = null;
+			while (!hdrFile.eof)
+			{
+				// Read the next line from the header file.
+				fileLine = hdrFile.readln(2048);
+				// fileLine should be a string, but I've seen some cases
+				// where it isn't, so check its type.
+				if (typeof(fileLine) != "string")
+					continue;
+
+				// Make sure the line isn't longer than the user's terminal
+				//if (fileLine.length > console.screen_columns)
+				//   fileLine = fileLine.substr(0, console.screen_columns);
+				txtFileLines.push(fileLine);
+
+				// If the header array now has the maximum number of lines, then
+				// stop reading the header file.
+				if (txtFileLines.length == maxNumLines)
+					break;
+			}
+			hdrFile.close();
+		}
+	}
+	return txtFileLines;
+}
+
+// Returns the portion (if any) of a string after the period.
+//
+// Parameters:
+//  pStr: The string to extract from
+//
+// Return value: The portion of the string after the dot, if there is one.  If
+//               not, then an empty string will be returned.
+function getStrAfterPeriod(pStr)
+{
+	var strAfterPeriod = "";
+	var dotIdx = pStr.lastIndexOf(".");
+	if (dotIdx > -1)
+		strAfterPeriod = pStr.substr(dotIdx+1);
+	return strAfterPeriod;
 }
\ No newline at end of file
diff --git a/xtrn/DDAreaChoosers/DDMsgAreaChooser.cfg b/xtrn/DDAreaChoosers/DDMsgAreaChooser.cfg
index 7e5cd45fe00f5dfb2be5d5d6f78af43fd0b96a6d..1c510a59a5ce5838dd9c673c9fba212088972eba 100644
--- a/xtrn/DDAreaChoosers/DDMsgAreaChooser.cfg
+++ b/xtrn/DDAreaChoosers/DDMsgAreaChooser.cfg
@@ -4,6 +4,8 @@ useLightbarInterface=true
 ; will be the messaeg import date.  If false, the date will represent
 ; the timestamp in the message.
 showImportDates=true
+areaChooserHdrFilenameBase=msgAreaChgHeader
+areaChooserHdrMaxLines=5
 
 [COLORS]
 ; Area number
diff --git a/xtrn/DDAreaChoosers/DDMsgAreaChooser.js b/xtrn/DDAreaChoosers/DDMsgAreaChooser.js
index 0e194726dca5fcce1ca9ac2217156e1aac16e727..c40dd6a59a46574c3d8c2c7fb2b32c585ace7635 100644
--- a/xtrn/DDAreaChoosers/DDMsgAreaChooser.js
+++ b/xtrn/DDAreaChoosers/DDMsgAreaChooser.js
@@ -50,6 +50,8 @@
  *                                  line argument now specifies whether or not to
  *                                  allow choosing the message group, and it defaults
  *                                  to true.
+ * 2016-02-12 Eric Oulashin 1.10Beta Started working on adding the ability to display
+ *                                  a header ANSI/ASCII file above the list.
 */
 
 /* Command-line arguments:
@@ -78,8 +80,8 @@ if (system.version_num < 31400)
 }
 
 // Version & date variables
-var DD_MSG_AREA_CHOOSER_VERSION = "1.09";
-var DD_MSG_AREA_CHOOSER_VER_DATE = "2016-01-17";
+var DD_MSG_AREA_CHOOSER_VERSION = "1.10 Beta 2";
+var DD_MSG_AREA_CHOOSER_VER_DATE = "2016-02-14";
 
 // Keyboard input key codes
 var CTRL_M = "\x0d";
@@ -94,6 +96,14 @@ var KEY_PAGE_DOWN = "\1PgDn";
 var UP_ARROW = ascii(24);
 var DOWN_ARROW = ascii(25);
 
+// Determine the script's startup directory.
+// This code is a trick that was created by Deuce, suggested by Rob Swindell
+// as a way to detect which directory the script was executed in.  I've
+// shortened the code a little.
+var gStartupPath = '.';
+try { throw dig.dist(dist); } catch(e) { gStartupPath = e.fileName; }
+gStartupPath = backslash(gStartupPath.replace(/[\/\\][^\/\\]*$/,''));
+
 // 1st command-line argument: Whether or not to choose a message group first (if
 // false, then only choose a sub-board within the user's current group).  This
 // can be true or false.
@@ -175,6 +185,10 @@ function DDMsgAreaChooser()
 	this.msgGrpDescLen = console.screen_columns - this.areaNumLen -
 	this.numItemsLen - 5;
 
+	// Filename base of a header to display above the area list
+	this.areaChooserHdrFilenameBase = "msgAreaChgHeader";
+	this.areaChooserHdrMaxLines = 5;
+
 	// Set the function pointers for the object
 	this.ReadConfigFile = DDMsgAreaChooser_ReadConfigFile;
 	this.WriteKeyHelpLine = DDMsgAreaChooser_writeKeyHelpLine;
@@ -198,6 +212,7 @@ function DDMsgAreaChooser()
 	// Function to build the sub-board printf information for a message
 	// group
 	this.BuildSubBoardPrintfInfoForGrp = DDMsgAreaChooser_buildSubBoardPrintfInfoForGrp;
+	this.DisplayAreaChgHdr = DDMsgAreaChooser_DisplayAreaChgHdr;
 
 	// Read the settings from the config file.
 	this.ReadConfigFile();
@@ -287,6 +302,10 @@ function DDMsgAreaChooser()
 	// on the fly the first time the user lists sub-boards for a message
 	// group.
 	this.subBoardListPrintfInfo = new Array();
+
+	// areaChangeHdrLines is an array of text lines to use as a header to display
+	// above the message area changer lists.
+	this.areaChangeHdrLines = loadTextFileIntoArray(this.areaChooserHdrFilenameBase, this.areaChooserHdrMaxLines);
 }
 
 // For the DDMsgAreaChooser class: Writes the line of key help at the bottom
@@ -399,7 +418,7 @@ function DDMsgAreaChooser_selectMsgArea_Lightbar(pChooseGroup)
 		if ((bbs.curgrp != null) && (typeof(bbs.curgrp) == "number"))
 			selectedGrpIndex = bbs.curgrp;
 
-		var listStartRow = 2;      // The row on the screen where the list will start
+		var listStartRow = 2 + this.areaChangeHdrLines.length; // The row on the screen where the list will start
 		var listEndRow = console.screen_rows - 1; // Row on screen where list will end
 		var topMsgGrpIndex = 0;    // The index of the message group at the top of the list
 
@@ -442,14 +461,15 @@ function DDMsgAreaChooser_selectMsgArea_Lightbar(pChooseGroup)
 			}
 		}
 
-		// Clear the screen, write the help line and group list header, and output
+		// Clear the screen, write the header, help line, and group list header, and output
 		// a screenful of message groups.
 		console.clear("\1n");
+		this.DisplayAreaChgHdr(1);
 		this.WriteKeyHelpLine();
 
 		var curpos = new Object();
 		curpos.x = 1;
-		curpos.y = 1;
+		curpos.y = 1 + this.areaChangeHdrLines.length;
 		console.gotoxy(curpos);
 		var pageNum = calcPageNum(topMsgGrpIndex, numItemsPerPage);
 		this.WriteGrpListHdrLine(numPages, pageNum);
@@ -557,7 +577,8 @@ function DDMsgAreaChooser_selectMsgArea_Lightbar(pChooseGroup)
 				{
 					// A sub-board was not chosen, so we'll have to re-draw
 					// the header and list of message groups.
-					console.gotoxy(1, 1);
+					this.DisplayAreaChgHdr(1);
+					console.gotoxy(1, 1+this.areaChangeHdrLines.length);
 					this.WriteGrpListHdrLine(numPages, pageNum);
 					this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
 				}
@@ -620,7 +641,8 @@ function DDMsgAreaChooser_selectMsgArea_Lightbar(pChooseGroup)
 				console.pause();
 				// Refresh the screen
 				this.WriteKeyHelpLine();
-				console.gotoxy(1, 1);
+				this.DisplayAreaChgHdr(1);
+				console.gotoxy(1, 1+this.areaChangeHdrLines.length);
 				this.WriteGrpListHdrLine(numPages, pageNum);
 				this.ListScreenfulOfMsgGrps(topMsgGrpIndex, listStartRow, listEndRow, false, true);
 				break;
@@ -775,7 +797,7 @@ function DDMsgAreaChooser_selectSubBoard_Lightbar(pGrpIndex, pMarkIndex)
       }
    }
 
-   var listStartRow = 3;      // The row on the screen where the list will start
+   var listStartRow = 3+this.areaChangeHdrLines.length; // The row on the screen where the list will start
    var listEndRow = console.screen_rows - 1; // Row on screen where list will end
    var topSubIndex = 0;      // The index of the message group at the top of the list
    // Figure out the index of the last message group to appear on the screen.
@@ -818,16 +840,19 @@ function DDMsgAreaChooser_selectSubBoard_Lightbar(pGrpIndex, pMarkIndex)
       }
    }
 
-   // Clear the screen, write the help line and group list header, and output
+   // Clear the screen, write the header, help line, and group list header, and output
    // a screenful of message groups.
    console.clear("\1n");
+   this.DisplayAreaChgHdr(1); // Don't need to since it should already be drawn
+   if (this.areaChangeHdrLines.length > 0)
+	   console.crlf();
    var pageNum = calcPageNum(topSubIndex, numItemsPerPage);
    this.WriteSubBrdListHdr1Line(grpIndex, numPages, pageNum);
    this.WriteKeyHelpLine();
 
    var curpos = new Object();
    curpos.x = 1;
-   curpos.y = 2;
+   curpos.y = 2+this.areaChangeHdrLines.length;
    console.gotoxy(curpos);
    printf(this.subBoardListHdrPrintfStr, "Sub #", "Name", "# Posts", "Latest date & time");
    this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, listStartRow, listEndRow,
@@ -991,11 +1016,12 @@ function DDMsgAreaChooser_selectSubBoard_Lightbar(pGrpIndex, pMarkIndex)
             this.ShowHelpScreen(true, true);
             console.pause();
             // Refresh the screen
-            console.gotoxy(1, 1);
+			this.DisplayAreaChgHdr(1);
+            console.gotoxy(1, 1+this.areaChangeHdrLines.length);
             this.WriteSubBrdListHdr1Line(grpIndex, numPages, pageNum);
             console.cleartoeol("\1n");
             this.WriteKeyHelpLine();
-            console.gotoxy(1, 2);
+            console.gotoxy(1, 2+this.areaChangeHdrLines.length);
             printf(this.subBoardListHdrPrintfStr, "Sub #", "Name", "# Posts",
                    "Latest date & time");
             this.ListScreenfulOfSubBrds(grpIndex, topSubIndex, listStartRow,
@@ -1080,6 +1106,9 @@ function DDMsgAreaChooser_selectMsgArea_Traditional(pChooseGroup)
 			bbs.command_str = "";
 
 			console.clear("\1n");
+			this.DisplayAreaChgHdr(1);
+			if (this.areaChangeHdrLines.length > 0)
+				console.crlf();
 			this.ListMsgGrps();
 			console.crlf();
 			console.print("\1n\1b\1h� \1n\1cWhich, \1hQ\1n\1cuit, or [\1h" + +(bbs.curgrp+1) + "\1n\1c]: \1h");
@@ -1149,6 +1178,9 @@ function DDMsgAreaChooser_selectSubBoard_Traditional(pGrpIdx, pDefaultSubBoardId
 	retObj.subBoardChosen = false;
 	retObj.subBoardIndex = -1;
 
+	this.DisplayAreaChgHdr(1);
+	if (this.areaChangeHdrLines.length > 0)
+		console.crlf();
 	this.ListSubBoardsInMsgGroup(pGrpIdx, pDefaultSubBoardIdx);
 	console.crlf();
 	console.print("\1n\1b\1h� \1n\1cWhich, \1hQ\1n\1cuit, or [\1h" + +(pDefaultSubBoardIdx+1) + "\1n\1c]: \1h");
@@ -1491,12 +1523,12 @@ function DDMsgAreaChooser_updatePageNumInHeader(pPageNum, pNumPages, pGroup, pRe
 
   if (pGroup)
   {
-    console.gotoxy(29, 1);
+    console.gotoxy(29, 1+this.areaChangeHdrLines.length);
     console.print("\1n" + this.colors.header + pPageNum + " of " + pNumPages + ")   ");
   }
   else
   {
-    console.gotoxy(51, 1);
+    console.gotoxy(51, 1+this.areaChangeHdrLines.length);
     console.print("\1n" + this.colors.subBoardHeader + pPageNum + " of " + pNumPages + ")   ");
   }
 
@@ -1647,84 +1679,92 @@ function DDMsgAreaChooser_writeMsgSubBrdLine(pGrpIndex, pSubIndex, pHighlight)
 // For the DDMsgAreaChooser class: Reads the configuration file.
 function DDMsgAreaChooser_ReadConfigFile()
 {
-   // Determine the script's startup directory.
-   // This code is a trick that was created by Deuce, suggested by Rob Swindell
-   // as a way to detect which directory the script was executed in.  I've
-   // shortened the code a little.
-   var startup_path = '.';
-   try { throw dig.dist(dist); } catch(e) { startup_path = e.fileName; }
-   startup_path = backslash(startup_path.replace(/[\/\\][^\/\\]*$/,''));
-
-   // Open the configuration file
-   var cfgFile = new File(startup_path + "DDMsgAreaChooser.cfg");
-   if (cfgFile.open("r"))
-   {
-      var settingsMode = "behavior";
-      var fileLine = null;     // A line read from the file
-      var equalsPos = 0;       // Position of a = in the line
-      var commentPos = 0;      // Position of the start of a comment
-      var setting = null;      // A setting name (string)
-      var settingUpper = null; // Upper-case setting name
-      var value = null;        // A value for a setting (string)
-      while (!cfgFile.eof)
-      {
-         // Read the next line from the config file.
-         fileLine = cfgFile.readln(2048);
+	// Determine the script's startup directory.
+	// This code is a trick that was created by Deuce, suggested by Rob Swindell
+	// as a way to detect which directory the script was executed in.  I've
+	// shortened the code a little.
+	var startup_path = '.';
+	try { throw dig.dist(dist); } catch(e) { startup_path = e.fileName; }
+	startup_path = backslash(startup_path.replace(/[\/\\][^\/\\]*$/,''));
+
+	// Open the configuration file
+	var cfgFile = new File(startup_path + "DDMsgAreaChooser.cfg");
+	if (cfgFile.open("r"))
+	{
+		var settingsMode = "behavior";
+		var fileLine = null;     // A line read from the file
+		var equalsPos = 0;       // Position of a = in the line
+		var commentPos = 0;      // Position of the start of a comment
+		var setting = null;      // A setting name (string)
+		var settingUpper = null; // Upper-case setting name
+		var value = null;        // A value for a setting (string)
+		while (!cfgFile.eof)
+		{
+			// Read the next line from the config file.
+			fileLine = cfgFile.readln(2048);
 
-         // fileLine should be a string, but I've seen some cases
-         // where it isn't, so check its type.
-         if (typeof(fileLine) != "string")
-            continue;
+			// fileLine should be a string, but I've seen some cases
+			// where it isn't, so check its type.
+			if (typeof(fileLine) != "string")
+				continue;
 
-         // If the line starts with with a semicolon (the comment
-         // character) or is blank, then skip it.
-         if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0))
-            continue;
+			// If the line starts with with a semicolon (the comment
+			// character) or is blank, then skip it.
+			if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0))
+				continue;
 
-         // If in the "behavior" section, then set the behavior-related variables.
-         if (fileLine.toUpperCase() == "[BEHAVIOR]")
-         {
-            settingsMode = "behavior";
-            continue;
-         }
-         else if (fileLine.toUpperCase() == "[COLORS]")
-         {
-            settingsMode = "colors";
-            continue;
-         }
+			// If in the "behavior" section, then set the behavior-related variables.
+			if (fileLine.toUpperCase() == "[BEHAVIOR]")
+			{
+				settingsMode = "behavior";
+				continue;
+			}
+			else if (fileLine.toUpperCase() == "[COLORS]")
+			{
+				settingsMode = "colors";
+				continue;
+			}
 
-         // If the line has a semicolon anywhere in it, then remove
-         // everything from the semicolon onward.
-         commentPos = fileLine.indexOf(";");
-         if (commentPos > -1)
-            fileLine = fileLine.substr(0, commentPos);
-
-         // Look for an equals sign, and if found, separate the line
-         // into the setting name (before the =) and the value (after the
-         // equals sign).
-         equalsPos = fileLine.indexOf("=");
-         if (equalsPos > 0)
-         {
-            // Read the setting & value, and trim leading & trailing spaces.
-            setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true);
-            settingUpper = setting.toUpperCase();
-            value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true);
+			// If the line has a semicolon anywhere in it, then remove
+			// everything from the semicolon onward.
+			commentPos = fileLine.indexOf(";");
+			if (commentPos > -1)
+				fileLine = fileLine.substr(0, commentPos);
+
+			// Look for an equals sign, and if found, separate the line
+			// into the setting name (before the =) and the value (after the
+			// equals sign).
+			equalsPos = fileLine.indexOf("=");
+			if (equalsPos > 0)
+			{
+				// Read the setting & value, and trim leading & trailing spaces.
+				setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true);
+				settingUpper = setting.toUpperCase();
+				value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true);
 
-            if (settingsMode == "behavior")
-            {
-               // Set the appropriate value in the settings object.
-               if (settingUpper == "USELIGHTBARINTERFACE")
-                  this.useLightbarInterface = (value.toUpperCase() == "TRUE");
-               else if (settingUpper == "SHOWIMPORTDATES")
-                  this.showImportDates = (value.toUpperCase() == "TRUE");
-            }
-            else if (settingsMode == "colors")
-               this.colors[setting] = value;
-         }
-      }
-   
-      cfgFile.close();
-   }
+				if (settingsMode == "behavior")
+				{
+					// Set the appropriate value in the settings object.
+					if (settingUpper == "USELIGHTBARINTERFACE")
+						this.useLightbarInterface = (value.toUpperCase() == "TRUE");
+					else if (settingUpper == "SHOWIMPORTDATES")
+						this.showImportDates = (value.toUpperCase() == "TRUE");
+					else if (settingUpper == "AREACHOOSERHDRFILENAMEBASE")
+						this.areaChooserHdrFilenameBase = value;
+					else if (settingUpper == "AREACHOOSERHDRMAXLINES")
+					{
+						var maxNumLines = +value;
+						if (maxNumLines > 0)
+							this.areaChooserHdrMaxLines = maxNumLines;
+					}
+				}
+				else if (settingsMode == "colors")
+					this.colors[setting] = value;
+			}
+		}
+
+		cfgFile.close();
+	}
 }
 
 // For the DDMsgAreaChooser class: Shows the help screen
@@ -1785,10 +1825,10 @@ function DDMsgAreaChooser_showHelpScreen(pLightbar, pClearScreen)
 	console.crlf();
 }
 
-// Builds sub-board printf format information for a message group.
-// The widths of the description & # messages columns are calculated
-// based on the greatest number of messages in a sub-board for the
-// message group.
+// For the DDMsgAreaChooser class: Builds sub-board printf format information
+// for a message group.  The widths of the description & # messages columns
+// are calculated based on the greatest number of messages in a sub-board for
+// the message group.
 //
 // Parameters:
 //  pGrpIndex: The index of the message group
@@ -1837,6 +1877,64 @@ function DDMsgAreaChooser_buildSubBoardPrintfInfoForGrp(pGrpIndex)
    }
 }
 
+// For the DDMsgAreaChooser class: Displays the area chooser header
+//
+// Parameters:
+//  pStartScreenRow: The row on the screen at which to start displaying the
+//                   header information.  Will be used if the user's terminal
+//                   supports ANSI.
+//  pClearRowsFirst: Optional boolean - Whether or not to clear the rows first.
+//                   Defaults to true.  Only valid if the user's terminal supports
+//                   ANSI.
+function DDMsgAreaChooser_DisplayAreaChgHdr(pStartScreenRow, pClearRowsFirst)
+{
+	if (this.areaChangeHdrLines == null)
+		return;
+	if (this.areaChangeHdrLines.length == 0)
+		return;
+
+	// If the user's terminal supports ANSI and pStartScreenRow is a number, then
+	// we can move the cursor and display the header where specified.
+	if (console.term_supports(USER_ANSI) && (typeof(pStartScreenRow) == "number"))
+	{
+		// If specified to clear the rows first, then do so.
+		var screenX = 1;
+		var screenY = pStartScreenRow;
+		var clearRowsFirst = (typeof(pClearRowsFirst) == "boolean" ? pClearRowsFirst : true);
+		if (clearRowsFirst)
+		{
+			console.print("\1n");
+			for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+			{
+				console.gotoxy(screenX, screenY++);
+				console.cleartoeol();
+			}
+		}
+		// Display the header starting on the first column and the given screen row.
+		screenX = 1;
+		screenY = pStartScreenRow;
+		for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+		{
+			console.gotoxy(screenX, screenY++);
+			console.print(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.putmsg(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.cleartoeol("\1n"); // Shouldn't do this, as it resets color attributes
+		}
+	}
+	else
+	{
+		// The user's terminal doesn't support ANSI or pStartScreenRow is not a
+		// number - So just output the header lines.
+		for (var hdrFileIdx = 0; hdrFileIdx < this.areaChangeHdrLines.length; ++hdrFileIdx)
+		{
+			console.print(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.putmsg(this.areaChangeHdrLines[hdrFileIdx]);
+			//console.cleartoeol("\1n"); // Shouldn't do this, as it resets color attributes
+			console.crlf();
+		}
+	}
+}
+
 // Removes multiple, leading, and/or trailing spaces.
 // The search & replace regular expressions used in this
 // function came from the following URL:
@@ -1932,45 +2030,173 @@ function getGreatestNumMsgs(pGrpIndex)
 // Return value: The user's keypress
 function getKeyWithESCChars(pGetKeyMode)
 {
-   var getKeyMode = K_NONE;
-   if (typeof(pGetKeyMode) == "number")
-      getKeyMode = pGetKeyMode;
-
-   var userInput = console.getkey(getKeyMode);
-   if (userInput == KEY_ESC) {
-      switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-         case '[':
-            switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-               case 'V':
-                  userInput = KEY_PAGE_UP;
-                  break;
-               case 'U':
-                  userInput = KEY_PAGE_DOWN;
-                  break;
-           }
-           break;
-         case 'O':
-           switch (console.inkey(K_NOECHO|K_NOSPIN, 2)) {
-              case 'P':
-                 userInput = "\1F1";
-                 break;
-              case 'Q':
-                 userInput = "\1F2";
-                 break;
-              case 'R':
-                 userInput = "\1F3";
-                 break;
-              case 'S':
-                 userInput = "\1F4";
-                 break;
-              case 't':
-                 userInput = "\1F5";
-                 break;
-           }
-         default:
-           break;
-      }
-   }
+	var getKeyMode = K_NONE;
+	if (typeof(pGetKeyMode) == "number")
+		getKeyMode = pGetKeyMode;
+
+	var userInput = console.getkey(getKeyMode);
+	if (userInput == KEY_ESC)
+	{
+		switch (console.inkey(K_NOECHO|K_NOSPIN, 2))
+		{
+			case '[':
+				switch (console.inkey(K_NOECHO|K_NOSPIN, 2))
+				{
+					case 'V':
+						userInput = KEY_PAGE_UP;
+						break;
+					case 'U':
+						userInput = KEY_PAGE_DOWN;
+						break;
+				}
+				break;
+			case 'O':
+				switch (console.inkey(K_NOECHO|K_NOSPIN, 2))
+				{
+					case 'P':
+						userInput = "\1F1";
+						break;
+					case 'Q':
+						userInput = "\1F2";
+						break;
+					case 'R':
+						userInput = "\1F3";
+						break;
+					case 'S':
+						userInput = "\1F4";
+						break;
+					case 't':
+						userInput = "\1F5";
+						break;
+				}
+			default:
+				break;
+		}
+	}
+
+	return userInput;
+}
+
+// Loads a text file (an .ans or .asc) into an array.  This will first look for
+// an .ans version, and if exists, convert to Synchronet colors before loading
+// it.  If an .ans doesn't exist, this will look for an .asc version.
+//
+// Parameters:
+//  pFilenameBase: The filename without the extension
+//  pMaxNumLines: Optional - The maximum number of lines to load from the text file
+//
+// Return value: An array containing the lines from the text file
+function loadTextFileIntoArray(pFilenameBase, pMaxNumLines)
+{
+	if (typeof(pFilenameBase) != "string")
+		return new Array();
+
+	var maxNumLines = (typeof(pMaxNumLines) == "number" ? pMaxNumLines : -1);
+
+	var txtFileLines = new Array();
+	// See if there is a header file that is made for the user's terminal
+	// width (areaChgHeader-<width>.ans/asc).  If not, then just go with
+	// msgHeader.ans/asc.
+	var txtFileExists = true;
+	var txtFilenameFullPath = gStartupPath + pFilenameBase;
+	var txtFileFilename = "";
+	if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".ans"))
+		txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".ans";
+	else if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".asc"))
+		txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".asc";
+	else if (file_exists(txtFilenameFullPath + ".ans"))
+		txtFileFilename = txtFilenameFullPath + ".ans";
+	else if (file_exists(txtFilenameFullPath + ".asc"))
+		txtFileFilename = txtFilenameFullPath + ".asc";
+	else
+		txtFileExists = false;
+	if (txtFileExists)
+	{
+		var syncConvertedHdrFilename = txtFileFilename;
+		// If the user's console doesn't support ANSI and the header file is ANSI,
+		// then convert it to Synchronet attribute codes and read that file instead.
+		if (!console.term_supports(USER_ANSI) && (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS"))
+		{
+			syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
+			if (!file_exists(syncConvertedHdrFilename))
+			{
+				if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
+				{
+					var filenameBase = txtFileFilename.substr(0, dotIdx);
+					var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+								+ syncConvertedHdrFilename + "\"";
+					// Note: Both system.exec(cmdLine) and
+					// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
+					// execute the command, but system.exec() seems noticeably faster.
+					system.exec(cmdLine);
+				}
+				else
+					syncConvertedHdrFilename = txtFileFilename;
+			}
+		}
+		/*
+		// If the header file is ANSI, then convert it to Synchronet attribute
+		// codes and read that file instead.  This is done so that this script can
+		// accurately get the file line lengths using console.strlen().
+		var syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
+		if (!file_exists(syncConvertedHdrFilename))
+		{
+			if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
+			{
+				var filenameBase = txtFileFilename.substr(0, dotIdx);
+				var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+				            + syncConvertedHdrFilename + "\"";
+				// Note: Both system.exec(cmdLine) and
+				// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
+				// execute the command, but system.exec() seems noticeably faster.
+				system.exec(cmdLine);
+			}
+			else
+				syncConvertedHdrFilename = txtFileFilename;
+		}
+		*/
+		// Read the header file into txtFileLines
+		var hdrFile = new File(syncConvertedHdrFilename);
+		if (hdrFile.open("r"))
+		{
+			var fileLine = null;
+			while (!hdrFile.eof)
+			{
+				// Read the next line from the header file.
+				fileLine = hdrFile.readln(2048);
+				// fileLine should be a string, but I've seen some cases
+				// where it isn't, so check its type.
+				if (typeof(fileLine) != "string")
+					continue;
+
+				// Make sure the line isn't longer than the user's terminal
+				//if (fileLine.length > console.screen_columns)
+				//   fileLine = fileLine.substr(0, console.screen_columns);
+				txtFileLines.push(fileLine);
+
+				// If the header array now has the maximum number of lines, then
+				// stop reading the header file.
+				if (txtFileLines.length == maxNumLines)
+					break;
+			}
+			hdrFile.close();
+		}
+	}
+	return txtFileLines;
+}
 
-   return userInput;
+// Returns the portion (if any) of a string after the period.
+//
+// Parameters:
+//  pStr: The string to extract from
+//
+// Return value: The portion of the string after the dot, if there is one.  If
+//               not, then an empty string will be returned.
+function getStrAfterPeriod(pStr)
+{
+	var strAfterPeriod = "";
+	var dotIdx = pStr.lastIndexOf(".");
+	if (dotIdx > -1)
+		strAfterPeriod = pStr.substr(dotIdx+1);
+	return strAfterPeriod;
 }
\ No newline at end of file
diff --git a/xtrn/DDAreaChoosers/Read Me.txt b/xtrn/DDAreaChoosers/Read Me.txt
index 74c647e9fe2162b751948733d1111d56b8bf0b8e..42de386d79cd7cc0778925ea1b9d2a2d062723c7 100644
--- a/xtrn/DDAreaChoosers/Read Me.txt	
+++ b/xtrn/DDAreaChoosers/Read Me.txt	
@@ -1,6 +1,6 @@
                      Digital Distortion Area Choosers
-                              Version 1.09
-                        Release date: 2016-01-17
+                              Version 1.10
+                        Release date: 2016-??-??
 
                                   by
 
@@ -21,7 +21,6 @@ Contents
 4. Configuration file
 5. DDMsgAreaChooser class: Properties & methods
 6. DDFileAreaChooser class: Properties & methods
-7. Revision History
 
 
 1. Disclaimer
@@ -214,6 +213,23 @@ showImportDates                       true/false: Whether or not to show the
                                       in the latest date & time column in the
                                       sub-board lists.
 
+areaChooserHdrFilenameBase            The filename to use (without the
+                                      extension) for a header to display above
+                                      the message area chooser list.  For
+                                      example, if areaChgHeader is specified,
+                                      then the chooser will look for
+                                      areaChgHeader.ans if it exists, and if
+                                      not, the chooser will look for
+                                      areaChgHeader.asc.  Additionally, you
+                                      can have multiple header files for
+                                      different terminal widths; fpr example,
+                                      areaChgHeader-80.ans for an 80-column
+                                      terminal, areaChgHeader-140.ans for a
+                                      140-column terminal, etc.
+
+areaChooserHdrMaxLines                The maximum number of lines to use from
+                                      the message area chooser header file
+
 Colors section: Message area chooser
 ------------------------------------
 Color setting                        Description
@@ -281,6 +297,23 @@ Setting                               Description
 useLightbarInterface                  true/false: Whether or not to use a
                                       lightbar user interface.
 
+areaChooserHdrFilenameBase            The filename to use (without the
+                                      extension) for a header to display above
+                                      the file area chooser list.  For example,
+                                      if areaChgHeader is specified, then the
+                                      chooser will look for areaChgHeader.ans
+                                      if it exists, and if not, the chooser
+                                      will look for areaChgHeader.asc.
+                                      Additionally, you can have multiple
+                                      header files for different terminal
+                                      widths; fpr example, areaChgHeader-80.ans
+                                      for an 80-column terminal,
+                                      areaChgHeader-140.ans for a 140-column
+                                      terminal, etc.
+
+areaChooserHdrMaxLines                The maximum number of lines to use from
+                                      the message area chooser header file
+
 Colors section: File area chooser
 ------------------------------------
 Color setting                        Description
@@ -414,53 +447,3 @@ ListDirsInFileLib(pLibIndex,          Lists the directories in the user's
                                       specify the index of the file library
                                       and the index of the directory to mark
                                       with the "chosen" character.
-7. Revision History
-===================
-Version  Date         Description
--------  ----         -----------
-1.09     2016-01-17   Added a command-line parameter to let the user choose a
-                      message sub-board only within their current message
-                      group, or file directory only within their current file
-                      library.
-1.08     2015-04-19   Added customizable color settings for the key help text
-                      line displayed at the bottom of the screen in lightbar
-                      mode.  Also, updated to allow the PageUp and PageDown
-                      keys to be used instead of the P and N keys to go to the
-                      previous & next pages in lightbar mode.
-1.07     2014-12-22   Message area chooser:
-                      Bug fix: Made this.colors.subBoardHeader apply to the
-                      whole line rather than just the page number.
-                      Bug fix: The initial display of the page number is now
-                      correct (previously, it would start out saying page 1,
-                      even if on another page).
-                      Documentation & example configuration files:
-                      Added the color options subBoardHeader (for the message
-                      area chooser) and fileAreaHdr (for the file area chooser)
-                      to the documentation and example configuration files.
-1.06     2014-09-14   Bug fix: Updated the lightbar highlight format string to
-                      include a normal attribute at the end to avoid the
-                      highlight color to be used when clearing the screen,
-                      etc.  Bug reported by Psi-Jack.
-1.05     2013-05-10   Bug fix in the file area chooser: When listing
-                      directories in a file group, it would sometimes
-                      crash due to an incorrect array index used, and
-                      the array was not set up.  Those have been fixed.
-1.04     2013-05-04   Updated to properly format message sub-boards and
-                      file directories with more than 9999 entries.  The
-                      formatting is now dynamically adjusted depending
-                      on the greatest number of entries in a sub-board
-                      for a message group or file directory in a file
-                      library (the descriptions will shrink as the
-                      text length of the greatest number of entries
-                      increases).
-1.03     2012-11-30   Bug fix: After leaving the help screen from the
-                      sub-board/directory list, the top line is now
-                      correctly written with the page information as "Page
-                      # of #".
-1.02     2012-10-06   For the lightbar interface, the current page number is
-                      now displayed at the top of the screen (along with the
-                      total number of pages) and is updated when going to a
-                      new page.
-1.01     2011-04-22   Fixed the wording when choosing a message sub-board and
-                      file library.
-1.00     2010-03-13   First public release
\ No newline at end of file
diff --git a/xtrn/DDAreaChoosers/Revision history.txt b/xtrn/DDAreaChoosers/Revision history.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0b78364a2941d1572562044aad87a646fcc0ea9e
--- /dev/null
+++ b/xtrn/DDAreaChoosers/Revision history.txt	
@@ -0,0 +1,59 @@
+This file lists all of the changes made for each release of the Digital
+Distortion Area Choosers.
+
+Revision History (change log)
+=============================
+Version  Date         Description
+-------  ----         -----------
+1.10     2016-??-??   Added the ability to display a custom header file above
+                      the area lists in the area choosers.  Added the
+                      configuration options areaChooserHdrFilenameBase and
+                      areaChooserHdrMaxLines to specify the filename (without
+                      the extension) and maximum number of lines from the header
+                      file to use.
+1.09     2016-01-17   Added a command-line parameter to let the user choose a
+                      message sub-board only within their current message
+                      group, or file directory only within their current file
+                      library.
+1.08     2015-04-19   Added customizable color settings for the key help text
+                      line displayed at the bottom of the screen in lightbar
+                      mode.  Also, updated to allow the PageUp and PageDown
+                      keys to be used instead of the P and N keys to go to the
+                      previous & next pages in lightbar mode.
+1.07     2014-12-22   Message area chooser:
+                      Bug fix: Made this.colors.subBoardHeader apply to the
+                      whole line rather than just the page number.
+                      Bug fix: The initial display of the page number is now
+                      correct (previously, it would start out saying page 1,
+                      even if on another page).
+                      Documentation & example configuration files:
+                      Added the color options subBoardHeader (for the message
+                      area chooser) and fileAreaHdr (for the file area chooser)
+                      to the documentation and example configuration files.
+1.06     2014-09-14   Bug fix: Updated the lightbar highlight format string to
+                      include a normal attribute at the end to avoid the
+                      highlight color to be used when clearing the screen,
+                      etc.  Bug reported by Psi-Jack.
+1.05     2013-05-10   Bug fix in the file area chooser: When listing
+                      directories in a file group, it would sometimes
+                      crash due to an incorrect array index used, and
+                      the array was not set up.  Those have been fixed.
+1.04     2013-05-04   Updated to properly format message sub-boards and
+                      file directories with more than 9999 entries.  The
+                      formatting is now dynamically adjusted depending
+                      on the greatest number of entries in a sub-board
+                      for a message group or file directory in a file
+                      library (the descriptions will shrink as the
+                      text length of the greatest number of entries
+                      increases).
+1.03     2012-11-30   Bug fix: After leaving the help screen from the
+                      sub-board/directory list, the top line is now
+                      correctly written with the page information as "Page
+                      # of #".
+1.02     2012-10-06   For the lightbar interface, the current page number is
+                      now displayed at the top of the screen (along with the
+                      total number of pages) and is updated when going to a
+                      new page.
+1.01     2011-04-22   Fixed the wording when choosing a message sub-board and
+                      file library.
+1.00     2010-03-13   First public release
\ No newline at end of file