diff --git a/exec/SlyEdit.js b/exec/SlyEdit.js index d0119a91776289d371dd6c658ee68cbbc5604d9a..809db2f2332969f5f2ded77a67831e6a9c8697bf 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 0fb324b3b8ce677af944c9f8ec2d59a71bb69a8b..c9ae30ebb5fc07066a5519439e6570b70be770e0 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 5e8c5f24bd0ba1336a98bfa488037ae28bd0109f..7ef827193dba9358882b769dc4c9728fb5b417f8 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 12e02c9624d7bc15ca0da08d22f43703cba323fe..ade4bf7b1471159bc3e18539d7e4ee2002dca181 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. //