From d3507aad755c2c5455b8e0cb345f8ff89120078f Mon Sep 17 00:00:00 2001 From: nightfox <> Date: Sat, 15 Nov 2014 17:39:48 +0000 Subject: [PATCH] SlyEdit version 1.41: - Added the ability to use the PageUp and PageDown keys for page navigation in the message, replacing Ctrl-W and Ctrl-S. Also, enabled page navigation in the quote line selection window, as well as being able to go directly to the first & last pages of quote lines. - Corrected the width of the top border of the quote window in DCT mode for wide terminals (more than 80 characters). - Improved the detection of paragraphs for wrapping quote lines: If the next line of text is indented compared to the previous line, the indented line is considered the start of a new paragraph, and its lines will be wrapped separately from the previous paragraph. - Minor bug fix: When the cursor is on a line with a blank line below it, When backspacing to the beginning of the line and then to the line before it, the blank line below used to not be preserved when typing onto the next line again. - The control key passthru & BBS status configuration (i.e., screen pause) are now restored not only in normal exit, but also if there is an exit due to a runtime error (although I would not expect that to happen). --- exec/SlyEdit.js | 188 +++++++++++++------- exec/SlyEdit_DCTStuff.js | 34 ++-- exec/SlyEdit_IceStuff.js | 14 +- exec/SlyEdit_Misc.js | 360 +++++++++++++++++++++++++++------------ 4 files changed, 408 insertions(+), 188 deletions(-) diff --git a/exec/SlyEdit.js b/exec/SlyEdit.js index d0119a9177..809db2f233 100644 --- a/exec/SlyEdit.js +++ b/exec/SlyEdit.js @@ -13,46 +13,21 @@ * 2009-08-22 Eric Oulashin Version 1.00 * Initial public release * ....Removed some comments... - * 2013-10-22 Eric Oulashin Version 1.36 Beta - * Worked on debugging & fixing a bug when quoting - * with author initials to fix a bug where a large - * section in certain messages wasn't getting quoted. - * 2013-10-28 Eric Oulashin Version 1.36 - * Releasing this version, as it seems to be quoting - * all messages well with author initials. - * 2013-11-09 Eric Oulashin Version 1.37 Beta - * Started trying to debug an issue where SlyEdit - * sometimes uses the replying user's initials - * in the last line of a paragraph of the original - * author's message. - * 2013-11-10 Eric Oulashin Version 1.37 - * Releasing this version - * 2013-11-11 Eric Oulashin Version 1.38 - * Minor bug fix: If there are no text replacements - * to list when the user tries to list them, the cursor - * now returns to its proper place after displaying - * the error. - * 2013-11-25 Eric Oulashin Version 1.39 Beta - * Minor bug fix in wrapQuoteLinesUsingAuthorInitials() in - * SlyEdit_Misc.js starting on line 2739: Added more - * checks to ensure that the gQuoteLines object it - * references is valid. - * Minor bug fix: Updated the Ice-style color display - * for the control key help text in the lower right so - * that normal/high attributes don't interfere with the - * high blue color of the parenthesis. - * 2013-11-27 Eric Oulashin Version 1.39 - * Releasing this version after having done some testing. - * 2014-05-12 Eric Oulashin Version 1.40 (not released yet) - * Added a check in wrapQuoteLinesUsingAuthorInitials() in - * SlyEdit_Misc.js: When building the last section info object, - * added a check to the while loop that makes sure - * sectionInfo.endArrIndex is greater than 0 to avoid an index - * out-of-bounds issue with the check that references - * gQuoteLines[sectionInfo.endArrIndex-1]. This should hopefully - * fix a bug with SlyEdit crashing at that point. - * 2014-05-21 Eric Oulashin Version 1.40 - * Going ahead and releasing this version + * 2014-11-01 Eric Oulashin Version 1.41 Beta + * Added support for PageUp & PageDown keys for + * message & quote line scrolling + * 2014-11-08 Eric OUlashin Started working on improved paragraph detection + * for paragraphs where the first line is indented + * when wrapping non-quoted text in reply messages + * 2014-11-09 Eric Oulashin Bug fix in doQuoteSelection(): Reset gQuoteLinesIndex + * to 0 when re-formatting the quote lines to ensure + * valid behavior. gQuoteLinesTopIndex was already + * being reset to 0. + * 2014-11-15 Eric Oulashin Version 1.41 + * Decided to release this version with the PageUp/PageDown + * key support, improved paragraph detetion & wrapping + * for quote lines, backspace fix, and fixed DCT quote + * window top border for wide terminals. */ /* Command-line arguments: @@ -130,8 +105,8 @@ if (!console.term_supports(USER_ANSI)) } // Constants -const EDITOR_VERSION = "1.40"; -const EDITOR_VER_DATE = "2014-05-21"; +const EDITOR_VERSION = "1.41"; +const EDITOR_VER_DATE = "2014-11-15"; // Program variables @@ -387,6 +362,9 @@ bbs.sys_status &=~SS_PAUSEON; bbs.sys_status |= SS_PAUSEOFF; var gOldPassthru = console.ctrlkey_passthru; console.ctrlkey_passthru = "+ACGKLOPQRTUVWXYZ_"; +// Set some on-exit code to ensure that the original ctrl key passthru & system +// status settings are restored upon script exit, even if there is a runtime error. +js.on_exit("console.ctrlkey_passthru = gOldPassthru; bbs.sys_status = gOldStatus;"); // Enable delete line in SyncTERM (Disabling ANSI Music in the process) console.write("\033[=1M"); console.clear(); @@ -797,10 +775,8 @@ function doEditLoop() const CMDLIST_HELP_KEY = CTRL_P; const QUOTE_KEY = CTRL_Q; const PROGRAM_INFO_HELP_KEY = CTRL_R; - const PAGE_DOWN_KEY = CTRL_S; const LIST_TXT_REPLACEMENTS_KEY = CTRL_T; const USER_SETTINGS_KEY = CTRL_U; - const PAGE_UP_KEY = CTRL_W; const EXPORT_FILE_KEY = CTRL_X; const SAVE_KEY = CTRL_Z; @@ -829,7 +805,7 @@ function doEditLoop() var continueOn = true; while (continueOn) { - userInput = getUserKey(K_NOCRLF|K_NOSPIN, gConfigSettings); + userInput = getKeyWithESCChars(K_NOCRLF|K_NOSPIN, gConfigSettings); // If userInput is blank, then the input timeout was probably // reached, so abort. if (userInput == "") @@ -870,6 +846,7 @@ function doEditLoop() returnCode = 0; // Save continueOn = false; break; + case KEY_F1: case CMDLIST_HELP_KEY: displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements, gConfigSettings.allowUserSettings); @@ -1249,7 +1226,7 @@ function doEditLoop() currentWordLength = retObj.currentWordLength; console.print(chooseEditColor()); // Make sure the edit color is correct break; - case PAGE_UP_KEY: // Move 1 page up in the message + case KEY_PAGE_UP: // Move 1 page up in the message // Calculate the index of the message line shown at the top // of the edit area. var topEditIndex = gEditLinesIndex-(curpos.y-gEditTop); @@ -1296,7 +1273,7 @@ function doEditLoop() } console.print(chooseEditColor()); // Make sure the edit color is correct break; - case PAGE_DOWN_KEY: // Move 1 page down in the message + case KEY_PAGE_DOWN: // Move 1 page down in the message // Calculate the index of the message line shown at the top // of the edit area, and the index of the line that would be // shown at the bottom of the edit area. @@ -1576,6 +1553,11 @@ function doBackspace(pCurpos, pCurrentWordLength) // previous line. if (gEditLines[prevLineIndex].length() <= gEditWidth-1) { + // Copy the current line's "hard newline end" setting to the + // previous line (so that if there's a blank line below the + // current line, the blank line will be preserved), then remove + // the current edit line. + gEditLines[gEditLinesIndex-1].hardNewlineEnd = gEditLines[gEditLinesIndex].hardNewlineEnd; gEditLines.splice(gEditLinesIndex, 1); --gEditLinesIndex; @@ -1866,10 +1848,6 @@ function doPrintableChar(pUserInput, pCurpos, pCurrentWordLength) // on the screen. if ((reAdjusted && (gEditLines[gEditLinesIndex].text != originalAfterCharApplied)) || madeTxtReplacement) { - // TODO: In case the user entered a whole line of text without any spaces, - // the new character would appear on the next line, so we need to figure - // out where the cursor location should be. - // If gTextLineIndex is >= gEditLines[gEditLinesIndex].length(), then // we know the current word was wrapped to the next line. Figure out what // retObj.x, retObj.currentWordLength, gEditLinesIndex, and gTextLineIndex @@ -1877,8 +1855,8 @@ function doPrintableChar(pUserInput, pCurpos, pCurrentWordLength) // screen to update, and deal with scrolling if necessary. if (gTextLineIndex >= gEditLines[gEditLinesIndex].length()) { - // TODO: I changed this on 2010-02-14 to (hopefully) place the cursor - // where it should be + // I changed this on 2010-02-14 to (hopefully) place the cursor where + // it should be // Old line (prior to 2010-02-14): //var numChars = gTextLineIndex - gEditLines[gEditLinesIndex].length(); // New (2010-02-14): @@ -2441,7 +2419,10 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) gQuoteLines.push(doQuoteSelection.backupQuoteLines[i]); } - gQuoteLinesTopIndex = gQuoteLinesIndex = 0; // To prevent bad things + // Reset the selected quote line index & top displayed quote line index + // back to 0 to ensure valid screen display & scrolling behavior + gQuoteLinesIndex = 0; + gQuoteLinesTopIndex = 0; // Update the quote line prefix text and wrap the quote lines setQuotePrefix(); @@ -2469,6 +2450,12 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) const quoteWinTopScreenRow = quoteTopScreenRow-1; const quoteWinWidth = gEditRight - gEditLeft + 1; + // For pageUp/pageDown functionality - Calculate the top quote line index + // for the last page. + var quoteWinInnerHeight = quoteBottomScreenRow - quoteTopScreenRow + 1; // # of quote lines in the quote window + var numPages = Math.ceil(gQuoteLines.length / quoteWinInnerHeight); + var topIndexForLastPage = (quoteWinInnerHeight * numPages) - quoteWinInnerHeight; + // Display the top border of the quote window. fpDrawQuoteWindowTopBorder(quoteWinHeight, gEditLeft, gEditRight); @@ -2487,7 +2474,7 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) while (continueOn) { // Get a keypress from the user - userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); + userInput = getKeyWithESCChars(K_UPPER|K_NOCRLF|K_NOSPIN|K_NOECHO, gConfigSettings); if (userInput == "") { // The input timeout was reached. Abort. @@ -2549,6 +2536,93 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) screenLine = downRetObj.screenLine; quoteLine = downRetObj.quoteLine; break; + case KEY_HOME: // Select the first quote line on the current page + if (gQuoteLinesIndex != gQuoteLinesTopIndex) + { + gQuoteLinesIndex = gQuoteLinesTopIndex; + // Write the current quote line with unhighlighted colors + console.gotoxy(gEditLeft, screenLine); + printf(gFormatStrWithAttr, gQuoteWinTextColor, quoteLine); + // Calculate the new screen line and draw the new quote line with + // highlighted colors + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + console.gotoxy(gEditLeft, screenLine); + printf(gFormatStrWithAttr, gQuoteLineHighlightColor, quoteLine); + console.gotoxy(gEditLeft, screenLine); + } + break; + case KEY_END: // Select the last quote line on the current page + var lastIndexForCurrentPage = gQuoteLinesTopIndex + quoteWinInnerHeight - 1; + if (gQuoteLinesIndex != lastIndexForCurrentPage) + { + gQuoteLinesIndex = lastIndexForCurrentPage; + // Write the current quote line with unhighlighted colors + console.gotoxy(gEditLeft, screenLine); + printf(gFormatStrWithAttr, gQuoteWinTextColor, quoteLine); + // Calculate the new screen line and draw the new quote line with + // highlighted colors + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + console.gotoxy(gEditLeft, screenLine); + printf(gFormatStrWithAttr, gQuoteLineHighlightColor, quoteLine); + console.gotoxy(gEditLeft, screenLine); + } + break; + case KEY_PAGE_UP: // Go up 1 page in the quote lines + // If the current top quote line index is greater than 0, then go to + // the previous page of quote lines and select the top index on that + // page as the current selected quote line. + if (gQuoteLinesTopIndex > 0) + { + gQuoteLinesTopIndex -= quoteWinInnerHeight; + if (gQuoteLinesTopIndex < 0) + gQuoteLinesTopIndex = 0; + gQuoteLinesIndex = gQuoteLinesTopIndex; + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + displayQuoteWindowLines(gQuoteLinesTopIndex, quoteWinHeight, quoteWinWidth, true, + gQuoteLinesIndex); + } + break; + case KEY_PAGE_DOWN: // Go down 1 page in the quote lines + // If the current top quote line index is below the top index for the + // last page, then go to the next page of quote lines and select the + // top index on that page as the current selected quote line. + if (gQuoteLinesTopIndex < topIndexForLastPage) + { + gQuoteLinesTopIndex += quoteWinInnerHeight; + if (gQuoteLinesTopIndex > topIndexForLastPage) + gQuoteLinesTopIndex = topIndexForLastPage; + gQuoteLinesIndex = gQuoteLinesTopIndex; + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + displayQuoteWindowLines(gQuoteLinesTopIndex, quoteWinHeight, quoteWinWidth, true, + gQuoteLinesIndex); + } + break; + case "F": // Go to the first page + if (gQuoteLinesTopIndex > 0) + { + gQuoteLinesTopIndex = 0; + gQuoteLinesIndex = gQuoteLinesTopIndex; + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + displayQuoteWindowLines(gQuoteLinesTopIndex, quoteWinHeight, quoteWinWidth, true, + gQuoteLinesIndex); + } + break; + case "L": // Go to the last page + if (gQuoteLinesTopIndex < topIndexForLastPage) + { + gQuoteLinesTopIndex = topIndexForLastPage; + gQuoteLinesIndex = gQuoteLinesTopIndex; + quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); + screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); + displayQuoteWindowLines(gQuoteLinesTopIndex, quoteWinHeight, quoteWinWidth, true, + gQuoteLinesIndex); + } + break; case KEY_ENTER: // numTimesToMoveDown specifies how many times to move the cursor // down after inserting the quote line into the message. @@ -2648,8 +2722,8 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) // pQuoteWinHeight: The height of the quote window // pQuoteWinWidth: The width of the quote window // pQuoteBottomScreenLine: The bottommost screen line where quote lines are displayed -function moveDownOneQuoteLine(pQuoteLinesIndex, pScreenLine, pQuoteWinHeight, pQuoteWinWidth, - pQuoteBottomScreenLine) +function moveDownOneQuoteLine(pQuoteLinesIndex, pScreenLine, pQuoteWinHeight, + pQuoteWinWidth, pQuoteBottomScreenLine) { // Create the return object var returnObj = new Object(); diff --git a/exec/SlyEdit_DCTStuff.js b/exec/SlyEdit_DCTStuff.js index 0fb324b3b8..c9ae30ebb5 100644 --- a/exec/SlyEdit_DCTStuff.js +++ b/exec/SlyEdit_DCTStuff.js @@ -58,6 +58,10 @@ * of different widths. * 2013-09-16 Eric Oulashin Fixed off-by-one bug for the horizontal position in * Updated updateInsertModeOnScreen_DCTStyle(). + * 2014-11-04 Eric Oulashin Fixed the quote window top border length in + * DrawQuoteWindowTopBorder_DCTStyle(). Updated the + * quote window bottom border to display the new + * scroll keys in DrawQuoteWindowBottomBorder_DCTStyle(). */ load("sbbsdefs.js"); @@ -453,8 +457,8 @@ function DrawQuoteWindowTopBorder_DCTStyle(pQuoteWinHeight, pEditLeft, pEditRigh + gConfigSettings.DCTColors.QuoteWinBorderTextColor + "Quote Window " + gConfigSettings.DCTColors.QuoteWinBorderColor; var curLength = strip_ctrl(DrawQuoteWindowTopBorder_DCTStyle.border).length; - var endCol = console.screen_columns-1; - for (var i = curLength; i < endCol; ++i) + var borderWidth = pEditRight - pEditLeft; + for (var i = curLength; i < borderWidth; ++i) DrawQuoteWindowTopBorder_DCTStyle.border += HORIZONTAL_SINGLE; DrawQuoteWindowTopBorder_DCTStyle.border += UPPER_RIGHT_SINGLE; } @@ -478,22 +482,20 @@ function DrawQuoteWindowBottomBorder_DCTStyle(pEditLeft, pEditRight) if (typeof(DrawQuoteWindowBottomBorder_DCTStyle.border) == "undefined") { // Create a string containing the quote help text. - var quoteHelpText = " " + gConfigSettings.DCTColors.QuoteWinBorderTextColor - + "[Enter] Quote Line " + gConfigSettings.DCTColors.QuoteWinBorderColor - + " "; - for (var i = 0; i < 3; ++i) - quoteHelpText += HORIZONTAL_SINGLE; - quoteHelpText += " " + gConfigSettings.DCTColors.QuoteWinBorderTextColor - + "[ESC] Stop Quoting " + gConfigSettings.DCTColors.QuoteWinBorderColor - + " "; - for (var i = 0; i < 3; ++i) - quoteHelpText += HORIZONTAL_SINGLE; - quoteHelpText += " " + gConfigSettings.DCTColors.QuoteWinBorderTextColor - + "[Up/Down] Scroll Lines "; + var quoteHelpText = gConfigSettings.DCTColors.QuoteWinBorderTextColor + + "[Enter] Accept" + gConfigSettings.DCTColors.QuoteWinBorderColor + + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor + + "[^Q/ESC] End" + gConfigSettings.DCTColors.QuoteWinBorderColor + + HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor + + "[" + UP_ARROW + "/" + DOWN_ARROW + "/PgUp/PgDn] Scroll" + + gConfigSettings.DCTColors.QuoteWinBorderColor + HORIZONTAL_SINGLE + + HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor + + "[F/L] First/last page"; + var helpTextLen = strip_ctrl(quoteHelpText).length; // Figure out the starting horizontal position on the screen so that // the quote help text line can be centered. - var helpTextStartX = ((console.screen_columns/2) - (strip_ctrl(quoteHelpText).length/2)).toFixed(0); + var helpTextStartX = Math.floor((console.screen_columns/2) - (helpTextLen/2)); // Start creating DrawQuoteWindowBottomBorder_DCTStyle.border with the // bottom border lines, up until helpTextStartX. @@ -504,7 +506,7 @@ function DrawQuoteWindowBottomBorder_DCTStyle(pEditLeft, pEditRight) // Add the help text, then display the rest of the bottom border characters. DrawQuoteWindowBottomBorder_DCTStyle.border += quoteHelpText + gConfigSettings.DCTColors.QuoteWinBorderColor; - for (var XPos = pEditLeft+2+strip_ctrl(quoteHelpText).length; XPos <= pEditRight-1; ++XPos) + for (var XPos = helpTextStartX + helpTextLen; XPos <= pEditRight-2; ++XPos) DrawQuoteWindowBottomBorder_DCTStyle.border += HORIZONTAL_SINGLE; DrawQuoteWindowBottomBorder_DCTStyle.border += LOWER_RIGHT_SINGLE; } diff --git a/exec/SlyEdit_IceStuff.js b/exec/SlyEdit_IceStuff.js index 5e8c5f24bd..7ef827193d 100644 --- a/exec/SlyEdit_IceStuff.js +++ b/exec/SlyEdit_IceStuff.js @@ -399,7 +399,7 @@ function DisplayBottomHelpLine_IceStyle(pLineNum, pUsingQuotes) // This line contains the copyright mesage & ESC key help var screenText = iceText(EDITOR_PROGRAM_NAME + " v", "w") + "ch" + EDITOR_VERSION.toString() + " " - + iceText("Copyright", "w") + " ch2013 " + + iceText("Copyright", "w") + " ch2014 " + iceText("Eric Oulashin", "w") + " nb" + DOT_CHAR + " " + iceText("Press ESCape For Help", "w"); // Calculate the starting position to center the help text, and front-pad @@ -491,19 +491,23 @@ function DrawQuoteWindowBottomBorder_IceStyle(pEditLeft, pEditRight) gConfigSettings.iceColors.BorderColor1, gConfigSettings.iceColors.BorderColor2) + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT - + gConfigSettings.iceColors.QuoteWinBorderTextColor + "^Q/ESC-End" + + gConfigSettings.iceColors.QuoteWinBorderTextColor + "^Q/ESC=End" + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT + gConfigSettings.iceColors.BorderColor1 + HORIZONTAL_DOUBLE + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT - + gConfigSettings.iceColors.QuoteWinBorderTextColor + "CR-Accept" + + gConfigSettings.iceColors.QuoteWinBorderTextColor + "CR=Accept" + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT + gConfigSettings.iceColors.BorderColor1 + HORIZONTAL_DOUBLE + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT - + gConfigSettings.iceColors.QuoteWinBorderTextColor + "Up/Down-Scroll" + + gConfigSettings.iceColors.QuoteWinBorderTextColor + "Up/Down/PgUp/PgDn=Scroll" + + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT + + gConfigSettings.iceColors.BorderColor1 + HORIZONTAL_DOUBLE + + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT + + gConfigSettings.iceColors.QuoteWinBorderTextColor + "F/L=First/Last pg" + gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT; // The border from here to the end of the line: Random high/low blue var screenText = ""; - for (var posX = pEditLeft + 43; posX <= pEditRight; ++posX) + for (var posX = pEditLeft + 73; posX <= pEditRight; ++posX) screenText += HORIZONTAL_DOUBLE; screenText += LOWER_RIGHT_VSINGLE_HDOUBLE; DrawQuoteWindowBottomBorder_IceStyle.border += randomTwoColorString(screenText, diff --git a/exec/SlyEdit_Misc.js b/exec/SlyEdit_Misc.js index 12e02c9624..ade4bf7b14 100644 --- a/exec/SlyEdit_Misc.js +++ b/exec/SlyEdit_Misc.js @@ -12,86 +12,28 @@ * 2009-08-22 Eric Oulashin Version 1.00 * Initial public release * ....Removed some comments... - * 2013-08-24 Eric Oulashin Bug fix in wrapQuoteLines(): Off-by-one bug toward - * the end where there might be more quote lines - * than lineInfo objects, so it wouldn't quote the - * last line when using author initials. - * 2013-08-28 Eric Oulashin Updated ReadSlyEditConfigFile() to read and - * set the enableTextReplacements setting. It - * defaults to false. Also added populateTxtReplacements(). - * Added moveGenColorsToGenSettings(), which - * can be called by JavaScripts for different - * UI styles to move the general color settings - * from their own color array into the genColors - * array in the configuration object. - * 2013-08-31 Eric Oulashin Added the function getWordFromEditLine(). - * 2013-09-02 Eric Oulashin Worked on the new function doMacroTxtReplacementInEditLine(), - * which performs text replacement (AKA macros) on - * one of the message edit lines. Added - * genFullPathCfgFilename() so that the logic for finding - * the configuration files is all in one place. Added - * getFirstLetterFromStr() and firstLetterIsUppercase(), - * which are helpers for doMacroTxtReplacementInEditLine() - * for checking & fixing first-letter capitalization - * after doing a regex replace. - * 2013-09-03 Eric Oulashin Updated populateTxtReplacements() so that it won't - * force the replacement text strings to lowercase - * when not using regular expressions. Also made - * use of strip_ctrl() with the replacement text to - * prevent the use of color codes, which might mess - * up SlyEdit's tracking of string indexes, etc. - * Updated doMacroTxtReplacementInEditLine() so - * that macro text replacements won't lowercase the - * replacement text when in literal match & replace - * mode. - * 2013-09-07 Eric Oulashin Bug fix: Updated ReadSlyEditConfigFile() to - * default cfgObj.genColors.listBoxItemText to - * ensure that it gets defined. - * Code refactor: Moved doMacroTxtReplacementInEditLine() - * and getWordFromEditLine() to TextLine member - * methods TextLine_doMacroTxtReplacement() and - * TextLine_getWord(). - * 2013-09-13 - * 2013-09-14 Eric Oulashin Added functions for a new ChoiceScrollbox object - * type, which is a generic scrollable list box that - * allows a user to select an item. - * Added the functions readTxtFileIntoArray() and - * txtFileContainsLines(). Updated displayCommandList() - * to display the hotkey Ctrl-U for user settings. - * Moved the options for author initials in quote - * lines to user settings. - * 2013-09-19 Eric Oulashin Added the shuffleArray() function. Added 3 - * more options to the config file, to be read by - * ReadSlyEditConfigFile(): taglinePrefix, quoteTaglines, - * and shuffleTaglines. - * 2013-10-22 Eric Oulashin Worked on the wrapQuoteLines() function to improve - * quoting with author initials to fix a bug where - * a large section in certain messages wasn't getting - * quoted. - * 2013-10-22 to - * 2013-10-27 Eric Oulashin Worked on quote line wrapping to fix issues when - * quoting messages with author initials. - * 2013-11-09 Eric Oulashin Added an optional parameter to wrapTextLines() as - * an array to return indexes of lines that needed a - * new line inserted after it. This was done to help - * wrapQuoteLinesUsingAuthorInitials() move its line - * info objects down when a new line is added. - * 2013-11-10 Eric Oulashin Made some more refinements, mainly for - * wrapQuoteLinesUsingAuthorInitials(). - * 2013-11-25 Eric Oulashin Minor bug fix in wrapQuoteLinesUsingAuthorInitials() - * starting on line 2739: Added more checks to ensure - * that the gQuoteLines object it references is valid. - * Bug fix in DisplayTextAreaBottomBorder_IceStyle() in - * SlyEdit_IceStuff.js to ensure that the parenthesis in the - * CTRL key help text at the right in the bottom border are - * correctly displayed with a high blue color, regardless of - * what is specified in the color theme file. - * 2014-05-12 Eric Oulashin Added a check in wrapQuoteLinesUsingAuthorInitials(): When - * building the last section info object, added a check to - * the while loop that makes sure sectionInfo.endArrIndex is - * greater than 0 to avoid an index out-of-bounds issue with - * the check that references gQuoteLines[sectionInfo.endArrIndex-1]. - * This should hopefully fix a bug with SlyEdit crashing at that point. + * 2014-11-01 Eric Oulashin Added getKeyWithESCChars(), along with the key definitions + * KEY_PAGE_UP and KEY_PAGE_DOWN, to support inputting the + * PageUp & PageDown keys from the user. + * 2014-11-08 Eric Oulashin Updated wrapQuoteLinesUsingAuthorInitials() and + * wrapQuoteLines_NoAuthorInitials() so that if the + * current line's indentation differs from the previous + * line's indentation, it will mark a new section for + * the quote lines so that lines of different paragraphs + * don't get wrapped together. + * 2014-11-09 Eric Oulashin Bug fix in wrapTextLines(): For the edge case when + * text is trimmed from the end of the last line in + * the paragraph, it will insert a new line in the + * array at the end of the paragraph for the trimmed + * text. For lines before the last line in the + * paragraph, it will just prepend the text to the + * next line in the array. Also, updated + * wrapQuoteLinesUsingAuthorInitials() to trim leading + * spaces from non-quote text sections to leave more + * room for wrapping the lines and to avoid having + * whole sections of quote lines that start with + * several spaces. Also made a similar update to + * wrapQuoteLines_NoAuthorInitials(). */ // Note: These variables are declared with "var" instead of "const" to avoid @@ -185,6 +127,15 @@ 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"; // Store the full path & filename of the Digital Distortion Message // Lister, since it will be used more than once. @@ -1226,7 +1177,7 @@ function isPrintableChar(pText) // Removes multiple, leading, and/or trailing spaces // The search & replace regular expressions used in this // function came from the following URL: -// http://qodo.co.uk/blog/javascript-trim-leading-and-trailing-spaces +// http://qodo.co.uk/blog/javascript-trim-leading-and-trailing-spaces // // Parameters: // pString: The string to trim @@ -1348,7 +1299,7 @@ function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSy if ((pKey2.length == 0) && (pDesc2.length == 0)) sepChar2 = " "; printf("ch%-13sg" + sepChar1 + " nc%-28s kh" + VERTICAL_SINGLE + - " ch%-7sg" + sepChar2 + " nc%s", pKey, pDesc, pKey2, pDesc2); + " ch%-8sg" + sepChar2 + " nc%s", pKey, pDesc, pKey2, pDesc2); if (pCR) console.crlf(); } @@ -1369,8 +1320,8 @@ function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSy console.crlf(); // Command/edit keys console.print("ngCommand/edit keys\r\nkh�����������������\r\n"); - displayCmdKeyFormattedDouble("Ctrl-A", "Abort message", "Ctrl-W", "Page up", true); - displayCmdKeyFormattedDouble("Ctrl-Z", "Save message", "Ctrl-S", "Page down", true); + displayCmdKeyFormattedDouble("Ctrl-A", "Abort message", "PageUp", "Page up", true); + displayCmdKeyFormattedDouble("Ctrl-Z", "Save message", "PageDown", "Page down", true); displayCmdKeyFormattedDouble("Ctrl-Q", "Quote message", "Ctrl-N", "Find text", true); displayCmdKeyFormattedDouble("Insert/Ctrl-I", "Toggle insert/overwrite mode", "Ctrl-D", "Delete line", true); @@ -1385,7 +1336,7 @@ function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSy displayCmdKeyFormatted("Ctrl-U", "Your settings", true); if (pPause) - console.pause(); + consolePauseWithESCChars(isSysop); } // Displays the general help screen. @@ -2422,8 +2373,28 @@ function wrapTextLines(pLineArr, pStartLineIndex, pEndIndex, pLineWidth, pIdxesR if (trimmedText.charAt(trimmedText.length-1) != " ") trimmedText += " " } - // Prepend the trimmed text to the next line. - pLineArr[i+1] = trimmedText + pLineArr[i+1]; + // Prepend the trimmed text to the next line. If the next line's index + // is within the paragraph we're wrapping, then go ahead and prepend the + // text to the next line. Otherwise, add a new line to the array and + // add the text to the new line. + if (i+1 < pEndIndex) + pLineArr[i+1] = trimmedText + pLineArr[i+1]; + else + { + // Add the trimmed text on a new line in the array. Then, if the + // trimmed text's length is longer then the allowed line width, then + // we'll want to extend the end index so we can continue wrapping the + // lines in the current paragraph. Otherwise, add the current line's + // index to the array of lines requiring a newline. + pLineArr.splice(i+1, 0, trimmedText); + if (trimmedText.length > pLineWidth) + ++pEndIndex; + else + { + if (pNewLineIndexesIsArray) + pIdxesRequiringNL.push(i); + } + } } else { @@ -2738,6 +2709,8 @@ function wrapQuoteLinesUsingAuthorInitials(pIndentQuoteLines) if (gQuoteLines[quoteLineIndex].length == 0) continue; + // If this line has a different quote level than the previous line, then + // it marks a new section. if (lineInfos[quoteLineIndex].quoteLevel != lastQuoteLevel) { endArrIndex = quoteLineIndex; @@ -2758,10 +2731,34 @@ function wrapQuoteLinesUsingAuthorInitials(pIndentQuoteLines) ++sectionInfo.endArrIndex; quoteSections.push(sectionInfo); - startArrIndex = quoteLineIndex; lastQuoteLevel = lineInfos[quoteLineIndex].quoteLevel; } + // For lines with a quote level of 0, if this line's indentation differs from + // the previous line's indentation, then that marks a new section. + else if ((lineInfos[quoteLineIndex].quoteLevel == 0) && (lastQuoteLevel == 0) && + (lineInfos[quoteLineIndex].startIndex > lineInfos[quoteLineIndex-1].startIndex)) + { + endArrIndex = quoteLineIndex; // One past the last index of the current paragraph + var sectionInfo = new Object(); + sectionInfo.startArrIndex = startArrIndex; + sectionInfo.endArrIndex = endArrIndex; + sectionInfo.quoteLevel = 0; + // If the end array index is for a blank quote line, then + // adjust it to the first non-blank quote line before it. + while ((sectionInfo.endArrIndex-1 >= 0) && + (typeof(gQuoteLines[sectionInfo.endArrIndex-1]) == "string") && + gQuoteLines[sectionInfo.endArrIndex-1].length == 0) + { + --sectionInfo.endArrIndex; + } + // If we moved sectionInfo.endArrIndex back too far, then increment it. + while (typeof(gQuoteLines[sectionInfo.endArrIndex]) != "string") + ++sectionInfo.endArrIndex; + + quoteSections.push(sectionInfo); + startArrIndex = quoteLineIndex; + } } // If we only found one section or we're at the last section, then add it to // quoteSections. @@ -2778,9 +2775,24 @@ function wrapQuoteLinesUsingAuthorInitials(pIndentQuoteLines) quoteSections.push(sectionInfo); } - // 3. Go through each section of the quote lines and quote appropriately + // 3. Go through each section of the quote lines and wrap & quote appropriately for (var sIndex = 0; sIndex < quoteSections.length; ++sIndex) { + // If the section is not quoted text (in other words, it was written by + // author of the message), then remove leading whitespace from the text + // lines in this section to leave more room for wrapping and so that we + // don't end up with a section of quote lines that all start with several + // spaces. + if (quoteSections[sIndex].quoteLevel == 0) + { + for (var i = quoteSections[sIndex].startArrIndex; i < quoteSections[sIndex].endArrIndex; ++i) + { + gQuoteLines[i] = trimSpaces(gQuoteLines[i], true, true, false); + lineInfos[i].startIndex = 0; + lineInfos[i].begOfLine = ""; + } + } + // Remove the quote strings from the lines we're about to wrap var maxBegOfLineLen = 0; for (var i = quoteSections[sIndex].startArrIndex; i < quoteSections[sIndex].endArrIndex; ++i) @@ -2914,34 +2926,29 @@ function wrapQuoteLines_NoAuthorInitials() if (gQuoteLines.length == 0) return; - // Create an array for line information objects, and append the - // first line's info to it. Also, store the first line's quote - // level in the lastQuoteLevel variable. + // Create an array for line information objects. var lineInfos = new Array(); - var retObj = firstNonQuoteTxtIndex(gQuoteLines[0], false, false); - lineInfos.push(retObj); - var lastQuoteLevel = retObj.quoteLevel; + for (var quoteLineIndex = 0; quoteLineIndex < gQuoteLines.length; ++quoteLineIndex) + lineInfos.push(firstNonQuoteTxtIndex(gQuoteLines[quoteLineIndex], false, false)); + + // Set an initial value for lastQuoteLevel, which will be used to compare the + // quote levels of each line. + var lastQuoteLevel = lineInfos[0].quoteLevel; // Loop through the array starting at the 2nd line and wrap the lines var startArrIndex = 0; var endArrIndex = 0; var quoteStr = ""; var quoteLevel = 0; - var retObj = null; var i = 0; // Index variable for (var quoteLineIndex = 1; quoteLineIndex < gQuoteLines.length; ++quoteLineIndex) { - retObj = firstNonQuoteTxtIndex(gQuoteLines[quoteLineIndex], false, false); - lineInfos.push(retObj); - if (retObj.quoteLevel != lastQuoteLevel) + if (lineInfos[quoteLineIndex].quoteLevel != lastQuoteLevel) { endArrIndex = quoteLineIndex; // Remove the quote strings from the lines we're about to wrap for (i = startArrIndex; i < endArrIndex; ++i) { - // TODO - // Error on next line: !JavaScript TypeError: lineInfos[i] is undefined - // Fixed by checking that lineInfos[i] is not null.. but why would it be? if (lineInfos[i] != null) { if (lineInfos[i].startIndex > -1) @@ -2953,10 +2960,10 @@ function wrapQuoteLines_NoAuthorInitials() if (/^ +$/.test(gQuoteLines[i])) gQuoteLines[i] = ""; } } - // Wrap the text lines in the range we've seen + // Wrap the text lines in the range we've seen. // Note: 79 is assumed as the maximum line length because // that seems to be a commonly-accepted message width for - // BBSs. Also, the following length is subtracted from it: + // BBSes. Also, the following length is subtracted from it: // (2*(lastQuoteLevel+1) + gQuotePrefix.length) // That is because we'll be prepending "> " to the quote lines, // and then SlyEdit will prepend gQuotePrefix to them during quoting. @@ -2969,6 +2976,10 @@ function wrapQuoteLines_NoAuthorInitials() { endArrIndex += numLinesAdded; quoteLineIndex += (numLinesAdded-1); // - 1 because quoteLineIndex will be incremented by the for loop + // Splice new lineInfo objects into the lineInfos array at the end of this + // section for each new line added in this section. + for (var counter = 0; counter < numLinesAdded; ++counter) + lineInfos.splice(endArrIndex, 0, getDefaultQuoteStrObj()); } // Put quote strings ("> ") back into the lines we just wrapped if ((quoteLineIndex > 0) && (lastQuoteLevel > 0)) @@ -2979,7 +2990,43 @@ function wrapQuoteLines_NoAuthorInitials() for (i = startArrIndex; i < endArrIndex; ++i) gQuoteLines[i] = quoteStr + gQuoteLines[i].replace(/^\s*>/, ">"); } - lastQuoteLevel = retObj.quoteLevel; + lastQuoteLevel = lineInfos[quoteLineIndex].quoteLevel; + startArrIndex = quoteLineIndex; + } + // For lines with a quote level of 0, if this line's indentation differs from + // the previous line's indentation, then that marks a new section. + else if ((lineInfos[quoteLineIndex].quoteLevel == 0) && (lastQuoteLevel == 0) && + (lineInfos[quoteLineIndex].startIndex > lineInfos[quoteLineIndex-1].startIndex)) + { + endArrIndex = quoteLineIndex; + + // Remove leading whitespace from the text lines in this section to leave + // more room for wrapping and so that we don't end up with a section of + // quote lines that all start with several spaces. + for (var i = startArrIndex; i < endArrIndex; ++i) + { + gQuoteLines[i] = trimSpaces(gQuoteLines[i], true, true, false); + lineInfos[i].startIndex = 0; + lineInfos[i].begOfLine = ""; + } + + // Wrap the text lines in the range we've seen. + // Note: 79 is assumed as the maximum line length because + // that seems to be a commonly-accepted message width for + // BBSes. + var numLinesAdded = wrapTextLines(gQuoteLines, startArrIndex, endArrIndex, 79); + // If quote lines were added as a result of wrapping, then + // determine the number of lines added, and update endArrIndex + // and quoteLineIndex accordingly. + if (numLinesAdded > 0) + { + endArrIndex += numLinesAdded; + quoteLineIndex += (numLinesAdded-1); // - 1 because quoteLineIndex will be incremented by the for loop + // Splice new lineInfo objects into the lineInfos array at the end of this + // section for each new line added in this section. + for (var counter = 0; counter < numLinesAdded; ++counter) + lineInfos.splice(endArrIndex, 0, getDefaultQuoteStrObj()); + } startArrIndex = quoteLineIndex; } } @@ -3626,17 +3673,38 @@ function firstLetterIsUppercase(pString) // // Parameters: // pMode: The input mode flag(s) -// pCfgObj: The configuration object +// pCfgObj: The configuration object (stores the input timeout setting) // // Return value: The user's keypress (the return value of console.getkey() // or console.inkey()). function getUserKey(pMode, pCfgObj) { + var defaultTimeoutMS = 300000; var userKey = ""; - if (!pCfgObj.userInputTimeout || pCfgObj.userIsSysop) - userKey = console.getkey(pMode); - else - userKey = console.inkey(pMode, pCfgObj.inputTimeoutMS); + + if (typeof(pCfgObj) == "object") + { + // If the user is a sysop, don't use an input timeout. + if ((typeof(pCfgObj.userIsSysop) == "boolean") && pCfgObj.userIsSysop) + userKey = console.getkey(pMode); + else if (typeof(pCfgObj.userInputTimeout) == "number") + userKey = console.inkey(pMode, pCfgObj.inputTimeoutMS); + else + userKey = console.inkey(pMode, defaultTimeoutMS); + } + else if (typeof(pCfgObj) == "boolean") + { + // pCfgObj is a boolean that specifies whether or not the user is a sysop. + // If so, then use console.getkey(). If the user isn't a sysop, use a + // timeout of 5 minutes. + if (pCfgObj) + userKey = console.getkey(pMode); + else + userKey = console.inkey(pMode, defaultTimeoutMS); + } + else // pCfgObj is not a known type, so use the default input timeout. + userKey = console.inkey(pMode, defaultTimeoutMS); + return userKey; } @@ -4045,6 +4113,78 @@ function shuffleArray(pArray) return pArray; } +// Performs the same function as console.pause(), but also allows input of multi-key +// sequences such as PageUp, PageDown, F1, etc. without writing extra characters on +// the screen. +// +// Parameters: +// pCfgObj: Optional - The configuration object, which specifies the input timeout. +function consolePauseWithESCChars(pCfgObj) +{ + console.print("\1n" + bbs.text(563)); // 563 is the "Press a key" text in text.dat + getKeyWithESCChars(K_NOSPIN|K_NOCRLF|K_NOECHO, 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), +// respectively. Also, F1-F5 will be returned as "\1F1" +// through "\1F5", respectively. +// Thanks goes to Psi-Jack for the original impementation +// of this function. +// +// Parameters: +// pGetKeyMode: Optional - The mode bits for console.getkey(). +// If not specified, K_NONE will be used. +// pCfgObj: The configuration object (stores the input timeout setting) +// +// Return value: The user's keypress +function getKeyWithESCChars(pGetKeyMode, pCfgObj) +{ + var getKeyMode = K_NONE; + if (typeof(pGetKeyMode) == "number") + getKeyMode = pGetKeyMode; + + var userInput = getUserKey(getKeyMode, pCfgObj); + 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 = KEY_F1; + break; + case 'Q': + userInput = KEY_F2; + break; + case 'R': + userInput = KEY_F3; + break; + case 'S': + userInput = KEY_F4; + break; + case 't': + userInput = KEY_F5; + break; + } + default: + break; + } + } + + return userInput; +} + // This function displays debug text at a given location on the screen, then // moves the cursor back to a given location. // -- GitLab