Newer
Older
11001
11002
11003
11004
11005
11006
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
retObj.messageLines = lfexpand(msgTextWrapped).split("\r\n");
// Go through the message lines and trim them to ensure they'll easily fit
// in the message display area without having to trim them later. (Note:
// this is okay to do since we're only using messageLines to display the
// message on the screen; messageLines isn't used for quoting/replying).
for (var msgLnIdx = 0; msgLnIdx < retObj.messageLines.length; ++msgLnIdx)
retObj.messageLines[msgLnIdx] = shortenStrWithAttrCodes(retObj.messageLines[msgLnIdx], this.msgAreaWidth);
// Set up some variables for displaying the message
retObj.topMsgLineIdxForLastPage = retObj.messageLines.length - this.msgAreaHeight;
if (retObj.topMsgLineIdxForLastPage < 0)
retObj.topMsgLineIdxForLastPage = 0;
// Variables for the scrollbar to show the fraction of the message shown
retObj.msgFractionShown = this.msgAreaHeight / retObj.messageLines.length;
if (retObj.msgFractionShown > 1)
retObj.msgFractionShown = 1.0;
retObj.numSolidScrollBlocks = Math.floor(this.msgAreaHeight * retObj.msgFractionShown);
if (retObj.numSolidScrollBlocks == 0)
retObj.numSolidScrollBlocks = 1;
}
else
{
retObj.messageLines = [];
retObj.messageLines.push(msgTextAltered);
}
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052
11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145
11146
11147
11148
11149
11150
11151
11152
11153
11154
11155
11156
11157
11158
11159
11160
11161
11162
11163
11164
11165
11166
11167
11168
11169
11170
11171
11172
11173
11174
11175
11176
11177
11178
11179
11180
11181
11182
11183
11184
11185
11186
11187
11188
retObj.numNonSolidScrollBlocks = this.msgAreaHeight - retObj.numSolidScrollBlocks;
retObj.solidBlockStartRow = this.msgAreaTop;
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.
// If there is no last read message or if there is a problem getting the last read
// message index, this method will return -1.
//
// Parameters:
// pMailStartFromFirst: Optional boolean - Whether or not to start from the
// first message (rather than from the last message) if
// reading personal email. Will stop looking at the first
// unread message. Defaults to false.
//
// Return value: The index of the last read message in the current message area
function DigDistMsgReader_GetLastReadMsgIdx(pMailStartFromFirst)
{
var msgIndex = -1;
if (this.readingPersonalEmail)
{
if (this.SearchingAndResultObjsDefinedForCurSub())
{
var startFromFirst = (typeof(pMailStartFromFirst) == "boolean" ? pMailStartFromFirst : false);
if (startFromFirst)
{
for (var idx = 0; idx < this.msgSearchHdrs[this.subBoardCode].indexed.length; ++idx)
{
if ((this.msgSearchHdrs[this.subBoardCode].indexed[idx].attr & MSG_READ) == MSG_READ)
msgIndex = idx;
else
break;
}
}
else
{
for (var idx = this.msgSearchHdrs[this.subBoardCode].indexed.length-1; idx >= 0; --idx)
{
if ((this.msgSearchHdrs[this.subBoardCode].indexed[idx].attr & MSG_READ) == MSG_READ)
{
msgIndex = idx;
break;
}
}
}
// Sanity checking for msgIndex (note: this function should return -1 if
// there is no last read message).
if (msgIndex >= this.msgSearchHdrs[this.subBoardCode].indexed.length)
msgIndex = this.msgSearchHdrs[this.subBoardCode].indexed.length - 1;
}
}
else
{
msgIndex = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].last_read);
// Sanity checking for msgIndex (note: this function should return -1 if
// there is no last read message).
if ((this.msgbase != null) && this.msgbase.is_open)
{
//if (msgIndex >= this.msgbase.total_msgs)
// msgIndex = this.msgbase.total_msgs - 1;
// TODO: Is this code right? Modified 3/24/2015 to replace
// the above 2 commented lines.
if ((msgIndex < 0) || (msgIndex >= this.msgbase.total_msgs))
{
// Look for the first message not marked as deleted
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(0, true);
// If a non-deleted message was found, then set the last read
// pointer to it.
if (nonDeletedMsgIdx > -1)
{
var newLastRead = this.IdxToAbsMsgNum(nonDeletedMsgIdx);
if (newLastRead > -1)
msg_area.sub[this.subBoardCode].last_read = newLastRead;
else
msg_area.sub[this.subBoardCode].last_read = 0;
}
else
msg_area.sub[this.subBoardCode].last_read = 0;
}
}
}
return msgIndex;
}
// For the DigDistMsgReader class: Returns the index of the message pointed to
// by the scan pointer in the current sub-board. If reading personal email or
// if the message base isn't open, this will return 0. If the scan pointer is
// 0 or if the messagebase is open and the scan pointer is invalid, this will
// return -1.
function DigDistMsgReader_GetScanPtrMsgIdx()
{
if (this.readingPersonalEmail)
return 0;
if (msg_area.sub[this.subBoardCode].scan_ptr == 0)
return -1;
if ((this.msgbase == null) || (!this.msgbase.is_open))
return 0;
var msgIdx = this.AbsMsgNumToIdx(msg_area.sub[this.subBoardCode].scan_ptr);
// Sanity checking for msgIdx
if ((msgIdx < 0) || (msgIdx >= this.msgbase.total_msgs))
{
msgIdx = -1;
// Look for the first message not marked as deleted
var nonDeletedMsgIdx = this.FindNextNonDeletedMsgIdx(0, true);
// If a non-deleted message was found, then set the scan pointer to it.
if (nonDeletedMsgIdx > -1)
{
var newLastRead = this.IdxToAbsMsgNum(nonDeletedMsgIdx);
if (newLastRead > -1)
msg_area.sub[this.subBoardCode].scan_ptr = newLastRead;
else
msg_area.sub[this.subBoardCode].scan_ptr = 0;
}
else
msg_area.sub[this.subBoardCode].scan_ptr = 0;
}
return msgIdx;
}
// For the DigDistMsgReader class: Returns whether there is a search specified
// (according to this.searchType) and the search result objects are defined for
// the current sub-board (as specified by this.subBoardCode).
//
// Return value: Boolean - Whether or not there is a search specified and the
// search result objects are defined for the current sub-board
// (as specified by this.subBoardCode).
function DigDistMsgReader_SearchingAndResultObjsDefinedForCurSub()
{
return (this.SearchTypePopulatesSearchResults() && (this.msgSearchHdrs != null) &&
this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(typeof(this.msgSearchHdrs[this.subBoardCode]) == "object") &&
(typeof(this.msgSearchHdrs[this.subBoardCode].indexed) != "undefined"));
}
// For the DigDistMsgReader class: Removes a message header from the search
// results array for the current sub-board.
//
// Parameters:
// pMsgIdx: The index of the message header to remove (in the indexed messages,
// not necessarily the actual message offset in the messagebase)
function DigDistMsgReader_RemoveFromSearchResults(pMsgIdx)
{
if (typeof(pMsgIdx) != "number")
return;
if ((typeof(this.msgSearchHdrs) == "object") &&
this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(typeof(this.msgSearchHdrs[this.subBoardCode].indexed) != "undefined"))
{
if ((pMsgIdx >= 0) && (pMsgIdx < this.msgSearchHdrs[this.subBoardCode].indexed.length))
this.msgSearchHdrs[this.subBoardCode].indexed.splice(pMsgIdx, 1);
}
}
// For the DigDistMsgReader class: Looks for the next message in the thread of
// a given message (by its header).
//
// Paramters:
// pMsgHdr: A message header object - The next message in the thread will be
// searched starting from this message

