From 7468b7d84591276048e949bc42625a0617d9071d Mon Sep 17 00:00:00 2001
From: nightfox <>
Date: Mon, 18 Dec 2017 20:17:40 +0000
Subject: [PATCH] Version 1.53: Updated the definitions of the KEY_PAGE_UP and
 KEY_PAGE_DOWN variables to match what they are in sbbsdefs.js (if defined)
 from December 18, 2017 so that the PageUp and PageDown keys continue to work
 properly.  This update should still also work with older builds of
 Synchronet.

---
 exec/SlyEdit.js      | 112 +++++----
 exec/SlyEdit_Misc.js | 582 ++++++++++++++++++++++---------------------
 2 files changed, 358 insertions(+), 336 deletions(-)

diff --git a/exec/SlyEdit.js b/exec/SlyEdit.js
index 9428251c89..b9207ad34d 100644
--- a/exec/SlyEdit.js
+++ b/exec/SlyEdit.js
@@ -21,6 +21,12 @@
  *                              configuration option allowEditQuoteLines.
  * 2017-12-17 Eric Oulashin     Version 1.52
  *                              Releasing this version
+ * 2017-12-18 Eric Oulashin     Version 1.53
+ *                              Updated the PageUp and PageDown keys to ensure they
+ *                              match what's in sbbsdefs.js, since Synchronet added
+ *                              key codes for those keys on December 17, 2018.  SlyEdit
+ *                              should still work with older and newer builds of
+ *                              Synchronet, with or without the updated sbbsdefs.js.
  */
 
 /* Command-line arguments:
@@ -98,8 +104,8 @@ if (!console.term_supports(USER_ANSI))
 }
 
 // Constants
-const EDITOR_VERSION = "1.52";
-const EDITOR_VER_DATE = "2016-12-17";
+const EDITOR_VERSION = "1.53";
+const EDITOR_VER_DATE = "2016-12-18";
 
 
 // Program variables
@@ -5794,60 +5800,60 @@ function doUserSettings(pCurpos, pReturnCursorToOriginalPos)
 //  tagline: String - The tag line that was selected
 function doTaglineSelection()
 {
-   var retObj = new Object();
-   retObj.taglineWasSelected = false;
-   retObj.tagline = "";
-
-   // Read the tagline file
-   var taglines = readTxtFileIntoArray(gConfigSettings.tagLineFilename, true, true, 5000);
-   if (taglines.length == 0)
-      return;
+	var retObj = {
+		taglineWasSelected: false,
+		tagline: ""
+	};
+
+	// Read the tagline file
+	var taglines = readTxtFileIntoArray(gConfigSettings.tagLineFilename, true, true, 5000);
+	if (taglines.length == 0)
+		return retObj;
 
-   // If the configuration option to shuffle the taglines is enabled, then
-   // shuffle them.
-   if (gConfigSettings.shuffleTaglines)
-      shuffleArray(taglines);
-
-   // Create the list box for the taglines.  Make the box up to 14 lines tall.
-   var boxHeight = (taglines.length > 12 ? 14 : taglines.length+2);
-   var boxTopRow = gEditTop + Math.floor((gEditHeight/2) - (boxHeight/2));
-   var taglineBox = new ChoiceScrollbox(gEditLeft, boxTopRow, gEditWidth, boxHeight,
-                                         "Taglines", gConfigSettings, true, false);
-   var bottomBorderText = "nhcb, cb, cNy)bext, cPy)brev, "
-     + "cFy)birst, cLy)bast, cHOMEb, cENDb, cEntery=bSelect, "
-     + "cRy)bandom, cESCnc/hcQy=bEnd";
-   taglineBox.setBottomBorderText(bottomBorderText, false, false);
-   // Add R as an input loop exit key, to choose a random tagline.
-   taglineBox.addInputLoopExitKey("R");
-   taglineBox.addInputLoopExitKey("r");
-
-   // Set the tagline item array in the list box.  Don't strip control characters
-   // because we've already done that when we read the file.
-   taglineBox.setItemArray(taglines, false);
-   // Let the user choose a tagline
-   var taglineRetObj = taglineBox.doInputLoop(true);
-   retObj.taglineWasSelected = taglineRetObj.itemWasSelected;
-   if (retObj.taglineWasSelected)
-      retObj.tagline = taglineRetObj.selectedItem;
-   // If the R key was pressed, then choose a random tagline.
-   else if ((taglineRetObj.lastKeypress == "R") || (taglineRetObj.lastKeypress == "r"))
-   {
-      retObj.tagline = taglines[random(taglines.length)];
-      retObj.taglineWasSelected = true;
-   }
+	// If the configuration option to shuffle the taglines is enabled, then
+	// shuffle them.
+	if (gConfigSettings.shuffleTaglines)
+		shuffleArray(taglines);
+
+	// Create the list box for the taglines.  Make the box up to 14 lines tall.
+	var boxHeight = (taglines.length > 12 ? 14 : taglines.length+2);
+	var boxTopRow = gEditTop + Math.floor((gEditHeight/2) - (boxHeight/2));
+	var taglineBox = new ChoiceScrollbox(gEditLeft, boxTopRow, gEditWidth, boxHeight, "Taglines", gConfigSettings, true, false);
+	var bottomBorderText = "nhcb, cb, cNy)bext, cPy)brev, "
+	                     + "cFy)birst, cLy)bast, cHOMEb, cENDb, cEntery=bSelect, "
+	                     + "cRy)bandom, cESCnc/hcQy=bEnd";
+	taglineBox.setBottomBorderText(bottomBorderText, false, false);
+	// Add R as an input loop exit key, to choose a random tagline.
+	taglineBox.addInputLoopExitKey("R");
+	taglineBox.addInputLoopExitKey("r");
+
+	// Set the tagline item array in the list box.  Don't strip control characters
+	// because we've already done that when we read the file.
+	taglineBox.setItemArray(taglines, false);
+	// Let the user choose a tagline
+	var taglineRetObj = taglineBox.doInputLoop(true);
+	retObj.taglineWasSelected = taglineRetObj.itemWasSelected;
+	if (retObj.taglineWasSelected)
+		retObj.tagline = taglineRetObj.selectedItem;
+	// If the R key was pressed, then choose a random tagline.
+	else if ((taglineRetObj.lastKeypress == "R") || (taglineRetObj.lastKeypress == "r"))
+	{
+		retObj.tagline = taglines[random(taglines.length)];
+		retObj.taglineWasSelected = true;
+	}
 
-   // If a tagline was selected, then add the tagline prefix in front of it, and
-   // also quote the tagline if the option to do so is enabled.
-   if (retObj.taglineWasSelected)
-   {
-      if (gConfigSettings.taglinePrefix.length > 0)
-         retObj.tagline = gConfigSettings.taglinePrefix + retObj.tagline;
-      // If the option to quote taglines is enabled, then do it.
-      if (gConfigSettings.quoteTaglines)
-         retObj.tagline = "\"" + retObj.tagline + "\"";
-   }
+	// If a tagline was selected, then add the tagline prefix in front of it, and
+	// also quote the tagline if the option to do so is enabled.
+	if (retObj.taglineWasSelected)
+	{
+		if (gConfigSettings.taglinePrefix.length > 0)
+			retObj.tagline = gConfigSettings.taglinePrefix + retObj.tagline;
+		// If the option to quote taglines is enabled, then do it.
+		if (gConfigSettings.quoteTaglines)
+			retObj.tagline = "\"" + retObj.tagline + "\"";
+	}
 
-   return retObj;
+	return retObj;
 }
 
 // Sets the quote prefix, gQuotePrefix (the text to use for prefixing quote lines).
diff --git a/exec/SlyEdit_Misc.js b/exec/SlyEdit_Misc.js
index e53bc1200d..e7b6b73100 100644
--- a/exec/SlyEdit_Misc.js
+++ b/exec/SlyEdit_Misc.js
@@ -66,10 +66,12 @@
  *                              quote lines.
  * 2017-12-16 Eric Oulashin     Updated ReadSlyEditConfigFile() to include the
  *                              allowEditQuoteLines option.
+ * 2017-12-18 Eric Oulashin     Update the KEY_PAGE_UP and KEY_PAGE_DOWN keys to
+ *                              ensure they mat what's in sbbsdef.js
  */
  
  load("text.js");
