diff --git a/xtrn/DDMsgReader/DDMsgReader.js b/xtrn/DDMsgReader/DDMsgReader.js index 78dabc5681c7341b6b12ab112dbb02c2c547407b..2a2e2155368845d811534e238cd4bd59b686e694 100644 --- a/xtrn/DDMsgReader/DDMsgReader.js +++ b/xtrn/DDMsgReader/DDMsgReader.js @@ -105,6 +105,9 @@ * New configurable colors in the theme file for the indexed newscan menu * header text (indexMenuHeader), "NEW" indicator text (indexMenuNewIndicator), * and highlighted "NEW" indicator text (indexMenuNewIndicatorHighlight) + * 2023-12-26 Eric Oulashin Version 1.91 + * New sysop features while reading a message: Show message hex (with the X key) + * and save message hex to a file (with Ctrl-X) */ "use strict"; @@ -206,11 +209,12 @@ require("graphic.js", 'Graphic'); require("smbdefs.js", "SMB_POLL_ANSWER"); load('822header.js'); var ansiterm = require("ansiterm_lib.js", 'expand_ctrl_a'); +var hexdump = load('hexdump_lib.js'); // Reader version information -var READER_VERSION = "1.90b"; -var READER_DATE = "2023-12-15"; +var READER_VERSION = "1.91"; +var READER_DATE = "2023-12-26"; // Keyboard key codes for displaying on the screen var UP_ARROW = ascii(24); @@ -790,6 +794,9 @@ function DigDistMsgReader(pSubBoardCode, pScriptArgs) this.GetExtdMsgHdrInfo = DigDistMsgReader_GetExtdMsgHdrInfo; this.GetMsgHdrFieldListText = DigDistMsgReader_GetMsgHdrFieldListText; this.GetMsgInfoForEnhancedReader = DigDistMsgReader_GetMsgInfoForEnhancedReader; + this.ShowMsgHex_Scrolling = DigDistMsgReader_ShowMsgHex_Scrolling; + this.GetMsgHexInfo = DigDistMsgReader_GetMsgHexInfo; + this.SaveMsgHexDumpToFile = DigDistMsgReader_SaveMsgHexDumpToFile; this.GetLastReadMsgIdxAndNum = DigDistMsgReader_GetLastReadMsgIdxAndNum; this.GetScanPtrMsgIdx = DigDistMsgReader_GetScanPtrMsgIdx; this.RemoveFromSearchResults = DigDistMsgReader_RemoveFromSearchResults; @@ -1039,6 +1046,8 @@ function DigDistMsgReader(pSubBoardCode, pScriptArgs) validateMsg: "A", // Only if the user is a sysop quickValUser: CTRL_Q, operatorMenu: CTRL_O, + showMsgHex: "X", + hexDump: CTRL_X, threadView: "*" // TODO: Implement this }; //if (user.is_sysop) @@ -1523,8 +1532,10 @@ function DigDistMsgReader(pSubBoardCode, pScriptArgs) showHdrLines: 2, showKludgeLines: 3, showTallyStats: 4, - quickValUser: 5, - addAuthorToTwitList: 6 + showMsgHex: 5, + saveMsgHexToFile: 6, + quickValUser: 7, + addAuthorToTwitList: 8 }; } @@ -5152,6 +5163,9 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA } } + // For viewing the hex dump information (for the sysop) + var msgHexInfo = null; + // User input loop var continueOn = true; while (continueOn) @@ -5170,7 +5184,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA this.msgAreaLeft, this.msgAreaTop, msgAreaWidth, msgAreaHeight, 1, console.screen_rows, this.userSettings.useEnhReaderScrollbar, - msgScrollbarUpdateFn, scrollbarInfoObj, pmode); + msgScrollbarUpdateFn, pmode); topMsgLineIdx = scrollRetObj.topLineIdx; retObj.lastKeypress = scrollRetObj.lastKeypress; switch (retObj.lastKeypress) @@ -6174,6 +6188,60 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA this.RefreshMsgAreaRectangle(msgInfo.messageLines, topMsgLineIdx, userSettingsRetObj.optionBoxTopLeftX, userSettingsRetObj.optionBoxTopLeftY, userSettingsRetObj.optionBoxWidth, userSettingsRetObj.optionBoxHeight); } break; + case this.enhReaderKeys.showMsgHex: + if (user.is_sysop) + { + writeMessage = false; + if (msgHexInfo == null) + msgHexInfo = this.GetMsgHexInfo(messageText, true); + if (msgHexInfo.msgHexArray.length > 0) + { + this.ShowMsgHex_Scrolling(msgHexInfo); + if (this.userSettings.useEnhReaderScrollbar) + this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks); + writeMessage = true; + } + } + else // The user is not a sysop + writeMessage = false; + break; + case this.enhReaderKeys.hexDump: + // Save a hex dump of the message to the BBS machine - Only allow this + // if the user is a sysop. + if (user.is_sysop) + { + // Prompt the user for a filename to save the hex dump to the + // BBS machine + var promptPos = this.EnhReaderPrepLast2LinesForPrompt(); + console.print("\x01n\x01cFilename:\x01h"); + var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1 + var filename = console.getstr(inputLen, K_NOCRLF); + console.attributes = "N"; + if (filename.length > 0) + { + console.gotoxy(promptPos); + console.cleartoeol("\x01n"); + console.gotoxy(promptPos); + var saveHexRetObj = this.SaveMsgHexDumpToFile(msgHeader, filename); + if (saveHexRetObj.saveSucceeded) + console.print("\x01n\x01cThe hex dump has been saved.\x01n"); + else if (saveHexRetObj.errorMsg != "") + console.print("\x01n\x01y\x01h" + saveHexRetObj.errorMsg + "\x01n"); + else + console.print("\x01n\x01y\x01hFailed!\x01n"); + } + else + { + console.gotoxy(promptPos); + console.print("\x01n\x01y\x01hHex dump not exported\x01n"); + } + mswait(ERROR_PAUSE_WAIT_MS); + // Refresh the last 2 lines of the message on the screen to overwrite + // the file save prompt + this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr); + } + writeMessage = false; // Don't write the whole message again + break; case this.enhReaderKeys.operatorMenu: // Operator menu writeMessage = false; if (user.is_sysop) @@ -6259,6 +6327,50 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA } } break; + case this.readerOpMenuOptValues.showMsgHex: + writeMessage = false; + if (msgHexInfo == null) + msgHexInfo = this.GetMsgHexInfo(messageText, true); + if (msgHexInfo.msgHexArray.length > 0) + { + this.ShowMsgHex_Scrolling(msgHexInfo); + if (this.userSettings.useEnhReaderScrollbar) + this.DisplayEnhancedReaderWholeScrollbar(solidBlockStartRow, numSolidScrollBlocks); + writeMessage = true; + } + break; + case this.readerOpMenuOptValues.saveMsgHexToFile: + // Prompt the user for a filename to save the hex dump to the + // BBS machine + var promptPos = this.EnhReaderPrepLast2LinesForPrompt(); + console.print("\x01n\x01cFilename:\x01h"); + var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1 + var filename = console.getstr(inputLen, K_NOCRLF); + console.attributes = "N"; + if (filename.length > 0) + { + console.gotoxy(promptPos); + console.cleartoeol("\x01n"); + console.gotoxy(promptPos); + var saveHexRetObj = this.SaveMsgHexDumpToFile(msgHeader, filename); + if (saveHexRetObj.saveSucceeded) + console.print("\x01n\x01cThe hex dump has been saved.\x01n"); + else if (saveHexRetObj.errorMsg != "") + console.print("\x01n\x01y\x01h" + saveHexRetObj.errorMsg + "\x01n"); + else + console.print("\x01n\x01y\x01hFailed!\x01n"); + } + else + { + console.gotoxy(promptPos); + console.print("\x01n\x01y\x01hHex dump not exported\x01n"); + } + mswait(ERROR_PAUSE_WAIT_MS); + // Refresh the last 2 lines of the message on the screen to overwrite + // the file save prompt + this.DisplayEnhReaderError("", msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr); + writeMessage = true; + break; case this.readerOpMenuOptValues.quickValUser: // Quick validate the user var valRetObj = quickValidateLocalUser(msgHeader.from, this.scrollingReaderInterface && console.term_supports(USER_ANSI), this.quickUserValSetIndex); if (valRetObj.needWholeScreenRefresh) @@ -6355,7 +6467,7 @@ function DigDistMsgReader_ShowHdrOrKludgeLines_Scrollable(pOnlyKludgeLines, msgH // Display the kludge lines and let the user scroll through them this.DisplayEnhancedReaderWholeScrollbar(this.msgAreaTop, numInfoSolidScrollBlocks); } - scrollTextLines(extdHdrInfoLines, 0, this.colors["msgBodyColor"], true, this.msgAreaLeft, + scrollTextLines(extdHdrInfoLines, 0, this.colors.msgBodyColor, true, this.msgAreaLeft, this.msgAreaTop, msgAreaWidth, msgAreaHeight, 1, console.screen_rows, this.userSettings.useEnhReaderScrollbar, msgInfoScrollbarUpdateFn); writeMessage = true; // We want to refresh the message on the screen @@ -6611,6 +6723,9 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg } keyHelpText += "\x01c\x01hQ\x01y)\x01n\x01cuit\x01b, \x01c\x01h?\x01g: \x01c"; + // For showing the message hex dump (for the sysop) + var msgHexInfo = null; + // User input loop var writeMessage = true; var writePromptText = true; @@ -7402,6 +7517,59 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg writeMessage = true; } break; + case this.enhReaderKeys.showMsgHex: // Show message hex dump + writeMessage = false; + writePromptText = false; + if (user.is_sysop) + { + if (msgHexInfo == null) + msgHexInfo = this.GetMsgHexInfo(messageText, true); + if (msgHexInfo.msgHexArray.length > 0) + { + writeMessage = true; + writePromptText = true; + console.attributes = "N"; + console.crlf(); + console.print("\x01c\x01hMessage hex dump:\x01n\r\n"); + console.print("=================\r\n"); + for (var hexI = 0; hexI < msgHexInfo.msgHexArray.length; ++hexI) + console.print(msgHexInfo.msgHexArray[hexI] + "\r\n"); + console.pause(); + } + } + break; + case this.enhReaderKeys.hexDump: + writeMessage = false; + writePromptText = false; + // Save a hex dump of the message to the BBS machine - Only allow this + // if the user is a sysop. + if (user.is_sysop) + { + writeMessage = true; + writePromptText = true; + // Prompt the user for a filename to save the hex dump to the + // BBS machine + console.print("\x01n\r\n\x01cFilename:\x01h"); + var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1 + var filename = console.getstr(inputLen, K_NOCRLF); + console.attributes = "N"; + console.crlf(); + if (filename.length > 0) + { + var saveHexRetObj = this.SaveMsgHexDumpToFile(msgHeader, filename); + if (saveHexRetObj.saveSucceeded) + console.print("\x01n\x01cThe hex dump has been saved.\x01n"); + else if (saveHexRetObj.errorMsg != "") + console.print("\x01n\x01y\x01h" + saveHexRetObj.errorMsg + "\x01n"); + else + console.print("\x01n\x01y\x01hFailed!\x01n"); + } + else + console.print("\x01n\x01y\x01hHex dump not exported\x01n"); + console.crlf(); + console.pause(); + } + break; case this.enhReaderKeys.operatorMenu: // Operator menu if (user.is_sysop) { @@ -7498,6 +7666,49 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg } console.pause(); break; + case this.readerOpMenuOptValues.showMsgHex: + writeMessage = false; + writePromptText = false; + if (msgHexInfo == null) + msgHexInfo = this.GetMsgHexInfo(messageText, true); + if (msgHexInfo.msgHexArray.length > 0) + { + writeMessage = true; + writePromptText = true; + console.attributes = "N"; + console.crlf(); + console.print("\x01c\x01hMessage hex dump:\x01n\r\n"); + console.print("=================\r\n"); + for (var hexI = 0; hexI < msgHexInfo.msgHexArray.length; ++hexI) + console.print(msgHexInfo.msgHexArray[hexI] + "\r\n"); + console.pause(); + } + break; + case this.readerOpMenuOptValues.saveMsgHexToFile: + writeMessage = true; + writePromptText = true; + // Prompt the user for a filename to save the hex dump to the + // BBS machine + console.print("\x01n\r\n\x01cFilename:\x01h"); + var inputLen = console.screen_columns - 10; // 10 = "Filename:" length + 1 + var filename = console.getstr(inputLen, K_NOCRLF); + console.attributes = "N"; + console.crlf(); + if (filename.length > 0) + { + var saveHexRetObj = this.SaveMsgHexDumpToFile(msgHeader, filename); + if (saveHexRetObj.saveSucceeded) + console.print("\x01n\x01cThe hex dump has been saved.\x01n"); + else if (saveHexRetObj.errorMsg != "") + console.print("\x01n\x01y\x01h" + saveHexRetObj.errorMsg + "\x01n"); + else + console.print("\x01n\x01y\x01hFailed!\x01n"); + } + else + console.print("\x01n\x01y\x01hHex dump not exported\x01n"); + console.crlf(); + console.pause(); + break; case this.readerOpMenuOptValues.quickValUser: // Quick-validate the user quickValidateLocalUser(msgHeader.from, false, this.quickUserValSetIndex); break; @@ -7622,6 +7833,8 @@ function DigDistMsgReader_CreateReadModeOpMenu() opMenu.Add("&H: Show header lines", this.readerOpMenuOptValues.showHdrLines); opMenu.Add("&K: Show kludge lines", this.readerOpMenuOptValues.showKludgeLines); opMenu.Add("&T: Show tally stats", this.readerOpMenuOptValues.showTallyStats); + opMenu.Add("&X: Show message hex", this.readerOpMenuOptValues.showMsgHex); + opMenu.Add("&E: Save message hex to file", this.readerOpMenuOptValues.saveMsgHexToFile); opMenu.Add("&A: Quick validate the user", this.readerOpMenuOptValues.quickValUser); opMenu.Add("&I: Add author to twit list", this.readerOpMenuOptValues.addAuthorToTwitList); // Use cyan for the item color, and cyan with blue background for selected item color @@ -7634,6 +7847,8 @@ function DigDistMsgReader_CreateReadModeOpMenu() opMenu.Add("Show header lines", this.readerOpMenuOptValues.showHdrLines); opMenu.Add("Show kludge lines", this.readerOpMenuOptValues.showKludgeLines); opMenu.Add("Show tally stats", this.readerOpMenuOptValues.showTallyStats); + opMenu.Add("Show message hex", this.readerOpMenuOptValues.showMsgHex); + opMenu.Add("Save message hex to file", this.readerOpMenuOptValues.saveMsgHexToFile); opMenu.Add("Quick validate the user", this.readerOpMenuOptValues.quickValUser); opMenu.Add("Add author to twit list", this.readerOpMenuOptValues.addAuthorToTwitList); // Use green for the item color and high cyan for the item number color @@ -10899,6 +11114,8 @@ function DigDistMsgReader_DisplayEnhancedReaderHelp(pDisplayChgAreaOpt, pDisplay keyHelpLines.push("\x01h\x01cCtrl-S \x01g: \x01n\x01cSave the message (to the BBS machine)"); keyHelpLines.push("\x01h\x01c" + this.enhReaderKeys.validateMsg + " \x01g: \x01n\x01cValidate the message"); keyHelpLines.push("\x01h\x01cCtrl-O \x01g: \x01n\x01cShow operator menu"); + keyHelpLines.push("\x01h\x01cX \x01g: \x01n\x01cShow message hex dump"); + keyHelpLines.push("\x01h\x01cCtrl-X \x01g: \x01n\x01cSave message hex dump to a file"); var quickValUserLine = "\x01h\x01cCtrl-Q \x01g: \x01n\x01cQuick-validate user (must be local)"; if (this.quickUserValSetIndex >= 0 && this.quickUserValSetIndex < 10) quickValUserLine += "; Set index: " + this.quickUserValSetIndex; @@ -11034,22 +11251,6 @@ function DigDistMsgReader_DisplayEnhancedMsgHdr(pMsgHdr, pDisplayMsgNum, pStartS msgIsAPoll = Boolean(pMsgHdr.attr & MSG_POLL); // TODO: Fix the issue with not showing votes. With this, it seems to think it has 0 votes //var hdrWithVotes = getMsgHdr(this.subBoardCode, false, pMsgHdr.number, true, true); - /* - // Temporary - if (user.is_sysop) - { - console.print("\x01n\r\n"); - console.print("Has total_votes prop: " + hdrWithVotes.hasOwnProperty("total_votes") + "\r\n"); - console.print("Has upvotes prop: " + hdrWithVotes.hasOwnProperty("upvotes") + "\r\n"); - if (hdrWithVotes.hasOwnProperty("total_votes")) - console.print("total_votes: " + hdrWithVotes.total_votes + "\r\n"); - console.crlf(); - for (var prop in hdrWithVotes) - console.print(prop + ": " + hdrWithVotes[prop] + "\r\n"); - console.pause(); - } - // End Temporary - */ if (!msgIsAPoll && pMsgHdr.hasOwnProperty("total_votes") && pMsgHdr.hasOwnProperty("upvotes") && pMsgHdr.total_votes != 0) //if (!msgIsAPoll && hdrWithVotes.hasOwnProperty("total_votes") && hdrWithVotes.hasOwnProperty("upvotes") && hdrWithVotes.total_votes != 0) { @@ -11229,110 +11430,110 @@ function DigDistMsgReader_DisplayEnhancedReaderWholeScrollbar(pSolidBlockStartRo // pNumSolidBlocks: The number of solid/bright blocks function DigDistMsgReader_UpdateEnhancedReaderScrollbar(pNewStartRow, pOldStartRow, pNumSolidBlocks) { - // Calculate the difference in the start row. If the difference is positive, - // then the solid block section has moved down; if the diff is negative, the - // solid block section has moved up. - var solidBlockStartRowDiff = pNewStartRow - pOldStartRow; - var oldLastRow = pOldStartRow + pNumSolidBlocks - 1; - var newLastRow = pNewStartRow + pNumSolidBlocks - 1; - if (solidBlockStartRowDiff > 0) - { - // The solid block section has moved down - if (pNewStartRow > oldLastRow) - { - // No overlap - // Write dim blocks over the old solid block section - //console.print("\x01n\x01h\x01k"); - console.print("\x01n" + this.colors.scrollbarBGColor); - for (var screenY = pOldStartRow; screenY <= oldLastRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK1); - //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working - } - // Write solid blocks in the new locations - //console.print("\x01w"); - console.print("\x01n" + this.colors.scrollbarScrollBlockColor); - for (var screenY = pNewStartRow; screenY <= newLastRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK2); - //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working - } - } - else - { - // There is some overlap - // Write dim blocks on top - //console.print("\x01n\x01h\x01k"); - console.print("\x01n" + this.colors.scrollbarBGColor); - for (var screenY = pOldStartRow; screenY < pNewStartRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK1); - //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working - } - // Write bright blocks on the bottom - //console.print("\x01w"); - console.print("\x01n" + this.colors.scrollbarScrollBlockColor); - for (var screenY = oldLastRow+1; screenY <= newLastRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK2); - //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working - } - } - } - else if (solidBlockStartRowDiff < 0) - { - // The solid block section has moved up - if (pOldStartRow > newLastRow) - { - // No overlap - // Write dim blocks over the old solid block section - //console.print("\x01n\x01h\x01k"); - console.print("\x01n" + this.colors.scrollbarBGColor); - for (var screenY = pOldStartRow; screenY <= oldLastRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK1); - //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working - } - // Write solid blocks in the new locations - //console.print("\x01w"); - console.print("\x01n" + this.colors.scrollbarScrollBlockColor); - for (var screenY = pNewStartRow; screenY <= newLastRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK2); - //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working - } - } - else - { - // There is some overlap - // Write bright blocks on top - //console.print("\x01n\x01h\x01w"); - console.print("\x01n" + this.colors.scrollbarScrollBlockColor); - var endRow = pOldStartRow; - for (var screenY = pNewStartRow; screenY < endRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK2); - //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working - } - // Write dim blocks on the bottom - //console.print("\x01k"); - console.print("\x01n" + this.colors.scrollbarBGColor); - endRow = pOldStartRow + pNumSolidBlocks; - for (var screenY = pNewStartRow+pNumSolidBlocks; screenY < endRow; ++screenY) - { - console.gotoxy(this.msgAreaRight+1, screenY); - console.print(BLOCK1); - //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working - } - } - } + // Calculate the difference in the start row. If the difference is positive, + // then the solid block section has moved down; if the diff is negative, the + // solid block section has moved up. + var solidBlockStartRowDiff = pNewStartRow - pOldStartRow; + var oldLastRow = pOldStartRow + pNumSolidBlocks - 1; + var newLastRow = pNewStartRow + pNumSolidBlocks - 1; + if (solidBlockStartRowDiff > 0) + { + // The solid block section has moved down + if (pNewStartRow > oldLastRow) + { + // No overlap + // Write dim blocks over the old solid block section + //console.print("\x01n\x01h\x01k"); + console.print("\x01n" + this.colors.scrollbarBGColor); + for (var screenY = pOldStartRow; screenY <= oldLastRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK1); + //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working + } + // Write solid blocks in the new locations + //console.print("\x01w"); + console.print("\x01n" + this.colors.scrollbarScrollBlockColor); + for (var screenY = pNewStartRow; screenY <= newLastRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK2); + //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working + } + } + else + { + // There is some overlap + // Write dim blocks on top + //console.print("\x01n\x01h\x01k"); + console.print("\x01n" + this.colors.scrollbarBGColor); + for (var screenY = pOldStartRow; screenY < pNewStartRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK1); + //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working + } + // Write bright blocks on the bottom + //console.print("\x01w"); + console.print("\x01n" + this.colors.scrollbarScrollBlockColor); + for (var screenY = oldLastRow+1; screenY <= newLastRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK2); + //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working + } + } + } + else if (solidBlockStartRowDiff < 0) + { + // The solid block section has moved up + if (pOldStartRow > newLastRow) + { + // No overlap + // Write dim blocks over the old solid block section + //console.print("\x01n\x01h\x01k"); + console.print("\x01n" + this.colors.scrollbarBGColor); + for (var screenY = pOldStartRow; screenY <= oldLastRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK1); + //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working + } + // Write solid blocks in the new locations + //console.print("\x01w"); + console.print("\x01n" + this.colors.scrollbarScrollBlockColor); + for (var screenY = pNewStartRow; screenY <= newLastRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK2); + //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working + } + } + else + { + // There is some overlap + // Write bright blocks on top + //console.print("\x01n\x01h\x01w"); + console.print("\x01n" + this.colors.scrollbarScrollBlockColor); + var endRow = pOldStartRow; + for (var screenY = pNewStartRow; screenY < endRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK2); + //console.print(this.text.scrollbarScrollBlockChar); // TODO: This doesn't seem to be working + } + // Write dim blocks on the bottom + //console.print("\x01k"); + console.print("\x01n" + this.colors.scrollbarBGColor); + endRow = pOldStartRow + pNumSolidBlocks; + for (var screenY = pNewStartRow+pNumSolidBlocks; screenY < endRow; ++screenY) + { + console.gotoxy(this.msgAreaRight+1, screenY); + console.print(BLOCK1); + //console.print(this.text.scrollbarBGChar); // TODO: This doesn't seem to be working + } + } + } } // For the DigDistMsgReader class: Returns whether a particular message is @@ -13646,6 +13847,167 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter return retObj; } +// Shows a message hex dump with a scrollable interface +// +// Parameters: +// pMsgHexInfo: An object with message hex & scrollbar information, as returned by GetMsgHexInfo() +function DigDistMsgReader_ShowMsgHex_Scrolling(pMsgHexInfo) +{ + if (typeof(pMsgHexInfo) !== "object" || !Array.isArray(pMsgHexInfo.msgHexArray) || pMsgHexInfo.msgHexArray.length == 0) + return; + + var msgReaderObj = this; + var lastInfoSolidBlockStartRow = this.msgAreaTop; + + // This is a scrollbar update function for use when viewing the header info/kludge lines. + function msgHexScrollbarUpdateFn(pFractionToLastPage) + { + var infoSolidBlockStartRow = msgReaderObj.msgAreaTop + Math.floor(pMsgHexInfo.numNonSolidScrollBlocks * pFractionToLastPage); + if (infoSolidBlockStartRow != lastInfoSolidBlockStartRow) + msgReaderObj.UpdateEnhancedReaderScrollbar(infoSolidBlockStartRow, lastInfoSolidBlockStartRow, pMsgHexInfo.numSolidScrollBlocks); + lastInfoSolidBlockStartRow = infoSolidBlockStartRow; + console.gotoxy(1, console.screen_rows); + } + + if (pMsgHexInfo.msgHexArray.length > 0) + { + var msgAreaWidth = this.userSettings.useEnhReaderScrollbar ? this.msgAreaWidth : this.msgAreaWidth + 1; + var msgAreaHeight = this.msgAreaBottom - this.msgAreaTop + 1; + if (this.userSettings.useEnhReaderScrollbar) + this.DisplayEnhancedReaderWholeScrollbar(pMsgHexInfo.solidBlockStartRow, pMsgHexInfo.numSolidScrollBlocks); + scrollTextLines(pMsgHexInfo.msgHexArray, 0, this.colors.msgBodyColor, true, + this.msgAreaLeft, this.msgAreaTop, msgAreaWidth, msgAreaHeight, + 1, console.screen_rows, this.userSettings.useEnhReaderScrollbar, + msgHexScrollbarUpdateFn); + } +} + +// Gets message hex dump information, including scrollbar information for the hex lines +// +// Parameters: +// pMessageText: The text of the message +// pRemoveTrailingCRLF: Optional boolean - Whether or not to remove any trailing CR or LF characters. +// Defaults to false. +// +// Return value: An object with the following properties: +// msgHexArray: An array of text lines representing the hex dump of the message +// topLineIdxForLastPage: For scrolling, the index of the line for the top of the last page +// msgFractionShown: For scrolling, the fraction of the lines shown +// numSolidScrollBlocks: For scrolling, the number of solid srollbar blocks to use +// numNonSolidScrollBlocks: For scrolling, the number of non-solid scrollbar blocks to use +// solidBlockStartRow: For scrolling, the row on the screen to start the scrollbar at +function DigDistMsgReader_GetMsgHexInfo(pMessageText, pRemoveTrailingCRLF) +{ + var retObj = { + msgHexArray: [], + topLineIdxForLastPage: 0, + msgFractionShown: 0.0, + numSolidScrollBlocks: 0, + numNonSolidScrollBlocks: 0, + solidBlockStartRow: 0 + }; + + var hexArray = hexdump.generate(undefined, pMessageText, /* ASCII: */true, /* offsets: */true); + if (Array.isArray(hexArray) && hexArray.length > 0) + { + if (typeof(pRemoveTrailingCRLF) === "boolean" && pRemoveTrailingCRLF) + { + // Remove the trailing CR (and possibly LF) from the last Line + hexArray[hexArray.length-1] = hexArray[hexArray.length-1].replace(/[\r\n]+$/, ""); + } + + retObj.msgHexArray = hexArray; + retObj.topLineIdxForLastPage = retObj.msgHexArray.length - this.msgAreaHeight; + if (retObj.topLineIdxForLastPage < 0) + retObj.topLineIdxForLastPage = 0; + // Variables for the scrollbar to show the fraction of the message shown + retObj.msgFractionShown = this.msgAreaHeight / retObj.msgHexArray.length; + if (retObj.msgFractionShown > 1) + retObj.msgFractionShown = 1.0; + retObj.numSolidScrollBlocks = Math.floor(this.msgAreaHeight * retObj.msgFractionShown); + if (retObj.numSolidScrollBlocks == 0) + retObj.numSolidScrollBlocks = 1; + retObj.numNonSolidScrollBlocks = this.msgAreaHeight - retObj.numSolidScrollBlocks; + retObj.solidBlockStartRow = this.msgAreaTop; + } + + return retObj; +} + +// For the DDMsgReader class: Saves a message hex dump to a file on the BBS machine with the given filename +// +// Parameters: +// pMsgHdr: The header of the message +// pOutFilename: The full path & filename of the file to save the hex dump to +// +// Return value: An object with the following properties: +// saveSucceeded: Boolean - Whether or not the save succeeded +// errorMsg: A string containing an error on failure +function DigDistMsgReader_SaveMsgHexDumpToFile(pMsgHdr, pOutFilename) +{ + var retObj = { + saveSucceeded: false, + errorMsg: "" + }; + + if (typeof(pMsgHdr) !== "object" || typeof(pOutFilename) !== "string") + { + retObj.errorMsg = "Invalid parameter given"; + return retObj; + } + + var msgText = ""; + var hdrLines = null; + var msgbase = new MsgBase(this.subBoardCode); + if (msgbase.open()) + { + msgText = msgbase.get_msg_body(false, pMsgHdr.number); + hdrLines = this.GetExtdMsgHdrInfo(msgbase, pMsgHdr.number, false, false, false, false); + msgbase.close(); + } + + var hexLines = hexdump.generate(undefined, msgText, /* ASCII: */true, /* offsets: */true); + if (Array.isArray(hexLines) && hexLines.length > 0) + { + var outFile = new File(pOutFilename); + if (outFile.open("w")) + { + // Write the message header lines + if (Array.isArray(hdrLines) && hdrLines.length > 0) + { + for (var hdrI = 0; hdrI < hdrLines.length; ++hdrI) + outFile.writeln(hdrLines[hdrI]); + } + else + { + outFile.writeln("From: " + pMsgHdr.from); + outFile.writeln("To: " + pMsgHdr.to); + outFile.writeln("Subject: " + pMsgHdr.subject); + // Message time + var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(pMsgHdr); + var dateTimeStr = ""; + if (msgWrittenLocalTime != -1) + dateTimeStr = strftime("%a, %d %b %Y %H:%M:%S", msgWrittenLocalTime); + else + dateTimeStr = pMsgHdr.date.replace(/ [-+][0-9]+$/, ""); + outFile.writeln("Date: " + dateTimeStr); + } + outFile.writeln("================================="); + // Write the hex dump + for (var hexI = 0; hexI < hexLines.length; ++hexI) + outFile.writeln(hexLines[hexI]); + outFile.close(); + retObj.saveSucceeded = true; + } + else + retObj.errorMsg = "File write failed"; + } + else + errorMsg = "Hex dump is not available"; + + return retObj; +} + // For the DigDistMsgReader class: Returns the index of the last read message in // the current message area. If reading personal email, this will look at the // search results. Otherwise, this will use the sub-board's last_read pointer. @@ -18173,14 +18535,13 @@ function userHandleAliasNameMatch(pNameOrCRC16) // lines // pPostWriteCurY: The Y location for the cursor after writing the message // lines -// pUseScrollbar: Boolean - Whether or not to display the scrollbar. If false, -// this will display a scroll status line at the bottom instead. +// pUseScrollbar: Boolean - Whether or not to display the scrollbar. If false, +// this will display a scroll status line at the bottom instead. // pScrollUpdateFn: A function that the caller can provide for updating the // scroll position. This function has one parameter: // - fractionToLastPage: The fraction of the top index divided // by the top index for the last page (basically, the progress // to the last page). -// pScrollbarInfo: // pmode: Optional - Print mode (important for UTF8 info) // // Return value: An object with the following properties: @@ -18188,7 +18549,7 @@ function userHandleAliasNameMatch(pNameOrCRC16) // topLineIdx: The new top line index of the text lines, in case of scrolling function scrollTextLines(pTxtLines, pTopLineIdx, pTxtAttrib, pWriteTxtLines, pTopLeftX, pTopLeftY, pWidth, pHeight, pPostWriteCurX, pPostWriteCurY, pUseScrollbar, pScrollUpdateFn, - pScrollbarInfo, pmode) + pmode) { // Variables for the top line index for the last page, scrolling, etc. var topLineIdxForLastPage = pTxtLines.length - pHeight; @@ -18206,20 +18567,6 @@ function scrollTextLines(pTxtLines, pTopLineIdx, pTxtAttrib, pWriteTxtLines, pTo topLineIdx: pTopLineIdx }; - /* - // Temporary - if (user.is_sysop) - { - console.print("\x01n\r\n"); - console.print("pWidth: " + pWidth + "\r\n"); - console.print("pHeight: " + pHeight + "\r\n"); - console.print("pUseScrollbar: " + pUseScrollbar + "\r\n"); - console.print("pScrollUpdateFn: " + typeof(pScrollUpdateFn) + "\r\n"); - console.pause(); - } - // End Temporary - */ - // Create an array of color/attribute codes for each line of // text, in case there are any such codes in the text lines, // so that the colors in the message are displayed properly. diff --git a/xtrn/DDMsgReader/readme.txt b/xtrn/DDMsgReader/readme.txt index 4f97b0e5db6e4b6918937e0c28a894adc038c003..67ab71ed4bd24d0107ae49f7a1f483c76c854d79 100644 --- a/xtrn/DDMsgReader/readme.txt +++ b/xtrn/DDMsgReader/readme.txt @@ -1,6 +1,6 @@ Digital Distortion Message Reader - Version 1.90b - Release date: 2023-12-15 + Version 1.91 + Release date: 2023-12-26 by diff --git a/xtrn/DDMsgReader/revision_history.txt b/xtrn/DDMsgReader/revision_history.txt index dd401ea37dcc26ec3474f049559c0a66374c196c..b0b207d341c94e5302de1171f2a338fa7f6ba834 100644 --- a/xtrn/DDMsgReader/revision_history.txt +++ b/xtrn/DDMsgReader/revision_history.txt @@ -5,6 +5,9 @@ Revision History (change log) ============================= Version Date Description ------- ---- ----------- +1.91 2023-12-26 New sysop features while reading a message: Show message + hex (with the X key) and save message hex to a file (with + Ctrl-X) 1.90b 2023-12-15 New configurable colors in the theme file for the indexed newscan menu header text (indexMenuHeader), "NEW" indicator text (indexMenuNewIndicator), and highlighted