nightfox
committed
// pThreadType: The type of threading to use. Can be THREAD_BY_ID, THREAD_BY_TITLE,
// THREAD_BY_AUTHOR, or THREAD_BY_TO_USER.
// pPositionCursorForStatus: Optional boolean - Whether or not to move the cursor
// to the bottom row before outputting status messages.
// Defaults to false.
//
// Return value: The offset (index) of the next message thread, or -1 if none
// was found.

nightfox
committed
function DigDistMsgReader_FindThreadNextOffset(pMsgHdr, pThreadType, pPositionCursorForStatus)
{
if ((this.msgbase == null) || (!this.msgbase.is_open))
return -1;
if ((pMsgHdr == null) || (typeof(pMsgHdr) != "object"))
return -1;
var newMsgOffset = -1;

nightfox
committed
switch (pThreadType)
{

nightfox
committed
case THREAD_BY_ID:
default:
11210
11211
11212
11213
11214
11215
11216
11217
11218
11219
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234
11235
11236
11237
11238
11239
11240
11241
11242
11243
11244
11245
11246
11247
// The thread_id field was introduced in Synchronet 3.16. So, if
// the Synchronet version is 3.16 or higher and the message header
// has a thread_id field, then look for the next message with the
// same thread ID. If the Synchronet version is below 3.16 or there
// is no thread ID, then fall back to using the header's thread_next,
// if it exists.
if ((system.version_num >= 31600) && (typeof(pMsgHdr.thread_id) == "number"))
{
// Look for the next message with the same thread ID.
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the next message in the thread
var nextMsgOffset = -1;
var numOfMessages = this.NumMessages();
if (pMsgHdr.offset < numOfMessages - 1)
{
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset+1; (messageIdx < numOfMessages) && (nextMsgOffset == -1); ++messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((nextMsgHdr.attr & MSG_DELETE) == 0) && (typeof(nextMsgHdr.thread_id) == "number") && (nextMsgHdr.thread_id == pMsgHdr.thread_id))
nextMsgOffset = nextMsgHdr.offset;
}
}
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}
// Fall back to thread_next if the Synchronet version is below 3.16 or there is
// no thread_id field in the header
else if ((typeof(pMsgHdr.thread_next) == "number") && (pMsgHdr.thread_next > 0))
newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_next);