-
+ 
 // Note: These variables are declared with "var" instead of "const" to avoid
 // multiple declaration errors when this file is loaded more than once.
 
@@ -161,15 +163,26 @@ var CTRL_X = "\x18";
 var CTRL_Y = "\x19";
 var CTRL_Z = "\x1a";
 var KEY_ESC = "\x1b";
-// Key code strings returned by getKeyWithESCChars() - Not real key codes, as
-// the keys they represent are returned as multiple key captures.
-var KEY_PAGE_UP = "\1PgUp";
-var KEY_PAGE_DOWN = "\1PgDn";
 var KEY_F1 = "\1F1";
 var KEY_F2 = "\1F2";
 var KEY_F3 = "\1F3";
 var KEY_F4 = "\1F4";
 var KEY_F5 = "\1F5";
+// PageUp & PageDown keys - Synchronet 3.17 as of about December 18, 2017
+// use CTRL-P and CTRL-N for PageUp and PageDown, respectively.  sbbsdefs.js
+// defines them as KEY_PAGEUP and KEY_PAGEDN; I've used slightly different names
+// in this script so that this script will work with Synchronet systems before
+// and after the update containing those key definitions.
+var KEY_PAGE_UP = CTRL_P;
+var KEY_PAGE_DOWN = CTRL_N;
+// Ensure KEY_PAGE_UP and KEY_PAGE_DOWN are set to what's defined in sbbs.js
+// for KEY_PAGEUP and KEY_PAGEDN in case they change.  Note that this relies
+// on sbbsdefs.js being loaded; SlyEdit.js loads sbbsdefs.js before this file,
+// so this should work.
+if (typeof(KEY_PAGEUP) === "string")
+	KEY_PAGE_UP = KEY_PAGEUP;
+if (typeof(KEY_PAGEDN) === "string")
+	KEY_PAGE_DOWN = KEY_PAGEDN;
 
 // Store the full path & filename of the Digital Distortion Message
 // Lister, since it will be used more than once.
