diff --git a/exec/SlyEdit.js b/exec/SlyEdit.js index 98254de6e5ba1a25c7f0846be0dbd9b6b4fd950a..16515fe8d7b779cd6de3a65770d3f269eef0a75e 100644 --- a/exec/SlyEdit.js +++ b/exec/SlyEdit.js @@ -47,6 +47,13 @@ * Bug fix: Updated ReadSlyEditConfigFile() to * default cfgObj.genColors.txtReplacementList to * ensure that it gets defined. + * Bug fix: Made use of K_NOSPIN wherever user input + * is done so that the spinning cursor doesn't overwrite + * anything on the screen. + * Code refactor: Moved doMacroTxtReplacementInEditLine() + * and getWordFromEditLine() to TextLine member + * methods TextLine_doMacroTxtReplacement() and + * TextLine_getWord() in SlyEdit_Misc.js. */ /* Command-line arguments: @@ -124,7 +131,6 @@ const EDITOR_VER_DATE = "2013-09-07"; // Program variables -var gIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop var gEditTop = 6; // The top line of the edit area var gEditBottom = console.screen_rows-2; // The last line of the edit area // gEditLeft and gEditRight are the rightmost and leftmost columns of the edit @@ -814,15 +820,7 @@ function doEditLoop() var continueOn = true; while (continueOn) { - // Get a key, and time out after 5 minutes. - // Get a keypress from the user. If the setting for using the - // input timeout is enabled and the user is not a sysop, then use - // the input timeout specified in the config file. Otherwise, - // don't use a timeout. - if (gConfigSettings.userInputTimeout && !gIsSysop) - userInput = console.inkey(K_NOCRLF|K_NOSPIN, gConfigSettings.inputTimeoutMS); - else - userInput = console.getkey(K_NOCRLF|K_NOSPIN); + userInput = getUserKey(K_NOCRLF|K_NOSPIN, gConfigSettings); // If userInput is blank, then the input timeout was probably // reached, so abort. if (userInput == "") @@ -864,7 +862,7 @@ function doEditLoop() continueOn = false; break; case CMDLIST_HELP_KEY: - displayCommandList(true, true, true, gCanCrossPost, gIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(curpos.y-gEditTop), @@ -904,9 +902,8 @@ function doEditLoop() } break; case CHANGE_COLOR_KEY: - /* // Let the user change the text color. - if (gConfigSettings.allowColorSelection) + /*if (gConfigSettings.allowColorSelection) { var retObject = doColorSelection(curpos, currentWordLength); curpos.x = retObject.x; @@ -921,8 +918,7 @@ function doEditLoop() console.print("nhr" + EDITOR_PROGRAM_NAME + ": Input timeout reached."); continue; } - } - */ + }*/ break; case KEY_UP: // Move the cursor up one line. @@ -1162,7 +1158,7 @@ function doEditLoop() else if (retObject.showHelp) { displayProgramInfo(true, false); - displayCommandList(false, false, true, gCanCrossPost, gIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(curpos.y-gEditTop), @@ -1211,9 +1207,9 @@ function doEditLoop() break; case IMPORT_FILE_KEY: // Only let sysops import files. - if (gIsSysop) + if (gConfigSettings.userIsSysop) { - var retObj = importFile(gIsSysop, curpos); + var retObj = importFile(gConfigSettings.userIsSysop, curpos); curpos.x = retObj.x; curpos.y = retObj.y; currentWordLength = retObj.currentWordLength; @@ -1222,9 +1218,9 @@ function doEditLoop() break; case EXPORT_FILE_KEY: // Only let sysops export files. - if (gIsSysop) + if (gConfigSettings.userIsSysop) { - exportToFile(gIsSysop); + exportToFile(gConfigSettings.userIsSysop); console.gotoxy(curpos); } break; @@ -1780,9 +1776,8 @@ function doPrintableChar(pUserInput, pCurpos, pCurrentWordLength) var madeTxtReplacement = false; // For screen refresh purposes if (gConfigSettings.enableTextReplacements && (pUserInput == " ")) { - var txtReplaceObj = doMacroTxtReplacementInEditLine(gTxtReplacements, gEditLinesIndex, - gTextLineIndex, - gConfigSettings.textReplacementsUseRegex); + var txtReplaceObj = gEditLines[gEditLinesIndex].doMacroTxtReplacement(gTxtReplacements, gTextLineIndex, + gConfigSettings.textReplacementsUseRegex); madeTxtReplacement = txtReplaceObj.madeTxtReplacement; if (madeTxtReplacement) { @@ -2068,9 +2063,8 @@ function doEnterKey(pCurpos, pCurrentWordLength) var cursorHorizDiff = 0; if (gConfigSettings.enableTextReplacements) { - var txtReplaceObj = doMacroTxtReplacementInEditLine(gTxtReplacements, gEditLinesIndex, - gTextLineIndex-1, - gConfigSettings.textReplacementsUseRegex); + var txtReplaceObj = gEditLines[gEditLinesIndex].doMacroTxtReplacement(gTxtReplacements, gTextLineIndex-1, + gConfigSettings.textReplacementsUseRegex); if (txtReplaceObj.madeTxtReplacement) { gTextLineIndex += txtReplaceObj.wordLenDiff; @@ -2346,8 +2340,8 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) var continueOn = true; while (continueOn) { - // Get a key, and time out after 1 minute. - userInput = console.inkey(0, 100000); + // Get a keypress from the user + userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); if (userInput == "") { // The input timeout was reached. Abort. @@ -2965,7 +2959,7 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) var editLineDiff = pCurpos.y - gEditTop; var menuChoice = doDCTMenu(gEditLeft, gEditRight, gEditTop, displayMessageRectangle, gEditLinesIndex, - editLineDiff, gIsSysop, gCanCrossPost); + editLineDiff, gConfigSettings.userIsSysop, gCanCrossPost); // Take action according to the user's choice. // Save if ((menuChoice == "S") || (menuChoice == CTRL_Z) || @@ -2997,7 +2991,7 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // Import file (sysop only) else if (menuChoice == DCTMENU_SYSOP_IMPORT_FILE) { - var retval = importFile(gIsSysop, pCurpos); + var retval = importFile(gConfigSettings.userIsSysop, pCurpos); returnObj.x = retval.x; returnObj.y = retval.y; returnObj.currentWordLength = retval.currentWordLength; @@ -3005,9 +2999,9 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // Import file for sysop, or Insert/Overwrite toggle for non-sysop else if (menuChoice == "I") { - if (gIsSysop) + if (gConfigSettings.userIsSysop) { - var retval = importFile(gIsSysop, pCurpos); + var retval = importFile(gConfigSettings.userIsSysop, pCurpos); returnObj.x = retval.x; returnObj.y = retval.y; returnObj.currentWordLength = retval.currentWordLength; @@ -3026,7 +3020,7 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // Command List else if ((menuChoice == "O") || (menuChoice == DCTMENU_HELP_COMMAND_LIST)) { - displayCommandList(true, true, true, gCanCrossPost, gIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(pCurpos.y-gEditTop), @@ -3053,9 +3047,9 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // Export the message else if ((menuChoice == "X") || (menuChoice == DCTMENU_SYSOP_EXPORT_FILE)) { - if (gIsSysop) + if (gConfigSettings.userIsSysop) { - exportToFile(gIsSysop); + exportToFile(gConfigSettings.userIsSysop); console.gotoxy(returnObj.x, returnObj.y); } } @@ -3065,12 +3059,17 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // We don't need to do do anything in here. } // Cross-post - else if ((menuChoice == CTRL_C) || (menuChoice == "C") || - (menuChoice == DCTMENU_CROSS_POST)) + else if ((menuChoice == CTRL_C) || (menuChoice == "C") || (menuChoice == DCTMENU_CROSS_POST)) { if (gCanCrossPost) doCrossPosting(pCurpos); } + // List text replacements + else if ((menuChoice == CTRL_T) || (menuChoice == "T") || (menuChoice == DCTMENU_LIST_TXT_REPLACEMENTS)) + { + if (gConfigSettings.enableTextReplacements) + listTextReplacements(); + } // Make sure the edit color attribute is set back. //console.print("n" + gTextAttrs); @@ -3126,7 +3125,7 @@ function handleIceESCMenu(pCurpos, pCurrentWordLength) break; case ICE_ESC_MENU_HELP: displayProgramInfo(true, false); - displayCommandList(false, false, true, gCanCrossPost, gIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(pCurpos.y-gEditTop), @@ -3693,11 +3692,12 @@ function doColorSelection(pCurpos, pCurrentWordLength) console.cleartoeol("n"); console.crlf(); console.clearline("n"); + //Special: H:High Intensity I:Blinking N:Normal � Choose Color: console.print("Special: whH:n" + gTextAttrs + "hHigh Intensity wI:n" + gTextAttrs + "iBlinking nwhN:nNormal c� nChoose Color: "); var attr = FORE_ATTR; var toggle = true; //var key = console.getkeys("KRGYBMCW01234567HIN").toString(); // Outputs a CR.. bad - var key = console.getkey(K_UPPER|K_NOCRLF); + var key = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); switch (key) { // Foreground colors: @@ -3791,8 +3791,8 @@ function doColorSelection(pCurpos, pCurrentWordLength) var continueOn = true; while (continueOn) { - // Get a key, and time out after 1 minute. - userInput = console.inkey(0, 100000); + // Get a keypress from the user + userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); if (userInput == "") { // The input timeout was reached. Abort. @@ -4199,7 +4199,7 @@ function doCrossPosting(pOriginalCurpos) pageNum = calcPageNum(topMsgGrpIndex, selBoxInnerHeight); // Get a key from the user (upper-case) and take action based upon it. - userInput = console.getkey(K_UPPER | K_NOCRLF); + userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); switch (userInput) { case KEY_UP: // Move up one message group in the list @@ -4774,7 +4774,7 @@ function crossPosting_selectSubBoardInGrp(pGrpIndex, pSelBoxUpperLeft, pSelBoxLo pageNum = calcPageNum(topMsgSubIndex, pSelBoxInnerHeight); // Get a key from the user (upper-case) and take action based upon it. - userInput = console.getkey(K_UPPER | K_NOCRLF); + userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); switch (userInput) { case KEY_UP: // Move up one message sub-board in the list @@ -5467,7 +5467,7 @@ function listTextReplacements() } // Get a key from the user (upper-case) and take action based upon it. - userInput = console.getkey(K_UPPER | K_NOCRLF); + userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); switch (userInput) { case KEY_UP: diff --git a/exec/SlyEdit_DCTStuff.js b/exec/SlyEdit_DCTStuff.js index aed6ffb05af5689cd85029e9ca9cc60e602ea072..ce02c11551d8588f976763d8995cabb08a6dbe8c 100644 --- a/exec/SlyEdit_DCTStuff.js +++ b/exec/SlyEdit_DCTStuff.js @@ -65,6 +65,7 @@ var DCTMENU_HELP_COMMAND_LIST = 8; var DCTMENU_HELP_GENERAL = 9; var DCTMENU_HELP_PROGRAM_INFO = 10; var DCTMENU_CROSS_POST = 12; +var DCTMENU_LIST_TXT_REPLACEMENTS = 13; // Read the color configuration file readColorConfig(gConfigSettings.DCTColors.ThemeFilename); @@ -582,8 +583,8 @@ function promptYesNo_DCTStyle(pQuestion, pBoxTitle, pDefaultYes, pParamObj) // Move the cursor where it needs to be to write the "Yes" // or "No" console.gotoxy(boxX+20, boxY+2); - // Get a key, (time out after 1 minute), and take appropriate action. - userInput = console.inkey(0, 100000).toUpperCase(); + // Get a key and take appropriate action. + userInput = getUserKey(K_UPPER|K_NOECHO|K_NOCRLF|K_NOSPIN, gConfigSettings); if (userInput == KEY_ENTER) continueOn = false; else if (userInput == "Y") @@ -818,6 +819,15 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, doDCTMenu.allMenus[helpMenuNum].addItem("C&ommand List Ctrl-P", DCTMENU_HELP_COMMAND_LIST); doDCTMenu.allMenus[helpMenuNum].addItem("&General Help Ctrl-G", DCTMENU_HELP_GENERAL); doDCTMenu.allMenus[helpMenuNum].addItem("&Program Info Ctrl-R", DCTMENU_HELP_PROGRAM_INFO); + if (gConfigSettings.enableTextReplacements) + { + // For some reason, Ctrl-T isn't working properly in this context - It + // exits the help menu but doesn't exit the menu overall. So for now, + // I'm not showing or allowing Ctrl-T in this context. + //doDCTMenu.allMenus[helpMenuNum].addItem("&Text replcmts Ctrl-T", DCTMENU_LIST_TXT_REPLACEMENTS); + //doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_T, DCTMENU_LIST_TXT_REPLACEMENTS); + doDCTMenu.allMenus[helpMenuNum].addItem("&Text replcmts ", DCTMENU_LIST_TXT_REPLACEMENTS); + } doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_P, DCTMENU_HELP_COMMAND_LIST); doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_G, DCTMENU_HELP_GENERAL); doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_R, DCTMENU_HELP_PROGRAM_INFO); @@ -865,7 +875,7 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, userInput = menuRetObj.userInput; // If nothing from the menu was matched, then get a key from the user. else - userInput = console.inkey(K_UPPER, 60000); + userInput = getUserKey(K_UPPER|K_NOECHO|K_NOCRLF|K_NOSPIN, gConfigSettings); menuRetObj.userInput = ""; // If a menu return code was matched or userInput is blank (the @@ -928,6 +938,11 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, if (pCanCrossPost) continueOn = false; break; + case "T": // List text replacements + //case CTRL_T: // List text replacements + if (gConfigSettings.enableTextReplacements) + continueOn = false; + break; default: break; } @@ -967,6 +982,8 @@ function valMatchesMenuCode(pVal, pIsSysop) (pVal == DCTMENU_EDIT_FIND_TEXT) || (pVal == DCTMENU_HELP_COMMAND_LIST) || (pVal == DCTMENU_HELP_GENERAL) || (pVal == DCTMENU_HELP_PROGRAM_INFO)); } + if (gConfigSettings.enableTextReplacements) + valMatch = (valMatches || DCTMENU_LIST_TXT_REPLACEMENTS); return valMatches; } @@ -983,7 +1000,7 @@ function inputMatchesMenuSelection(pInput, pIsSysop) (pInput == "S") || (pInput == "A") || (pInput == "E") || (pInput == "I") || (pIsSysop && (pInput == "X")) || (pInput == "F") || (pInput == "C") || (pInput == "G") || - (pInput == "P")); + (pInput == "P") || (pInput == "T")); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1341,8 +1358,7 @@ function DCTMenu_DoInputLoop() while (continueOn) { // Get a key, (time out after the selected time), and take appropriate action. - //returnObj.userInput = console.inkey(0, this.timeoutMS).toUpperCase(); - returnObj.userInput = console.getkey(K_NONE); + returnObj.userInput = getUserKey(K_UPPER|K_NOECHO|K_NOCRLF|K_NOSPIN, gConfigSettings); // If the user input is blank, then the input timed out, and we should quit. if (returnObj.userInput == "") { diff --git a/exec/SlyEdit_IceStuff.js b/exec/SlyEdit_IceStuff.js index 0210190e18d58737196b91a284558005532a0cf9..d3c9d014ab8c34b70c7701004272b8ddf5259c4b 100644 --- a/exec/SlyEdit_IceStuff.js +++ b/exec/SlyEdit_IceStuff.js @@ -515,8 +515,8 @@ function promptYesNo_IceStyle(pQuestion, pDefaultYes) // Move the cursor to the start of the "Yes" or "No" text (whichever // one is currently selected). console.gotoxy(userResponse ? yesNoX : yesNoX+7, console.screen_rows); - // Get a key, (time out after 1 minute), and take appropriate action. - userInput = console.inkey(0, 100000).toUpperCase(); + // Get a keypress from the user and take appropriate action. + userInput = getUserKey(K_UPPER|K_NOECHO|K_NOCRLF|K_NOSPIN, gConfigSettings); // If userInput is blank, then the timeout was hit, so exit the loop. // Also exit the loop of the user pressed enter. if ((userInput == "") || (userInput == KEY_ENTER)) @@ -711,7 +711,7 @@ function doIceESCMenu(pY, pCanCrossPost) } // Get the user's choice - userInput = console.getkey(K_UPPER|K_ALPHA|K_NOECHO|K_NOSPIN|K_NOCRLF); + userInput = getUserKey(K_UPPER|K_NOECHO|K_NOCRLF|K_NOSPIN, gConfigSettings); switch (userInput) { case KEY_UP: diff --git a/exec/SlyEdit_Misc.js b/exec/SlyEdit_Misc.js index 1450b7cd75fb9c588a4d1e38f2179ae3adefed54..8e536bc47aeed50569038fa4f7d761e64773ab4b 100644 --- a/exec/SlyEdit_Misc.js +++ b/exec/SlyEdit_Misc.js @@ -47,6 +47,10 @@ * 2013-09-07 Eric Oulashin Bug fix: Updated ReadSlyEditConfigFile() to * default cfgObj.genColors.txtReplacementList to * ensure that it gets defined. + * Code refactor: Moved doMacroTxtReplacementInEditLine() + * and getWordFromEditLine() to TextLine member + * methods TextLine_doMacroTxtReplacement() and + * TextLine_getWord(). */ // Note: These variables are declared with "var" instead of "const" to avoid @@ -177,6 +181,8 @@ function TextLine(pText, pHardNewlineEnd, pIsQuoteLine) // Functions this.length = TextLine_Length; this.print = TextLine_Print; + this.doMacroTxtReplacement = TextLine_doMacroTxtReplacement; + this.getWord = TextLine_getWord; } // For the TextLine class: Returns the length of the text. function TextLine_Length() @@ -194,6 +200,171 @@ function TextLine_Print(pClearToEOL) if (pClearToEOL) console.cleartoeol(); } +// Performs text replacement (AKA macro replacement) in the text line. +// +// Parameters: +// pTxtReplacements: An associative array of text to be replaced (i.e., +// gTxtReplacements) +// pCharIndex: The current character index in the text line +// pUseRegex: Whether or not to treat the text replacement search string as a +// regular expression. +// +// Return value: An object containing the following properties: +// textLineIndex: The updated text line index (integer) +// wordLenDiff: The change in length of the word that +// was replaced (integer) +// wordStartIdx: The index of the first character in the word. +// Only valid if a word was found. Otherwise, this +// will be 0. +// newTextEndIdx: The index of the last character in the new +// text. Only valid if a word was replaced. +// Otherwise, this will be 0. +// newTextLen: The length of the new text in the string. Will be +// the length of the existing word if the word wasn't +// replaced or 0 if no word was found. +// madeTxtReplacement: Whether or not a text replacement was made +// (boolean) +function TextLine_doMacroTxtReplacement(pTxtReplacements, pCharIndex, pUseRegex) +{ + var retObj = new Object(); + retObj.textLineIndex = pCharIndex; + retObj.wordLenDiff = 0; + retObj.wordStartIdx = 0; + retObj.newTextEndIdx = 0; + retObj.newTextLen = 0; + retObj.madeTxtReplacement = false; + + var wordObj = this.getWord(retObj.textLineIndex); + if (wordObj.foundWord) + { + retObj.wordStartIdx = wordObj.startIdx; + retObj.newTextLen = wordObj.word.length; + + // See if the word starts with a capital letter; if so, we'll capitalize + // the replacement word. + var firstCharUpper = false; + var txtReplacement = ""; + if (pUseRegex) + { + // Since a regular expression might have more characters in addition + // to the actual word, we need to go through all the replacement strings + // in pTxtReplacements and use the first one that changes the text. + for (var prop in pTxtReplacements) + { + if (pTxtReplacements.hasOwnProperty(prop)) + { + var regex = new RegExp(prop); + txtReplacement = wordObj.word.replace(regex, pTxtReplacements[prop]); + retObj.madeTxtReplacement = (txtReplacement != wordObj.word); + // If a text replacement was made, then check and see if the first + // letter in the original text was uppercase, and if so, make the + // first letter in the new text (txtReplacement) uppercase. + if (retObj.madeTxtReplacement) + { + if (firstLetterIsUppercase(wordObj.word)) + { + var letterInfo = getFirstLetterFromStr(txtReplacement); + if (letterInfo.idx > -1) + { + txtReplacement = txtReplacement.substr(0, letterInfo.idx) + + letterInfo.letter.toUpperCase() + + txtReplacement.substr(letterInfo.idx+1); + } + } + // Now that we've made a text replacement, stop going through + // pTxtReplacements looking for a matching regex. + break; + } + } + } + } + else + { + // Not using a regular expression. + firstCharUpper = (wordObj.word.charAt(0) == wordObj.word.charAt(0).toUpperCase()); + // Convert the word to all uppercase to do the case-insensitive lookup + // in pTxtReplacements. + wordObj.word = wordObj.word.toUpperCase(); + if (pTxtReplacements.hasOwnProperty(wordObj.word)) + { + txtReplacement = pTxtReplacements[wordObj.word]; + retObj.madeTxtReplacement = true; + } + } + if (retObj.madeTxtReplacement) + { + if (firstCharUpper) + txtReplacement = txtReplacement.charAt(0).toUpperCase() + txtReplacement.substr(1); + this.text = this.text.substr(0, wordObj.startIdx) + txtReplacement + + this.text.substr(wordObj.endIndex+1); + // Based on the difference in word length, update the data that + // matters (retObj.textLineIndex, which keeps track of the index of the current line). + // Note: The horizontal cursor position variable should be replaced after calling this + // function. + retObj.wordLenDiff = txtReplacement.length - wordObj.word.length; + retObj.textLineIndex += retObj.wordLenDiff; + retObj.newTextEndIdx = wordObj.endIndex + retObj.wordLenDiff; + retObj.newTextLen = txtReplacement.length; + } + } + + return retObj; +} +// Returns the word in a text line at a given index. If the index +// is at a space, then this function will return the word before +// (to the left of) the space. +// +// Parameters: +// pEditLinesIndex: The index of the line to look at (0-based) +// pCharIndex: The character index in the text line (0-based) +// +// Return value: An object containing the following properties: +// foundWord: Whether or not a word was found (boolean) +// word: The word in the edit line at the given indexes (text). +// This might include control/color codes, etc.. +// plainWord: The word in the edit line without any control +// or color codes, etc. This may or may not be +// the same as word. +// startIdx: The index of the first character of the word (integer) +// endIndex: The index of the last character of the word (integer) +// This includes any control/color codes, etc. +function TextLine_getWord(pCharIndex) +{ + var retObj = new Object(); + retObj.foundWord = false; + retObj.word = ""; + retObj.plainWord = ""; + retObj.startIdx = 0; + retObj.endIndex = 0; + + // Parameter checking + if ((pCharIndex < 0) || (pCharIndex >= this.text.length)) + return retObj; + + // If pCharIndex specifies the index of a space, then look for a non-space + // character before it. + var charIndex = pCharIndex; + while (this.text.charAt(charIndex) == " ") + --charIndex; + // Look for the start & end of the word based on the indexes of a space + // before and at/after the given character index. + var wordStartIdx = charIndex; + var wordEndIdx = charIndex; + while ((this.text.charAt(wordStartIdx) != " ") && (wordStartIdx >= 0)) + --wordStartIdx; + ++wordStartIdx; + while ((this.text.charAt(wordEndIdx) != " ") && (wordEndIdx < this.text.length)) + ++wordEndIdx; + --wordEndIdx; + + retObj.foundWord = true; + retObj.startIdx = wordStartIdx; + retObj.endIndex = wordEndIdx; + retObj.word = this.text.substring(wordStartIdx, wordEndIdx+1); + retObj.plainWord = strip_ctrl(retObj.word); + return retObj; +} + // AbortConfirmFuncParams constructor: This object contains parameters used by // the abort confirmation function (actually, there are separate ones for @@ -634,6 +805,7 @@ function ReadSlyEditConfigFile() cfgObj.allowCrossPosting = true; cfgObj.enableTextReplacements = false; cfgObj.textReplacementsUseRegex = false; + cfgObj.userIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop // General SlyEdit color settings cfgObj.genColors = new Object(); @@ -2365,7 +2537,7 @@ function populateTxtReplacements(pArray, pRegex) function moveGenColorsToGenSettings(pColorsArray, pCfgObj) { - // Set up an array of color setting names + // Set up an array of color setting names var colorSettingStrings = new Array(); colorSettingStrings.push("crossPostBorder"); // Deprecated colorSettingStrings.push("crossPostBorderText"); // Deprecated @@ -2426,179 +2598,6 @@ function charIsLetter(pChar) return /^[ABCDEFGHIJKLMNOPQRSTUVWXYZ�������������������������������������������������������������]$/.test(pChar.toUpperCase()); } -// Returns the word in a text line at a given index. If the index -// is at a space, then this function will return the word before -// (to the left of) the space. -// -// Parameters: -// pEditLinesIndex: The index of the line to look at (0-based) -// pCharIndex: The character index in the text line (0-based) -// -// Return value: An object containing the following properties: -// foundWord: Whether or not a word was found (boolean) -// word: The word in the edit line at the given indexes (text) -// editLineIndex: The index of the edit line (integer) -// startIdx: The index of the first character of the word (integer) -// endIndex: The index of the last character of the word (integer) -function getWordFromEditLine(pEditLinesIndex, pCharIndex) -{ - var retObj = new Object(); - retObj.foundWord = false; - retObj.word = ""; - retObj.editLineIndex = pEditLinesIndex; - retObj.startIdx = 0; - retObj.endIndex = 0; - - // Parameter checking - if ((pEditLinesIndex < 0) || (pEditLinesIndex >= gEditLines.length)) - { - retObj.editLineIndex = 0; - return retObj; - } - if ((pCharIndex < 0) || (pCharIndex >= gEditLines[pEditLinesIndex].text.length)) - { - //displayDebugText(1, 1, "pCharIndex: " + pCharIndex, null, true, false); // Temporary - //displayDebugText(1, 2, "Line len: " + gEditLines[pEditLinesIndex].text.length, console.getxy(), true, false); // Temporary - return retObj; - } - - // If pCharIndex specifies the index of a space, then look for a non-space - // character before it. - var charIndex = pCharIndex; - while (gEditLines[pEditLinesIndex].text.charAt(charIndex) == " ") - --charIndex; - // Look for the start & end of the word based on the indexes of a space - // before and at/after the given character index. - var wordStartIdx = charIndex; - var wordEndIdx = charIndex; - while ((gEditLines[pEditLinesIndex].text.charAt(wordStartIdx) != " ") && (wordStartIdx >= 0)) - --wordStartIdx; - ++wordStartIdx; - while ((gEditLines[pEditLinesIndex].text.charAt(wordEndIdx) != " ") && (wordEndIdx < gEditLines[pEditLinesIndex].text.length)) - ++wordEndIdx; - --wordEndIdx; - - retObj.foundWord = true; - retObj.startIdx = wordStartIdx; - retObj.endIndex = wordEndIdx; - retObj.word = gEditLines[pEditLinesIndex].text.substring(wordStartIdx, wordEndIdx+1); - return retObj; -} - -// Performs text replacement (AKA macro replacement) in an edit line. -// -// Parameters: -// pTxtReplacements: An associative array of text to be replaced (i.e., -// gTxtReplacements) -// pEditLinesIndex: The index of the line in gEditLines -// pCharIndex: The current character index in the text line -// pUseRegex: Whether or not to treat the text replacement search string as a -// regular expression. -// -// Return value: An object containing the following properties: -// textLineIndex: The updated text line index (integer) -// wordLenDiff: The change in length of the word that -// was replaced (integer) -// wordStartIdx: The index of the first character in the word. -// Only valid if a word was found. Otherwise, this -// will be 0. -// newTextEndIdx: The index of the last character in the new -// text. Only valid if a word was replaced. -// Otherwise, this will be 0. -// newTextLen: The length of the new text in the string. Will be -// the length of the existing word if the word wasn't -// replaced or 0 if no word was found. -// madeTxtReplacement: Whether or not a text replacement was made -// (boolean) -function doMacroTxtReplacementInEditLine(pTxtReplacements, pEditLinesIndex, pCharIndex, pUseRegex) -{ - var retObj = new Object(); - retObj.textLineIndex = pCharIndex; - retObj.wordLenDiff = 0; - retObj.wordStartIdx = 0; - retObj.newTextEndIdx = 0; - retObj.newTextLen = 0; - retObj.madeTxtReplacement = false; - - var wordObj = getWordFromEditLine(pEditLinesIndex, retObj.textLineIndex); - if (wordObj.foundWord) - { - retObj.wordStartIdx = wordObj.startIdx; - retObj.newTextLen = wordObj.word.length; - - // See if the word starts with a capital letter; if so, we'll capitalize - // the replacement word. - //var firstCharUpper = (wordObj.word.charAt(0) == wordObj.word.charAt(0).toUpperCase()); - var firstCharUpper = false; - var txtReplacement = ""; - if (pUseRegex) - { - // Since a regular expression might have more characters in addition - // to the actual word, we need to go through all the replacement strings - // in pTxtReplacements and use the first one that changes the text. - for (var prop in pTxtReplacements) - { - if (pTxtReplacements.hasOwnProperty(prop)) - { - var regex = new RegExp(prop); - txtReplacement = wordObj.word.replace(regex, pTxtReplacements[prop]); - retObj.madeTxtReplacement = (txtReplacement != wordObj.word); - // If a text replacement was made, then check and see if the first - // letter in the original text was uppercase, and if so, make the - // first letter in the new text (txtReplacement) uppercase. - if (retObj.madeTxtReplacement) - { - if (firstLetterIsUppercase(wordObj.word)) - { - var letterInfo = getFirstLetterFromStr(txtReplacement); - if (letterInfo.idx > -1) - { - txtReplacement = txtReplacement.substr(0, letterInfo.idx) - + letterInfo.letter.toUpperCase() - + txtReplacement.substr(letterInfo.idx+1); - } - } - // Now that we've made a text replacement, stop going through - // pTxtReplacements looking for a matching regex. - break; - } - } - } - } - else - { - // Not using a regular expression. - firstCharUpper = (wordObj.word.charAt(0) == wordObj.word.charAt(0).toUpperCase()); - // Convert the word to all uppercase to do the case-insensitive lookup - // in pTxtReplacements. - wordObj.word = wordObj.word.toUpperCase(); - if (pTxtReplacements.hasOwnProperty(wordObj.word)) - { - txtReplacement = pTxtReplacements[wordObj.word]; - retObj.madeTxtReplacement = true; - } - } - if (retObj.madeTxtReplacement) - { - if (firstCharUpper) - txtReplacement = txtReplacement.charAt(0).toUpperCase() + txtReplacement.substr(1); - gEditLines[pEditLinesIndex].text = gEditLines[pEditLinesIndex].text.substr(0, wordObj.startIdx) - + txtReplacement - + gEditLines[pEditLinesIndex].text.substr(wordObj.endIndex+1); - // Based on the difference in word length, update the data that - // matters (retObj.textLineIndex, which keeps track of the index of the current line). - // Note: The horizontal cursor position variable should be replaced after calling this - // function. - retObj.wordLenDiff = txtReplacement.length - wordObj.word.length; - retObj.textLineIndex += retObj.wordLenDiff; - retObj.newTextEndIdx = wordObj.endIndex + retObj.wordLenDiff; - retObj.newTextLen = txtReplacement.length; - } - } - - return retObj; -} - // For configuration files, this function returns a fully-pathed filename. // This function first checks to see if the file exists in the sbbs/mods // directory, then the sbbs/ctrl directory, and if the file is not found there, @@ -2674,6 +2673,26 @@ function firstLetterIsUppercase(pString) return firstIsUpper; } +// Gets a keypress from the user. Uses the configured timeout if configured to +// do so and the user is not a sysop; otherwise (no timeout configured or the +// user is a sysop), the configured input timeout will be used. +// +// Parameters: +// pMode: The input mode flag(s) +// pCfgObj: The configuration object +// +// Return value: The user's keypress (the return value of console.getkey() +// or console.inkey()). +function getUserKey(pMode, pCfgObj) +{ + var userKey = ""; + if (!pCfgObj.userInputTimeout || pCfgObj.userIsSysop) + userKey = console.getkey(pMode); + else + userKey = console.inkey(pMode, pCfgObj.inputTimeoutMS); + return userKey; +} + // This function displays debug text at a given location on the screen, then // moves the cursor back to a given location. //