nightfox
committed
break;
case THREAD_BY_TITLE:
case THREAD_BY_AUTHOR:
case THREAD_BY_TO_USER:
// Title (subject) searching will look for the subject anywhere in the
// other messages' subjects (not a fully exact subject match), so if
// the message subject is blank, we won't want to do the search.
var doSearch = true;
if ((pThreadType == THREAD_BY_TITLE) && (pMsgHdr.subject.length == 0))
doSearch = false;
if (doSearch)
{

nightfox
committed
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
var subjUppercase = "";
var fromNameUppercase = "";
var toNameUppercase = "";
// Set up a message header matching function, depending on
// which field of the header we want to match
var msgHdrMatch;
if (pThreadType == THREAD_BY_TITLE)
{
subjUppercase = pMsgHdr.subject.toUpperCase();
// Remove any leading instances of "RE:" from the subject
while (/^RE:/.test(subjUppercase))
subjUppercase = subjUppercase.substr(3);
while (/^RE: /.test(subjUppercase))
subjUppercase = subjUppercase.substr(4);
// Remove any leading & trailing whitespace from the subject
subjUppercase = trimSpaces(subjUppercase, true, true, true);
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.subject.toUpperCase().indexOf(subjUppercase, 0) > -1));
};
}
else if (pThreadType == THREAD_BY_AUTHOR)
{
fromNameUppercase = pMsgHdr.from.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.from.toUpperCase() == fromNameUppercase));
};
}
else if (pThreadType == THREAD_BY_TO_USER)
{
toNameUppercase = pMsgHdr.to.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.to.toUpperCase() == toNameUppercase));
};
}
// Perform the search
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the next message that contains the given message's subject
var nextMsgOffset = -1;
var numOfMessages = this.NumMessages();
if (pMsgHdr.offset < numOfMessages - 1)
{

nightfox
committed
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset+1; (messageIdx < numOfMessages) && (nextMsgOffset == -1); ++messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (msgHdrMatch(nextMsgHdr))
nextMsgOffset = nextMsgHdr.offset;
}
}

nightfox
committed
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}