@@ -434,7 +447,7 @@ function ChoiceScrollbox_MinWidth()
 //                           spaces in the top border text with border characters.
 //                           Defaults to false.
 function ChoiceScrollbox(pLeftX, pTopY, pWidth, pHeight, pTopBorderText, pSlyEdCfgObj,
-                          pAddTCharsAroundTopText, pReplaceTopTextSpacesWithBorderChars)
+                         pAddTCharsAroundTopText, pReplaceTopTextSpacesWithBorderChars)
 {
    // The default is to add left & right T characters around the top border
    // text.  But also use pAddTCharsAroundTopText if it's a boolean.
@@ -821,295 +834,298 @@ function ChoiceScrollbox_RefreshItemCharOnScreen(pItemIndex, pCharIndex)
 }
 function ChoiceScrollbox_DoInputLoop(pDrawBorder)
 {
-   var retObj = new Object();
-   retObj.itemWasSelected = false;
-   retObj.selectedIndex = -1;
-   retObj.selectedItem = "";
-   retObj.lastKeypress = "";
+	var retObj = {
+		itemWasSelected: false,
+		selectedIndex: -1,
+		selectedItem: "",
+		lastKeypress: ""
+	};
 
-   // Don't do anything if the item list doesn't contain any items
-   if (this.txtItemList.length == 0)
-      return retObj;
+	// Don't do anything if the item list doesn't contain any items
+	if (this.txtItemList.length == 0)
+		return retObj;
 
-   //////////////////////////////////
-   // Locally-defined functions
-
-   // This function returns the index of the bottommost item that
-   // can be displayed in the box.
-   //
-   // Parameters:
-   //  pArray: The array containing the items
-   //  pTopindex: The index of the topmost item displayed in the box
-   //  pNumItemsPerPage: The number of items per page
-   function getBottommostItemIndex(pArray, pTopIndex, pNumItemsPerPage)
-   {
-      var bottomIndex = pTopIndex + pNumItemsPerPage - 1;
-      // If bottomIndex is beyond the last index, then adjust it.
-      if (bottomIndex >= pArray.length)
-         bottomIndex = pArray.length - 1;
-      return bottomIndex;
-   }
+	//////////////////////////////////
+	// Locally-defined functions
 
+	// This function returns the index of the bottommost item that
+	// can be displayed in the box.
+	//
+	// Parameters:
+	//  pArray: The array containing the items
+	//  pTopindex: The index of the topmost item displayed in the box
+	//  pNumItemsPerPage: The number of items per page
+	function getBottommostItemIndex(pArray, pTopIndex, pNumItemsPerPage)
+	{
+		var bottomIndex = pTopIndex + pNumItemsPerPage - 1;
+		// If bottomIndex is beyond the last index, then adjust it.
+		if (bottomIndex >= pArray.length)
+			bottomIndex = pArray.length - 1;
+		return bottomIndex;
+	}
 
 
-   //////////////////////////////////
-   // Code
 
-   // Variables for keeping track of the item list
-   const numItemsPerPage = this.dimensions.height - 2;
-   this.topItemIndex = 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.
-   this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage);
-   const numPages = Math.ceil(this.txtItemList.length / numItemsPerPage);
-   const topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage;
+	//////////////////////////////////
+	// Code
 
-   if (pDrawBorder)
-      this.drawBorder();
+	// Variables for keeping track of the item list
+	const numItemsPerPage = this.dimensions.height - 2;
+	this.topItemIndex = 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.
+	this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage);
+	const numPages = Math.ceil(this.txtItemList.length / numItemsPerPage);
+	const topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage;
 
-   // User input loop
-   // For the horizontal location of the page number text for the box border:
-   // Based on the fact that there can be up to 9999 text replacements and 10
-   // per page, there will be up to 1000 pages of replacements.  To write the
-   // text, we'll want to be 20 characters to the left of the end of the border
-   // of the box.
-   const pageNumTxtStartX = this.dimensions.topLeftX + this.dimensions.width - 19;
-   const maxItemWidth = this.dimensions.width - 2;
-   var pageNum = 0;
-   var startArrIndex = 0;
-   this.chosenTextItemIndex = retObj.selectedIndex = 0;
-   var endArrIndex = 0; // One past the last array item
-   var screenY = 0;
-   var curpos = new Object(); // For keeping track of the current cursor position
-   curpos.x = 0;
-   curpos.y = 0;
-   var refreshList = true; // For screen redraw optimizations
-   var continueOn = true;
-   while (continueOn)
-   {
-      if (refreshList)
-      {
-         this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage);
-
-         // Write the list of items for the current page
-         startArrIndex = pageNum * numItemsPerPage;
-         endArrIndex = startArrIndex + numItemsPerPage;
-         if (endArrIndex > this.txtItemList.length)
-            endArrIndex = this.txtItemList.length;
-         var selectedItemRow = this.dimensions.topLeftY+1;
-         screenY = this.dimensions.topLeftY + 1;
-         for (var i = startArrIndex; i < endArrIndex; ++i)
-         {
-            console.gotoxy(this.dimensions.topLeftX+1, screenY);
-            if (i == retObj.selectedIndex)
-            {
-               printf(this.listIemHighlightFormatStr, this.txtItemList[i].substr(0, maxItemWidth));
-               selectedItemRow = screenY;
-            }
-            else
-               printf(this.listIemFormatStr, this.txtItemList[i].substr(0, maxItemWidth));
-            ++screenY;
-         }
-         // If the current screen row is below the bottom row inside the box,
-         // continue and write blank lines to the bottom of the inside of the box
-         // to blank out any text that might still be there.
-         while (screenY < this.dimensions.topLeftY+this.dimensions.height-1)
-         {
-            console.gotoxy(this.dimensions.topLeftX+1, screenY);
-            printf(this.listIemFormatStr, "");
-            ++screenY;
-         }
+	if (pDrawBorder)
+		this.drawBorder();
 
-         // Update the page number in the top border of the box.
-         console.gotoxy(pageNumTxtStartX, this.dimensions.topLeftY);
-         printf("n" + this.SlyEdCfgObj.genColors.listBoxBorderText + "Page %4d of %4d", pageNum+1, numPages);
+	// User input loop
+	// For the horizontal location of the page number text for the box border:
+	// Based on the fact that there can be up to 9999 text replacements and 10
+	// per page, there will be up to 1000 pages of replacements.  To write the
+	// text, we'll want to be 20 characters to the left of the end of the border
+	// of the box.
+	const pageNumTxtStartX = this.dimensions.topLeftX + this.dimensions.width - 19;
+	const maxItemWidth = this.dimensions.width - 2;
+	var pageNum = 0;
+	var startArrIndex = 0;
+	this.chosenTextItemIndex = retObj.selectedIndex = 0;
+	var endArrIndex = 0; // One past the last array item
+	var screenY = 0;
+	var curpos = new Object(); // For keeping track of the current cursor position
+	curpos.x = 0;
+	curpos.y = 0;
+	var refreshList = true; // For screen redraw optimizations
+	var continueOn = true;
+	while (continueOn)
+	{
+		if (refreshList)
+		{
+			this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage);
+
+			// Write the list of items for the current page
+			startArrIndex = pageNum * numItemsPerPage;
+			endArrIndex = startArrIndex + numItemsPerPage;
+			if (endArrIndex > this.txtItemList.length)
+				endArrIndex = this.txtItemList.length;
+			var selectedItemRow = this.dimensions.topLeftY+1;
+			screenY = this.dimensions.topLeftY + 1;
+			for (var i = startArrIndex; i < endArrIndex; ++i)
+			{
+				console.gotoxy(this.dimensions.topLeftX+1, screenY);
+				if (i == retObj.selectedIndex)
+				{
+					printf(this.listIemHighlightFormatStr, this.txtItemList[i].substr(0, maxItemWidth));
+					selectedItemRow = screenY;
+				}
+				else
+					printf(this.listIemFormatStr, this.txtItemList[i].substr(0, maxItemWidth));
+				++screenY;
+			}
+			// If the current screen row is below the bottom row inside the box,
+			// continue and write blank lines to the bottom of the inside of the box
+			// to blank out any text that might still be there.
+			while (screenY < this.dimensions.topLeftY+this.dimensions.height-1)
+			{
+				console.gotoxy(this.dimensions.topLeftX+1, screenY);
+				printf(this.listIemFormatStr, "");
+				++screenY;
+			}
 
-         // Just for sane appearance: Move the cursor to the first character of
-         // the currently-selected row and set the appropriate color.
-         curpos.x = this.dimensions.topLeftX+1;
-         curpos.y = selectedItemRow;
-         console.gotoxy(curpos.x, curpos.y);
-         console.print(this.SlyEdCfgObj.genColors.listBoxItemHighlight);
+			// Update the page number in the top border of the box.
+			console.gotoxy(pageNumTxtStartX, this.dimensions.topLeftY);
+			printf("\1n" + this.SlyEdCfgObj.genColors.listBoxBorderText + "Page %4d of %4d", pageNum+1, numPages);
 
-         refreshList = false;
-      }
+			// Just for sane appearance: Move the cursor to the first character of
+			// the currently-selected row and set the appropriate color.
+			curpos.x = this.dimensions.topLeftX+1;
+			curpos.y = selectedItemRow;
+			console.gotoxy(curpos.x, curpos.y);
+			console.print(this.SlyEdCfgObj.genColors.listBoxItemHighlight);
 
-      // Get a key from the user (upper-case) and take action based upon it.
-      retObj.lastKeypress = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, this.SlyEdCfgObj);
-      switch (retObj.lastKeypress)
-      {
-         case 'N': // Next page
-            refreshList = (pageNum < numPages-1);
-            if (refreshList)
-            {
-               ++pageNum;
-               this.topItemIndex += numItemsPerPage;
-               this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
-               // Note: this.bottomItemIndex is refreshed at the top of the loop
-            }
-            break;
-         case 'P': // Previous page
-            refreshList = (pageNum > 0);
-            if (refreshList)
-            {
-               --pageNum;
-               this.topItemIndex -= numItemsPerPage;
-               this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
-               // Note: this.bottomItemIndex is refreshed at the top of the loop
-            }
-            break;
-         case 'F': // First page
-            refreshList = (pageNum > 0);
-            if (refreshList)
-            {
-               pageNum = 0;
-               this.topItemIndex = 0;
-               this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
-               // Note: this.bottomItemIndex is refreshed at the top of the loop
-            }
-            break;
-         case 'L': // Last page
-            refreshList = (pageNum < numPages-1);
-            if (refreshList)
-            {
-               pageNum = numPages-1;
-               this.topItemIndex = topIndexForLastPage;
-               this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
-               // Note: this.bottomItemIndex is refreshed at the top of the loop
-            }
-            break;
-         case KEY_UP:
-            // Move the cursor up one item
-            if (retObj.selectedIndex > 0)
-            {
-               // If the previous item index is on the previous page, then we'll
-               // want to display the previous page.
-               var previousItemIndex = retObj.selectedIndex - 1;
-               if (previousItemIndex < this.topItemIndex)
-               {
-                  --pageNum;
-                  this.topItemIndex -= numItemsPerPage;
-                  // Note: this.bottomItemIndex is refreshed at the top of the loop
-                  refreshList = true;
-               }
-               else
-               {
-                  // Display the current line un-highlighted
-                  console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
-                  printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-                  // Display the previous line highlighted
-                  curpos.x = this.dimensions.topLeftX+1;
-                  --curpos.y;
-                  console.gotoxy(curpos);
-                  printf(this.listIemHighlightFormatStr, this.txtItemList[previousItemIndex].substr(0, maxItemWidth));
-                  console.gotoxy(curpos); // Move the cursor into place where it should be
-                  refreshList = false;
-               }
-               this.chosenTextItemIndex = retObj.selectedIndex = previousItemIndex;
-            }
-            break;
-         case KEY_DOWN:
-            // Move the cursor down one item
-            if (retObj.selectedIndex < this.txtItemList.length - 1)
-            {
-               // If the next item index is on the next page, then we'll want to
-               // display the next page.
-               var nextItemIndex = retObj.selectedIndex + 1;
-               if (nextItemIndex > this.bottomItemIndex)
-               {
-                  ++pageNum;
-                  this.topItemIndex += numItemsPerPage;
-                  // Note: this.bottomItemIndex is refreshed at the top of the loop
-                  refreshList = true;
-               }
-               else
-               {
-                  // Display the current line un-highlighted
-                  console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
-                  printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-                  // Display the previous line highlighted
-                  curpos.x = this.dimensions.topLeftX+1;
-                  ++curpos.y;
-                  console.gotoxy(curpos);
-                  printf(this.listIemHighlightFormatStr, this.txtItemList[nextItemIndex].substr(0, maxItemWidth));
-                  console.gotoxy(curpos); // Move the cursor into place where it should be
-                  refreshList = false;
-               }
-               this.chosenTextItemIndex = retObj.selectedIndex = nextItemIndex;
-            }
-            break;
-         case KEY_HOME: // Go to the first row in the box
-            if (retObj.selectedIndex > this.topItemIndex)
-            {
-               // Display the current line un-highlighted
-               console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
-               printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-               // Select the top item, and display it highlighted.
-               this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
-               curpos.x = this.dimensions.topLeftX+1;
-               curpos.y = this.dimensions.topLeftY+1;
-               console.gotoxy(curpos);
-               printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-               console.gotoxy(curpos); // Move the cursor into place where it should be
-               refreshList = false;
-            }
-            break;
-         case KEY_END: // Go to the last row in the box
-            if (retObj.selectedIndex < this.bottomItemIndex)
-            {
-               // Display the current line un-highlighted
-               console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
-               printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-               // Select the bottommost item, and display it highlighted.
-               this.chosenTextItemIndex = retObj.selectedIndex = this.bottomItemIndex;
-               curpos.x = this.dimensions.topLeftX+1;
-               curpos.y = this.dimensions.bottomRightY-1;
-               console.gotoxy(curpos);
-               printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
-               console.gotoxy(curpos); // Move the cursor into place where it should be
-               refreshList = false;
-            }
-            break;
-         case KEY_ENTER:
-            // If the enter key override function is set, then call it and pass
-            // this object into it.  Otherwise, just select the item and quit.
-            if (this.enterKeyOverrideFn !== null)
-               this.enterKeyOverrideFn(this);
-            else
-            {
-               retObj.itemWasSelected = true;
-               // Note: retObj.selectedIndex is already set.
-               retObj.selectedItem = this.txtItemList[retObj.selectedIndex];
-               refreshList = false;
-               continueOn = false;
-            }
-            break;
-         case KEY_ESC: // Quit
-         case CTRL_A:  // Quit
-         case 'Q':     // Quit
-            this.chosenTextItemIndex = retObj.selectedIndex = -1;
-            refreshList = false;
-            continueOn = false;
-            break;
-         default:
-            // If the keypress is an additional key to exit the input loop, then
-            // do so.
-            if (this.inputLoopExitKeys.hasOwnProperty(retObj.lastKeypress))
-            {
-               this.chosenTextItemIndex = retObj.selectedIndex = -1;
-               refreshList = false;
-               continueOn = false;
-            }
-            else
-            {
-               // Unrecognized command.  Don't refresh the list of the screen.
-               refreshList = false;
-            }
-            break;
-      }
-   }
+			refreshList = false;
+		}
 