nightfox
committed
break;
}
// If no messages were found, then output a message to say so with a momentary pause.
if (newMsgOffset == -1)
{
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
else
console.crlf();
console.print("\1n\1h\1yNo messages found.\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
return newMsgOffset;
}
// For the DigDistMsgReader class: Looks for the previous message in the thread of
// a given message (by its header).
//
// Paramters:
// pMsgHdr: A message header object - The previous message in the thread will be
// searched starting from this message

nightfox
committed
// pThreadType: The type of threading to use. Can be THREAD_BY_ID, THREAD_BY_TITLE,
// THREAD_BY_AUTHOR, or THREAD_BY_TO_USER.
// pPositionCursorForStatus: Optional boolean - Whether or not to move the cursor
// to the bottom row before outputting status messages.
// Defaults to false.
//
// Return value: The offset (index) of the previous message thread, or -1 if
// none was found.

nightfox
committed
function DigDistMsgReader_FindThreadPrevOffset(pMsgHdr, pThreadType, pPositionCursorForStatus)
{
if ((this.msgbase == null) || (!this.msgbase.is_open))
return -1;
if ((pMsgHdr == null) || (typeof(pMsgHdr) != "object"))
return -1;
var newMsgOffset = -1;

nightfox
committed
switch (pThreadType)
{

nightfox
committed
case THREAD_BY_ID:
default:
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
// The thread_id field was introduced in Synchronet 3.16. So, if
// the Synchronet version is 3.16 or higher and the message header
// has a thread_id field, then look for the previous message with the
// same thread ID. If the Synchronet version is below 3.16 or there
// is no thread ID, then fall back to using the header's thread_next,
// if it exists.
if ((system.version_num >= 31600) && (typeof(pMsgHdr.thread_id) == "number"))
{
// Look for the previous message with the same thread ID.
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the previous message in the thread
var nextMsgOffset = -1;
if (pMsgHdr.offset > 0)
{
var prevMsgHdr;
for (var messageIdx = pMsgHdr.offset-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
prevMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((prevMsgHdr.attr & MSG_DELETE) == 0) && (typeof(prevMsgHdr.thread_id) == "number") && (prevMsgHdr.thread_id == pMsgHdr.thread_id))
nextMsgOffset = prevMsgHdr.offset;
}
}
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}
// Fall back to thread_next if the Synchronet version is below 3.16 or there is
// no thread_id field in the header
else if ((typeof(pMsgHdr.thread_back) == "number") && (pMsgHdr.thread_back > 0))
newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_back);
/*

nightfox
committed
// If thread_back is valid for the message header, then use that.
if ((typeof(pMsgHdr.thread_back) == "number") && (pMsgHdr.thread_back > 0))
newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_back);
else
{

nightfox
committed
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
11447
11448
11449
11450
11451
// If thread_id is defined and the index of the first message
// in the thread is before the current message, then search
// backwards for messages with a matching thread_id.
var firstThreadMsgIdx = this.AbsMsgNumToIdx(pMsgHdr.thread_first);
if ((typeof(pMsgHdr.thread_id) == "number") && (firstThreadMsgIdx < pMsgHdr.offset))
{
// Note (2014-10-11): Digital Man said thread_id was
// introduced in Synchronet version 3.16 and was still
// a work in progress and isn't 100% accurate for
// networked sub-boards.
// Look for the previous message with the same thread ID.
// Note: I'm not sure when thread_id was introduced in
// Synchronet, so I'm not sure of the minimum version where
// this will work.
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the previous message in the thread
var nextMsgOffset = -1;
if (pMsgHdr.offset > 0)
{
for (var messageIdx = pMsgHdr.offset-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
var prevMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((prevMsgHdr.attr & MSG_DELETE) == 0) && (typeof(prevMsgHdr.thread_id) == "number") && (prevMsgHdr.thread_id == pMsgHdr.thread_id))
nextMsgOffset = prevMsgHdr.offset;
}
}
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}
}
*/

nightfox
committed
break;
case THREAD_BY_TITLE:
case THREAD_BY_AUTHOR:
case THREAD_BY_TO_USER:
// Title (subject) searching will look for the subject anywhere in the
// other messages' subjects (not a fully exact subject match), so if
// the message subject is blank, we won't want to do the search.
var doSearch = true;
if ((pThreadType == THREAD_BY_TITLE) && (pMsgHdr.subject.length == 0))
doSearch = false;
if (doSearch)
{

nightfox
committed
11466
11467
11468
11469
11470
11471
11472
11473
11474
11475
11476
11477
11478
11479
11480
11481
11482
11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
var subjUppercase = "";
var fromNameUppercase = "";
var toNameUppercase = "";
// Set up a message header matching function, depending on
// which field of the header we want to match
var msgHdrMatch;
if (pThreadType == THREAD_BY_TITLE)
{
subjUppercase = pMsgHdr.subject.toUpperCase();
// Remove any leading instances of "RE:" from the subject
while (/^RE:/.test(subjUppercase))
subjUppercase = subjUppercase.substr(3);
while (/^RE: /.test(subjUppercase))
subjUppercase = subjUppercase.substr(4);
// Remove any leading & trailing whitespace from the subject
subjUppercase = trimSpaces(subjUppercase, true, true, true);
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.subject.toUpperCase().indexOf(subjUppercase, 0) > -1));
};
}
else if (pThreadType == THREAD_BY_AUTHOR)
{
fromNameUppercase = pMsgHdr.from.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.from.toUpperCase() == fromNameUppercase));
};
}
else if (pThreadType == THREAD_BY_TO_USER)
{
toNameUppercase = pMsgHdr.to.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.to.toUpperCase() == toNameUppercase));
};
}
// Perform the search
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the next message that contains the given message's subject
var nextMsgOffset = -1;
if (pMsgHdr.offset > 0)
{

nightfox
committed
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (msgHdrMatch(nextMsgHdr))
nextMsgOffset = nextMsgHdr.offset;
}
}

nightfox
committed
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}

nightfox
committed
break;
}
// If no messages were found, then output a message to say so with a momentary pause.
if (newMsgOffset == -1)
{
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
else
console.crlf();
console.print("\1n\1h\1yNo messages found.\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
return newMsgOffset;
}
// For the DigDistMsgReader class: Calculates the top message index for a page,
// for the traditional-style message list.
//
// Parameters:
// pPageNum: A page number (1-based)
function DigDistMsgReader_CalcTraditionalMsgListTopIdx(pPageNum)
{
if (this.reverseListOrder)
this.tradListTopMsgIdx = this.NumMessages() - (this.tradMsgListNumLines * (pPageNum-1)) - 1;
else
this.tradListTopMsgIdx = (this.tradMsgListNumLines * (pPageNum-1));
}
// For the DigDistMsgReader class: Calculates the top message index for a page,
// for the lightbar message list.
//
// Parameters:
// pPageNum: A page number (1-based)
function DigDistMsgReader_CalcLightbarMsgListTopIdx(pPageNum)
{
if (this.reverseListOrder)
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
this.lightbarListTopMsgIdx = this.NumMessages() - (this.lightbarMsgListNumLines * (pPageNum-1)) - 1;
else
this.lightbarListTopMsgIdx = (this.lightbarMsgListNumLines * (pPageNum-1));
}
// For the DigDistMsgReader class: Given a message number (1-based), this calculates
// the screen index veriables (stored in the object) for the message list. This is
// used for the enhanced reader mode when we want the message list to be in the
// correct place for the message being read.
//
// Parameters:
// pMsgNum: The message number (1-based)
function DigDistMsgReader_CalcMsgListScreenIdxVarsFromMsgNum(pMsgNum)
{
// Calculate the message list variables
var numItemsPerPage = this.tradMsgListNumLines;
if (this.msgListUseLightbarListInterface && canDoHighASCIIAndANSI())
numItemsPerPage = this.lightbarMsgListNumLines;
var newPageNum = findPageNumOfItemNum(pMsgNum, numItemsPerPage, this.NumMessages(), this.reverseListOrder);
11588
11589
11590
11591
11592
11593
11594
11595
11596
11597
11598
11599
11600
11601
11602
11603
11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
this.CalcTraditionalMsgListTopIdx(newPageNum);
this.CalcLightbarMsgListTopIdx(newPageNum);
this.lightbarListSelectedMsgIdx = pMsgNum - 1;
if (this.lightbarListCurPos == null)
this.lightbarListCurPos = new Object();
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + ((pMsgNum-1) - this.lightbarListTopMsgIdx);
}
// For the DigDistMsgReader class: Validates a user's choice in message area.
// Returns a status/error message for the caller to display if there's an
// error. This function outputs intermediate status messages (i.e.,
// "Searching..").
//
// Parameters:
// pGrpIdx: The message group index (i.e., bbs.curgrp)
// pSubIdx: The message sub-board index (i.e., bbs.cursub)
// pCurPos: Optional - An object containing x and y properties representing
// the cursor position. Used for outputting intermediate status
// messages, but not for outputting the error message.
//
// Return value: An object containing the following properties:
// msgAreaGood: A boolean to indicate whether the message area
// can be selected
// errorMsg: If the message area can't be selected, this string
// will contain an eror message. Otherwise, this will
// be an empty string.
function DigDistMsgReader_ValidateMsgAreaChoice(pGrpIdx, pSubIdx, pCurPos)
{
var retObj = new Object();
retObj.msgAreaGood = true;
retObj.errorMsg = "";
// Get the internal code of the sub-board from the given group & sub-board
// indexes
var subCode = msg_area.grp_list[pGrpIdx].sub_list[pSubIdx].code;
// If a search is specified that would populate the search results, then do
// a search in the given sub-board.
if (this.SearchTypePopulatesSearchResults())
{
// See if we can use pCurPos to move the cursor before displaying messages
var useCurPos = (console.term_supports(USER_ANSI) && (typeof(pCurPos) == "object") &&
(typeof(pCurPos.x) == "number") && (typeof(pCurPos.y) == "number"));
// TODO: In case new messages were posted in this sub-board, it might help
// to check the current number of messages vs. the previous number of messages
// and search the new messages if there are more.
// Determine whether or not to search - If there are no search results for
// the given sub-board already, then do a search in the sub-board.
var doSearch = true;
if (this.msgSearchHdrs.hasOwnProperty(subCode) &&
(typeof(this.msgSearchHdrs[subCode]) == "object") &&
(typeof(this.msgSearchHdrs[subCode].indexed) != "undefined"))
{
doSearch = (this.msgSearchHdrs[subCode].indexed.length == 0);
}
if (doSearch)
{
if (useCurPos)
{
console.gotoxy(pCurPos);
console.cleartoeol("\1n");
console.gotoxy(pCurPos);
}
console.print("\1n\1h\1wSearching\1i...\1n");
var msgBase = new MsgBase(subCode);
if (msgBase.open())
{
this.msgSearchHdrs[subCode] = searchMsgbase(subCode, msgBase, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
msgBase.close();
// If there are no messages, then set the return object variables to indicate so.
if (this.msgSearchHdrs[subCode].indexed.length == 0)
{
retObj.msgAreaGood = false;
retObj.errorMsg = "No search results found";
}
}
else
{
retObj.msgAreaGood = false;
retObj.errorMsg = "Unable to open message base (for searching)!";
}
}
}
else
{
// No search is specified. Just check to see if there are any messages
// to read in the given sub-board.
var msgBase = new MsgBase(subCode);
if (msgBase.open())
{
if (msgBase.total_msgs == 0)
{
retObj.msgAreaGood = false;
retObj.errorMsg = "No messages in that message area";
}
msgBase.close();
}
}
return retObj;
}
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
11735
11736
11737
11738
11739
11740
11741
11742
11743
11744
11745
11746
11747
11748
11749
11750
11751
11752
11753
11754
11755
11756
11757
11758
11759
11760
11761
11762
11763
11764
11765
11766
11767
11768
11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
// For the DigDistMsgReader class: Writes message lines to a file on the BBS
// machine.
//
// Parameters:
// pMsgHdr: The header object for the message
// pFilename: The name of the file to write the message to
// pStripCtrl: Boolean - Whether or not to remove Synchronet control
// codes from the message lines
// pMsgLines: An array containing the message lines
// pAttachments: An array containing attachment information (as returned by determineMsgAttachments())
//
// Return value: An object containing the following properties:
// succeeded: Boolean - Whether or not the file was successfully written
// errorMsg: String - On failure, will contain the reason it failed
function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename, pStripCtrl, pMsgLines, pAttachments)
{
// Sanity checking
if (typeof(pFilename) != "string")
return({ succeeded: false, errorMsg: "Filename parameter not a string"});
if (pFilename.length == 0)
return({ succeeded: false, errorMsg: "Empty filename given"});
// If no message lines are passed in, then get the message lines now.
var msgLines = pMsgLines;
var attachments = pAttachments;
if ((pMsgLines == null) || (typeof(pMsgLines) != "object"))
{
if (typeof(pMsgHdr) == "object")
{
// Get the message text, interpret any @-codes in it, replace tabs with spaces
// to prevent weirdness when displaying the message lines, and word-wrap the
// text so that it looks good on the screen,
//GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments, pGetB64Data)
//var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, false, false, false);
var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, true, true, true);
msgLines = msgInfo.messageLines;
if (msgInfo.hasOwnProperty("attachments"))
attachments = msgInfo.attachments;
}
else
return({ succeeded: false, errorMsg: "No message lines and null header object"});
}
var retObj = new Object();
retObj.succeeded = true;
retObj.errorMsg = "";
// If there are message attachments, then treat pFilename as a directory and
// create the directory for saving both the message text & attachments.
// Then, save the attachments to that directory.
var msgTextFilename = pFilename;
if ((attachments != null) && (attachments.length > 0))
{
if (file_isdir(pFilename))
{
if (file_exists(pFilename))
return({ succeeded: false, errorMsg: "Can't make directory: File with that name exists"});
}
else
{
if (!mkdir(pFilename))
return({ succeeded: false, errorMsg: "Failed to create directory"});
}
// The name of the file to save the message text will be called "messageText.txt"
// in the save directory.
var savePathWithTrailingSlash = backslash(pFilename);
msgTextFilename = savePathWithTrailingSlash + "messageText.txt";
// Save the attachments to the directory
var saveFileError = "";
for (var attachIdx = 0; (attachIdx < attachments.length) && (saveFileError.length == 0); ++attachIdx)
{
var destFilename = savePathWithTrailingSlash + attachments[attachIdx].filename;
// If the file info has base64 data, then decode & save it to the directory.
// Otherwise, the file was probably uploaded to the user's mailbox in Synchronet,
// so copy the file to the save directory.
if (attachments[attachIdx].hasOwnProperty("B64Data"))
{
var attachedFile = new File(destFilename);
if (attachedFile.open("wb"))
{
attachedFile.base64 = true;
if (!attachedFile.write(attachments[attachIdx].B64Data))
saveFileError = "\1n\1cFailed to save " + attachments[attachIdx].filename;
attachedFile.close();
}
}
else
{
// There is no base64 data for the file, so it's probably in the
// user's mailbox in Synchronet, so copy it to the save directory.
if (file_exists(attachments[attachIdx].fullyPathedFilename))
{
if (!file_copy(attachments[attachIdx].fullyPathedFilename, destFilename))
saveFileError = "Failed to copy " + attachments[attachIdx].filename;
}
else
saveFileError = "File " + attachments[attachIdx].fullyPathedAttachmentFilename + " doesn't exist";
}
}
if (saveFileError.length > 0)
return({ succeeded: false, errorMsg: saveFileError });
}
var messageSaveFile = new File(msgTextFilename);
if (messageSaveFile.open("w"))
{
// Write some header information to the file
if (typeof(pMsgHdr) == "object")
{
if (pMsgHdr.hasOwnProperty("from"))
messageSaveFile.writeln("From: " + pMsgHdr.from);
if (pMsgHdr.hasOwnProperty("to"))
messageSaveFile.writeln(" To: " + pMsgHdr.to);
if (pMsgHdr.hasOwnProperty("subject"))
messageSaveFile.writeln("Subj: " + pMsgHdr.subject);
/*
if (pMsgHdr.hasOwnProperty("when_written_time"))
messageSaveFile.writeln(strftime("Date: %Y-%m-%d %H:%M:%S", msgHeader.when_written_time));
*/
if (pMsgHdr.hasOwnProperty("date"))
messageSaveFile.writeln("Date: " + pMsgHdr.date);
if (pMsgHdr.hasOwnProperty("from_net_addr"))
messageSaveFile.writeln("From net address: " + pMsgHdr.from_net_addr);
if (pMsgHdr.hasOwnProperty("to_net_addr"))
messageSaveFile.writeln("To net address: " + pMsgHdr.to_net_addr);
if (pMsgHdr.hasOwnProperty("id"))
messageSaveFile.writeln("ID: " + pMsgHdr.id);
if (pMsgHdr.hasOwnProperty("reply_id"))
messageSaveFile.writeln("Reply ID: " + pMsgHdr.reply_id);
messageSaveFile.writeln("===============================");
}
// Write the message body to the file
if (pStripCtrl)
{
for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx)
messageSaveFile.writeln(strip_ctrl(msgLines[msgLineIdx]));
}
else
{
for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx)
messageSaveFile.writeln(msgLines[msgLineIdx]);
}
messageSaveFile.close();
}
else
{
retObj.succeeded = false;
retObj.errorMsg = "Unable to open the message file for writing";
}
return retObj;
}
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
11887
11888
11889
11890
11891
11892
11893
11894
11895
11896
11897
11898
11899
11900
11901
11902
11903
11904
11905
11906
11907
11908
11909
11910
11911
11912
11913
11914
11915
11916
11917
11918
11919
11920
11921
11922
11923
11924
11925
11926
11927
11928
11929
11930
11931
11932
11933
11934
11935
11936
11937
11938
11939
11940
11941
11942
11943
11944
11945
11946
11947
11948
11949
11950
11951
11952
11953
11954
11955
11956
11957
11958
11959
11960
11961
11962
11963
11964
11965
11966
11967
11968
11969
11970
11971
11972
11973
11974
///////////////////////////////////////////////////////////////////////////////////
// Helper functions
// Displays the program information.
function DisplayProgramInfo()
{
displayTextWithLineBelow("Digital Distortion Message Reader", true, "\1n\1c\1h", "\1k\1h")
console.center("\1n\1cVersion \1g" + READER_VERSION + " \1w\1h(\1b" + READER_DATE + "\1w)");
console.crlf();
}
// This function returns an array of default colors used in the
// DigDistMessageReader class.
function getDefaultColors()
{
var colorArray = new Array();
// Header line: "Current msg group:"
colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message group name
colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1w"; // High white
// Header line: "Current sub-board:"
colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message sub-board name
colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1w"; // High white
// Line with column headers
//colorArray["msgListColHeader"] = "\1h\1w"; // High white (keep blue background)
colorArray["msgListColHeader"] = "\1n\1h\1w"; // High white on black background
//colorArray["msgListColHeader"] = "\1h\1c"; // High cyan (keep blue background)
//colorArray["msgListColHeader"] = "\1" + "4\1h\1y"; // High yellow (keep blue background)
// Message list information
colorArray["msgListMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromColor"] = "\1n\1c";
colorArray["msgListToColor"] = "\1n\1c";
colorArray["msgListSubjectColor"] = "\1n\1c";
colorArray["msgListDateColor"] = "\1h\1b";
colorArray["msgListTimeColor"] = "\1h\1b";
// Message information for messages written to the user
colorArray["msgListToUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListToUserFromColor"] = "\1h\1g";
colorArray["msgListToUserToColor"] = "\1h\1g";
colorArray["msgListToUserSubjectColor"] = "\1h\1g";
colorArray["msgListToUserDateColor"] = "\1h\1b";
colorArray["msgListToUserTimeColor"] = "\1h\1b";
// Message information for messages from the user
colorArray["msgListFromUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromUserFromColor"] = "\1n\1c";
colorArray["msgListFromUserToColor"] = "\1n\1c";
colorArray["msgListFromUserSubjectColor"] = "\1n\1c";
colorArray["msgListFromUserDateColor"] = "\1h\1b";
colorArray["msgListFromUserTimeColor"] = "\1h\1b";
// Message list highlight colors
colorArray["msgListHighlightBkgColor"] = "\1" + "4"; // Background
colorArray["msgListMsgNumHighlightColor"] = "\1h\1y";
colorArray["msgListFromHighlightColor"] = "\1h\1c";
colorArray["msgListToHighlightColor"] = "\1h\1c";
colorArray["msgListSubjHighlightColor"] = "\1h\1c";
colorArray["msgListDateHighlightColor"] = "\1h\1w";
colorArray["msgListTimeHighlightColor"] = "\1h\1w";
// Lightbar message list help line colors
colorArray["lightbarMsgListHelpLineBkgColor"] = "\1" + "7"; // Background
colorArray["lightbarMsgListHelpLineGeneralColor"] = "\1b";
colorArray["lightbarMsgListHelpLineHotkeyColor"] = "\1r";
colorArray["lightbarMsgListHelpLineParenColor"] = "\1m";
// Continue prompt colors
colorArray["tradInterfaceContPromptMainColor"] = "\1n\1g"; // Main text color
colorArray["tradInterfaceContPromptHotkeyColor"] = "\1h\1c"; // Hotkey color
colorArray["tradInterfaceContPromptUserInputColor"] = "\1h\1g"; // User input color
// Message body color
colorArray["msgBodyColor"] = "\1n\1w";
// Read message confirmation colors
colorArray["readMsgConfirmColor"] = "\1n\1c";
colorArray["readMsgConfirmNumberColor"] = "\1h\1c";
// Prompt for continuing to list messages after reading a message
colorArray["afterReadMsg_ListMorePromptColor"] = "\1n\1c";
// Help screen text color
colorArray["tradInterfaceHelpScreenColor"] = "\1n\1h\1w";
// Colors for choosing a message group & sub-board
colorArray["areaChooserMsgAreaNumColor"] = "\1n\1w\1h";
colorArray["areaChooserMsgAreaDescColor"] = "\1n\1c";
colorArray["areaChooserMsgAreaNumItemsColor"] = "\1b\1h";
colorArray["areaChooserMsgAreaHeaderColor"] = "\1n\1y\1h";
colorArray["areaChooserSubBoardHeaderColor"] = "\1n\1g";
colorArray["areaChooserMsgAreaMarkColor"] = "\1g\1h";
colorArray["areaChooserMsgAreaLatestDateColor"] = "\1n\1g";
colorArray["areaChooserMsgAreaLatestTimeColor"] = "\1n\1m";
// Highlighted colors (for lightbar mode)
colorArray["areaChooserMsgAreaBkgHighlightColor"] = "\1" + "4"; // Blue background
colorArray["areaChooserMsgAreaNumHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaDescHighlightColor"] = "\1c";
colorArray["areaChooserMsgAreaDateHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaTimeHighlightColor"] = "\1w\1h";
colorArray["areaChooserMsgAreaNumItemsHighlightColor"] = "\1w\1h";
// Lightbar area chooser help line
colorArray["lightbarAreaChooserHelpLineBkgColor"] = "\1" + "7"; // Background
colorArray["lightbarAreaChooserHelpLineGeneralColor"] = "\1b";
colorArray["lightbarAreaChooserHelpLineHotkeyColor"] = "\1r";
colorArray["lightbarAreaChooserHelpLineParenColor"] = "\1m";
// Scrollbar background and scroll block colors (for the enhanced
// message reader interface)
colorArray["scrollbarBGColor"] = "\1n\1h\1k";
colorArray["scrollbarScrollBlockColor"] = "\1n\1h\1w";
// Color for the line drawn in the 2nd to last line of the message
// area in the enhanced reader mode before a prompt
colorArray["enhReaderPromptSepLineColor"] = "\1n\1h\1g";
// Colors for the enhanced reader help line
colorArray["enhReaderHelpLineBkgColor"] = "\1" + "7";
colorArray["enhReaderHelpLineGeneralColor"] = "\1b";
colorArray["enhReaderHelpLineHotkeyColor"] = "\1r";
colorArray["enhReaderHelpLineParenColor"] = "\1m";
// Message header line colors
colorArray["hdrLineLabelColor"] = "\1n\1c";
colorArray["hdrLineValueColor"] = "\1n\1g";
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
return colorArray;
}
// This function returns the month number (1-based) from a capitalized
// month name.
//
// Parameters:
// pMonthName: The name of the month
//
// Return value: The number of the month (1-12).
function getMonthNum(pMonthName)
{
var monthNum = 1;
if (pMonthName.substr(0, 3) == "Jan")
monthNum = 1;
else if (pMonthName.substr(0, 3) == "Feb")
monthNum = 2;
else if (pMonthName.substr(0, 3) == "Mar")
monthNum = 3;
else if (pMonthName.substr(0, 3) == "Apr")
monthNum = 4;