-   console.print("n"); // To prevent outputting highlight colors, etc..
-   return retObj;
+		// Get a key from the user (upper-case) and take action based upon it.
+		retObj.lastKeypress = getKeyWithESCChars(K_UPPER|K_NOCRLF|K_NOSPIN, this.SlyEdCfgObj);
+		switch (retObj.lastKeypress)
+		{
+			case 'N': // Next page
+			case KEY_PAGE_DOWN:
+				refreshList = (pageNum < numPages-1);
+				if (refreshList)
+				{
+					++pageNum;
+					this.topItemIndex += numItemsPerPage;
+					this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
+					// Note: this.bottomItemIndex is refreshed at the top of the loop
+				}
+				break;
+			case 'P': // Previous page
+			case KEY_PAGE_UP:
+				refreshList = (pageNum > 0);
+				if (refreshList)
+				{
+					--pageNum;
+					this.topItemIndex -= numItemsPerPage;
+					this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
+					// Note: this.bottomItemIndex is refreshed at the top of the loop
+				}
+				break;
+			case 'F': // First page
+				refreshList = (pageNum > 0);
+				if (refreshList)
+				{
+					pageNum = 0;
+					this.topItemIndex = 0;
+					this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
+					// Note: this.bottomItemIndex is refreshed at the top of the loop
+				}
+				break;
+			case 'L': // Last page
+				refreshList = (pageNum < numPages-1);
+				if (refreshList)
+				{
+					pageNum = numPages-1;
+					this.topItemIndex = topIndexForLastPage;
+					this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
+					// Note: this.bottomItemIndex is refreshed at the top of the loop
+				}
+				break;
+			case KEY_UP:
+				// Move the cursor up one item
+				if (retObj.selectedIndex > 0)
+				{
+					// If the previous item index is on the previous page, then we'll
+					// want to display the previous page.
+					var previousItemIndex = retObj.selectedIndex - 1;
+					if (previousItemIndex < this.topItemIndex)
+					{
+						--pageNum;
+						this.topItemIndex -= numItemsPerPage;
+						// Note: this.bottomItemIndex is refreshed at the top of the loop
+						refreshList = true;
+					}
+					else
+					{
+						// Display the current line un-highlighted
+						console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
+						printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+						// Display the previous line highlighted
+						curpos.x = this.dimensions.topLeftX+1;
+						--curpos.y;
+						console.gotoxy(curpos);
+						printf(this.listIemHighlightFormatStr, this.txtItemList[previousItemIndex].substr(0, maxItemWidth));
+						console.gotoxy(curpos); // Move the cursor into place where it should be
+						refreshList = false;
+					}
+					this.chosenTextItemIndex = retObj.selectedIndex = previousItemIndex;
+				}
+				break;
+			case KEY_DOWN:
+				// Move the cursor down one item
+				if (retObj.selectedIndex < this.txtItemList.length - 1)
+				{
+					// If the next item index is on the next page, then we'll want to
+					// display the next page.
+					var nextItemIndex = retObj.selectedIndex + 1;
+					if (nextItemIndex > this.bottomItemIndex)
+					{
+						++pageNum;
+						this.topItemIndex += numItemsPerPage;
+						// Note: this.bottomItemIndex is refreshed at the top of the loop
+						refreshList = true;
+					}
+					else
+					{
+						// Display the current line un-highlighted
+						console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
+						printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+						// Display the previous line highlighted
+						curpos.x = this.dimensions.topLeftX+1;
+						++curpos.y;
+						console.gotoxy(curpos);
+						printf(this.listIemHighlightFormatStr, this.txtItemList[nextItemIndex].substr(0, maxItemWidth));
+						console.gotoxy(curpos); // Move the cursor into place where it should be
+						refreshList = false;
+					}
+					this.chosenTextItemIndex = retObj.selectedIndex = nextItemIndex;
+				}
+				break;
+			case KEY_HOME: // Go to the first row in the box
+				if (retObj.selectedIndex > this.topItemIndex)
+				{
+					// Display the current line un-highlighted
+					console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
+					printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+					// Select the top item, and display it highlighted.
+					this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex;
+					curpos.x = this.dimensions.topLeftX+1;
+					curpos.y = this.dimensions.topLeftY+1;
+					console.gotoxy(curpos);
+					printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+					console.gotoxy(curpos); // Move the cursor into place where it should be
+					refreshList = false;
+				}
+				break;
+			case KEY_END: // Go to the last row in the box
+				if (retObj.selectedIndex < this.bottomItemIndex)
+				{
+					// Display the current line un-highlighted
+					console.gotoxy(this.dimensions.topLeftX+1, curpos.y);
+					printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+					// Select the bottommost item, and display it highlighted.
+					this.chosenTextItemIndex = retObj.selectedIndex = this.bottomItemIndex;
+					curpos.x = this.dimensions.topLeftX+1;
+					curpos.y = this.dimensions.bottomRightY-1;
+					console.gotoxy(curpos);
+					printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth));
+					console.gotoxy(curpos); // Move the cursor into place where it should be
+					refreshList = false;
+				}
+				break;
+			case KEY_ENTER:
+				// If the enter key override function is set, then call it and pass
+				// this object into it.  Otherwise, just select the item and quit.
+				if (this.enterKeyOverrideFn !== null)
+				this.enterKeyOverrideFn(this);
+				else
+				{
+					retObj.itemWasSelected = true;
+					// Note: retObj.selectedIndex is already set.
+					retObj.selectedItem = this.txtItemList[retObj.selectedIndex];
+					refreshList = false;
+					continueOn = false;
+				}
+				break;
+			case KEY_ESC: // Quit
+			case CTRL_A:  // Quit
+			case 'Q':     // Quit
+				this.chosenTextItemIndex = retObj.selectedIndex = -1;
+				refreshList = false;
+				continueOn = false;
+				break;
+			default:
+				// If the keypress is an additional key to exit the input loop, then
+				// do so.
+				if (this.inputLoopExitKeys.hasOwnProperty(retObj.lastKeypress))
+				{
+					this.chosenTextItemIndex = retObj.selectedIndex = -1;
+					refreshList = false;
+					continueOn = false;
+				}
+				else
+				{
+					// Unrecognized command.  Don't refresh the list of the screen.
+					refreshList = false;
+				}
+				break;
+		}
+	}
+
+	console.print("\1n"); // To prevent outputting highlight colors, etc..
+	return retObj;
 }
 
 
@@ -4232,7 +4248,7 @@ function consolePauseWithESCChars(pCfgObj)
 // Inputs a keypress from the user and handles some ESC-based
 // characters such as PageUp, PageDown, and ESC.  If PageUp
 // or PageDown are pressed, this function will return the
-// string "\1PgUp" (KEY_PAGE_UP) or "\1Pgdn" (KEY_PAGE_DOWN),
+// string defined by KEY_PAGE_UP or EY_PAGE_DOWN,
 // respectively.  Also, F1-F5 will be returned as "\1F1"
 // through "\1F5", respectively.
 // Thanks goes to Psi-Jack for the original impementation
-- 
GitLab