Newer
Older
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: gorthaur
// * example 1: strcmp( 'waldo', 'owald' ); // * returns 1: 1
// * example 2: strcmp( 'owald', 'waldo' );
// * returns 2: -1
subBoardArray.sort(function(pA, pB)
{
return ((pA.description == pB.description) ? 0 : ((pA.description > pB.description) ? 1 : -1));
});
}
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
// Display the sub-board list.
for (var i = 0; i < subBoardArray.length; ++i)
{
console.crlf();
console.print((subBoardArray[i].subBoardNum == highlightIndex) ? "\1n" +
this.colors.areaChooserMsgAreaMarkColor + "*" : " ");
printf(this.subBoardListPrintfInfo[grpIndex].printfStr, +(subBoardArray[i].subBoardNum+1),
subBoardArray[i].description.substr(0, this.subBoardNameLen),
subBoardArray[i].numPosts, strftime("%Y-%m-%d", subBoardArray[i].newestPostDate),
strftime("%H:%M:%S", subBoardArray[i].newestPostDate));
}
}
// If no sort type is specified, then output the sub-board information in
// order of sub-board number.
else
{
for (var arrSubBoardNum in msg_area.grp_list[grpIndex].sub_list)
{
// Open the current sub-board with the msgBase object.
msgBase = new MsgBase(msg_area.grp_list[grpIndex].sub_list[arrSubBoardNum].code);
if (msgBase.open())
{
// Get the date & time when the last message was imported.
if (msgBase.total_msgs > 0)
{
msgHeader = msgBase.get_msg_header(true, msgBase.total_msgs-1, true);
// Construct the date & time strings of the latest post
if (this.msgAreaList_lastImportedMsg_showImportTime)
{
newestDate.date = strftime("%Y-%m-%d", msgHeader.when_imported_time);
newestDate.time = strftime("%H:%M:%S", msgHeader.when_imported_time);
}
else
{
newestDate.date = strftime("%Y-%m-%d", msgHeader.when_written_time);
newestDate.time = strftime("%H:%M:%S", msgHeader.when_written_time);
}
}
else
newestDate.date = newestDate.time = "";
// Print the sub-board information
subBoardNum = +(arrSubBoardNum);
console.crlf();
console.print((subBoardNum == highlightIndex) ? "\1n" +
this.colors.areaChooserMsgAreaMarkColor + "*" : " ");
printf(this.subBoardListPrintfInfo[grpIndex].printfStr, +(subBoardNum+1),
msg_area.grp_list[grpIndex].sub_list[arrSubBoardNum].description.substr(0, this.subBoardListPrintfInfo[grpIndex].nameLen),
msgBase.total_msgs, newestDate.date, newestDate.time);
msgBase.close();
}
// Free some memory?
delete msgBase;
}
}
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
}
//////////////////////////////////////////////
// Message group list stuff (lightbar mode) //
//////////////////////////////////////////////
// Displays a screenful of message groups, for the lightbar interface.
//
// Parameters:
// pStartIndex: The message group index to start at (0-based)
// pStartScreenRow: The row on the screen to start at (1-based)
// pEndScreenRow: The row on the screen to end at (1-based)
// pClearScreenFirst: Boolean - Whether or not to clear the screen first
// pBlankToEndRow: Boolean - Whether or not to write blank lines to the end
// screen row if there aren't enough message groups to fill
// the screen.
function DigDistMsgReader_listScreenfulOfMsgGrps(pStartIndex, pStartScreenRow,
pEndScreenRow, pClearScreenFirst,
pBlankToEndRow)
{
// Check the parameters; If they're bad, then just return.
if ((typeof(pStartIndex) != "number") ||
(typeof(pStartScreenRow) != "number") ||
(typeof(pEndScreenRow) != "number"))
{
return;
}
if ((pStartIndex < 0) || (pStartIndex >= msg_area.grp_list.length))
return;
if ((pStartScreenRow < 1) || (pStartScreenRow > console.screen_rows))
return;
if ((pEndScreenRow < 1) || (pEndScreenRow > console.screen_rows))
return;
// If pStartScreenRow is greather than pEndScreenRow, then swap them.
if (pStartScreenRow > pEndScreenRow)
{
var temp = pStartScreenRow;
pStartScreenRow = pEndScreenRow;
pEndScreenRow = temp;
}
// Calculate the ending index to use for the message groups array.
var endIndex = pStartIndex + (pEndScreenRow-pStartScreenRow);
if (endIndex >= msg_area.grp_list.length)
endIndex = msg_area.grp_list.length - 1;
var onePastEndIndex = endIndex + 1;
// Clear the screen, go to the specified screen row, and display the message
// group information.
if (pClearScreenFirst)
console.clear("\1n");
console.gotoxy(1, pStartScreenRow);
var grpIndex = pStartIndex;
for (; grpIndex < onePastEndIndex; ++grpIndex)
{
this.WriteMsgGroupLine(grpIndex, false);
if (grpIndex < endIndex)
console.crlf();
}
// If pBlankToEndRow is true and we're not at the end row yet, then
// write blank lines to the end row.
if (pBlankToEndRow)
{
var screenRow = pStartScreenRow + (endIndex - pStartIndex) + 1;
if (screenRow <= pEndScreenRow)
{
for (; screenRow <= pEndScreenRow; ++screenRow)
{
console.gotoxy(1, screenRow);
console.clearline("\1n");
}
}
}
}
// For the DigDistMsgReader class - Writes a message group information line.
//
// Parameters:
// pGrpIndex: The index of the message group to write (assumed to be valid)
// pHighlight: Boolean - Whether or not to write the line highlighted.
function DigDistMsgReader_writeMsgGroupLine(pGrpIndex, pHighlight)
{
// TODO: If pHighlight is true, that causes the screen to be cleared
// and the line is written on the first row of the console.
console.print("\1n");
// Write the highlight background color if pHighlight is true.
if (pHighlight)
console.print(this.colors.areaChooserMsgAreaBkgHighlightColor);
// Write the message group information line
console.print(((typeof(bbs.curgrp) == "number") && (pGrpIndex == msg_area.sub[this.subBoardCode].grp_index)) ? this.colors.areaChooserMsgAreaMarkColor + "*" : " ");
printf((pHighlight ? this.msgGrpListHilightPrintfStr : this.msgGrpListPrintfStr),
+(pGrpIndex+1),
msg_area.grp_list[pGrpIndex].description.substr(0, this.msgGrpDescLen),
msg_area.grp_list[pGrpIndex].sub_list.length);
console.cleartoeol("\1n");
10167
10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
}
//////////////////////////////////////////////////
// Message sub-board list stuff (lightbar mode) //
//////////////////////////////////////////////////
// Updates the page number text in the group list header line on the screen.
//
// Parameters:
// pPageNum: The page number
// pNumPages: The total number of pages
// pGroup: Boolean - Whether or not this is for the group header. If so,
// then this will go to the right location for the group page text
// and use this.colors.areaChooserMsgAreaHeaderColor for the text.
// Otherwise, this will go to the right place for the sub-board page
// text and use the sub-board header color.
// pRestoreCurPos: Optional - Boolean - If true, then move the cursor back
// to the position where it was before this function was called
function DigDistMsgReader_updateMsgAreaPageNumInHeader(pPageNum, pNumPages, pGroup, pRestoreCurPos)
{
var originalCurPos = null;
if (pRestoreCurPos)
originalCurPos = console.getxy();
if (pGroup)
{
console.gotoxy(29, 1);
console.print("\1n" + this.colors.areaChooserMsgAreaHeaderColor + pPageNum + " of " +
pNumPages + ") ");
}
else
{
console.gotoxy(51, 1);
console.print("\1n" + this.colors.areaChooserSubBoardHeaderColor + pPageNum + " of " +
pNumPages + ") ");
}
if (pRestoreCurPos)
console.gotoxy(originalCurPos);
}
// Displays a screenful of message sub-boards, for the lightbar interface.
//
// Parameters:
// pGrpIndex: The index of the message group (0-based)
// pStartSubIndex: The message sub-board index to start at (0-based)
// pStartScreenRow: The row on the screen to start at (1-based)
// pEndScreenRow: The row on the screen to end at (1-based)
// pClearScreenFirst: Boolean - Whether or not to clear the screen first
// pBlankToEndRow: Boolean - Whether or not to write blank lines to the end
// screen row if there aren't enough message groups to fill
// the screen.
function DigDistMsgReader_ListScreenfulOfSubBrds(pGrpIndex, pStartSubIndex,
pStartScreenRow, pEndScreenRow,
pClearScreenFirst, pBlankToEndRow)
{
10223
10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245
10246
10247
10248
// Check the parameters; If they're bad, then just return.
if ((typeof(pGrpIndex) != "number") ||
(typeof(pStartSubIndex) != "number") ||
(typeof(pStartScreenRow) != "number") ||
(typeof(pEndScreenRow) != "number"))
{
return;
}
if ((pGrpIndex < 0) || (pGrpIndex >= msg_area.grp_list.length))
return;
if ((pStartSubIndex < 0) ||
(pStartSubIndex >= msg_area.grp_list[pGrpIndex].sub_list.length))
{
return;
}
if ((pStartScreenRow < 1) || (pStartScreenRow > console.screen_rows))
return;
if ((pEndScreenRow < 1) || (pEndScreenRow > console.screen_rows))
return;
// If pStartScreenRow is greather than pEndScreenRow, then swap them.
if (pStartScreenRow > pEndScreenRow)
{
var temp = pStartScreenRow;
pStartScreenRow = pEndScreenRow;
pEndScreenRow = temp;
}
// Calculate the ending index to use for the sub-board array.
var endIndex = pStartSubIndex + (pEndScreenRow-pStartScreenRow);
if (endIndex >= msg_area.grp_list[pGrpIndex].sub_list.length)
endIndex = msg_area.grp_list[pGrpIndex].sub_list.length - 1;
var onePastEndIndex = endIndex + 1;
// Clear the screen and go to the specified screen row.
if (pClearScreenFirst)
console.clear("\1n");
console.gotoxy(1, pStartScreenRow);
// Start listing the sub-boards.
var subIndex = pStartSubIndex;
for (; subIndex < onePastEndIndex; ++subIndex)
{
this.WriteMsgSubBoardLine(pGrpIndex, subIndex, false);
if (subIndex < endIndex)
console.crlf();
}
// If pBlankToEndRow is true and we're not at the end row yet, then
// write blank lines to the end row.
if (pBlankToEndRow)
{
var screenRow = pStartScreenRow + (endIndex - pStartSubIndex) + 1;
if (screenRow <= pEndScreenRow)
{
for (; screenRow <= pEndScreenRow; ++screenRow)
{
console.gotoxy(1, screenRow);
console.clearline("\1n");
}
}
}
}
// For the DigDistMsgReader class: Writes a message sub-board information line for
// the message area chooser functionality.
//
// Parameters:
// pGrpIndex: The index of the message group (assumed to be valid)
// pSubIndex: The index of the sub-board within the message group to write (assumed to be valid)
// pHighlight: Boolean - Whether or not to write the line highlighted.
function DigDistMsgReader_WriteMsgSubBrdLine(pGrpIndex, pSubIndex, pHighlight)
{
10296
10297
10298
10299
10300
10301
10302
10303
10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318
10319
10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
console.print("\1n");
// Write the highlight background color if pHighlight is true.
if (pHighlight)
console.print(this.colors.areaChooserMsgAreaBkgHighlightColor);
// Determine if pGrpIndex and pSubIndex specify the user's
// currently-selected group and sub-board.
var currentSub = false;
if ((typeof(bbs.curgrp) == "number") && (typeof(bbs.cursub) == "number"))
currentSub = ((pGrpIndex == msg_area.sub[this.subBoardCode].grp_index) && (pSubIndex == msg_area.sub[this.subBoardCode].index));
// Open the current sub-board with the msgBase object (so that we can get
// the date & time of the last imporeted message).
var msgBase = new MsgBase(msg_area.grp_list[pGrpIndex].sub_list[pSubIndex].code);
if (msgBase.open())
{
var newestDate = new Object(); // For storing the date of the newest post
// Get the date & time when the last message was imported.
if (msgBase.total_msgs > 0)
{
msgHeader = msgBase.get_msg_header(true, msgBase.total_msgs-1, true);
// Construct the date & time strings of the latest post
if (this.msgAreaList_lastImportedMsg_showImportTime)
{
newestDate.date = strftime("%Y-%m-%d", msgHeader.when_imported_time);
newestDate.time = strftime("%H:%M:%S", msgHeader.when_imported_time);
}
else
{
newestDate.date = strftime("%Y-%m-%d", msgHeader.when_written_time);
newestDate.time = strftime("%H:%M:%S", msgHeader.when_written_time);
}
}
else
newestDate.date = newestDate.time = "";
// Print the sub-board information line.
console.print(currentSub ? this.colors.areaChooserMsgAreaMarkColor + "*" : " ");
printf((pHighlight ? this.subBoardListPrintfInfo[pGrpIndex].highlightPrintfStr : this.subBoardListPrintfInfo[pGrpIndex].printfStr),
+(pSubIndex+1),
msg_area.grp_list[pGrpIndex].sub_list[pSubIndex].description.substr(0, this.subBoardListPrintfInfo[pGrpIndex].nameLen),
msgBase.total_msgs, newestDate.date, newestDate.time);
msgBase.close();
delete msgBase;
}
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463
10464
10465
10466
10467
}
///////////////////////////////////////////////
// Other functions for the msg. area chooser //
///////////////////////////////////////////////
// For the DigDistMsgReader class: Shows the help screen
//
// Parameters:
// pLightbar: Boolean - Whether or not to show lightbar help. If
// false, then this function will show regular help.
// pClearScreen: Boolean - Whether or not to clear the screen first
function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen)
{
if (pClearScreen && console.term_supports(USER_ANSI))
console.clear("\1n");
else
console.print("\1n");
DisplayProgramInfo();
console.crlf();
console.print("\1n\1c\1hMessage area (sub-board) chooser");
console.crlf();
console.print("\1kÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\1n");
console.crlf();
console.print("\1cFirst, a listing of message groups is displayed. One can be chosen by typing");
console.crlf();
console.print("its number. Then, a listing of sub-boards within that message group will be");
console.crlf();
console.print("shown, and one can be chosen by typing its number.");
console.crlf();
if (pLightbar)
{
console.crlf();
console.print("\1n\1cThe lightbar interface also allows up & down navigation through the lists:");
console.crlf();
console.print("\1k\1hÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ");
console.crlf();
console.print("\1n\1c\1hUp arrow\1n\1c: Move the cursor up one line");
console.crlf();
console.print("\1hDown arrow\1n\1c: Move the cursor down one line");
console.crlf();
console.print("\1hENTER\1n\1c: Select the current group/sub-board");
console.crlf();
console.print("\1hHOME\1n\1c: Go to the first item on the screen");
console.crlf();
console.print("\1hEND\1n\1c: Go to the last item on the screen");
console.crlf();
console.print("\1hF\1n\1c: Go to the first page");
console.crlf();
console.print("\1hL\1n\1c: Go to the last page");
console.crlf();
}
console.crlf();
console.print("Additional keyboard commands:");
console.crlf();
console.print("\1k\1hÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ");
console.crlf();
console.print("\1n\1c\1h?\1n\1c: Show this help screen");
console.crlf();
console.print("\1hQ\1n\1c: Quit");
console.crlf();
}
// Builds sub-board printf format information for a message group.
// The widths of the description & # messages columns are calculated
// based on the greatest number of messages in a sub-board for the
// message group.
//
// Parameters:
// pGrpIndex: The index of the message group
function DigDistMsgReader_BuildSubBoardPrintfInfoForGrp(pGrpIndex)
{
// If the array of sub-board printf strings doesn't contain the printf
// strings for this message group, then figure out the largest number
// of messages in the message group and add the printf strings.
if (typeof(this.subBoardListPrintfInfo[pGrpIndex]) == "undefined")
{
var greatestNumMsgs = getGreatestNumMsgs(pGrpIndex);
this.subBoardListPrintfInfo[pGrpIndex] = new Object();
this.subBoardListPrintfInfo[pGrpIndex].numMsgsLen = greatestNumMsgs.toString().length;
// Sub-board name length: With a # items length of 4, this should be
// 47 for an 80-column display.
this.subBoardListPrintfInfo[pGrpIndex].nameLen = console.screen_columns -
this.areaNumLen -
this.subBoardListPrintfInfo[pGrpIndex].numMsgsLen -
this.dateLen - this.timeLen - 7;
// Create the printf strings
this.subBoardListPrintfInfo[pGrpIndex].printfStr =
" " + this.colors.areaChooserMsgAreaNumColor
+ "%" + this.areaNumLen + "d "
+ this.colors.areaChooserMsgAreaDescColor + "%-"
+ this.subBoardListPrintfInfo[pGrpIndex].nameLen + "s "
+ this.colors.areaChooserMsgAreaNumItemsColor + "%"
+ this.subBoardListPrintfInfo[pGrpIndex].numMsgsLen + "d "
+ this.colors.areaChooserMsgAreaLatestDateColor + "%" + this.dateLen + "s "
+ this.colors.areaChooserMsgAreaLatestTimeColor + "%" + this.timeLen + "s";
this.subBoardListPrintfInfo[pGrpIndex].highlightPrintfStr =
"\1n" + this.colors.areaChooserMsgAreaBkgHighlightColor + " "
+ "\1n" + this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaNumHighlightColor
+ "%" + this.areaNumLen + "d \1n"
+ this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaDescHighlightColor + "%-"
+ this.subBoardListPrintfInfo[pGrpIndex].nameLen + "s \1n"
+ this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaNumItemsHighlightColor + "%"
+ this.subBoardListPrintfInfo[pGrpIndex].numMsgsLen + "d \1n"
+ this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaDateHighlightColor + "%" + this.dateLen + "s \1n"
+ this.colors.areaChooserMsgAreaBkgHighlightColor
+ this.colors.areaChooserMsgAreaTimeHighlightColor + "%" + this.timeLen + "s\1n";
}
}
// Returns an arry of strings containing extended message header information,
// such as the kludge lines (for FidoNet-style networks), etc.
// For each kludge line, there will be a label string for the info line, the
// info line itself (wrapped to fit the message area width), then a blank
// line (except for the last kludge line). The info lines that this method
// retrieves will only be retrieved if they exist in the given message header.
//
// Parameters:
// pMsgHdr: A message header
// pKludgeOnly: Boolean - Whether or not to only get the kludge lines. If false,
// then all header fields will be retrieved.
//
// Return value: An array of strings containing the extended message header information
10472
10473
10474
10475
10476
10477
10478
10479
10480
10481
10482
10483
10484
10485
10486
10487
10488
10489
10490
10491
10492
10493
10494
10495
10496
10497
10498
10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514
10515
10516
10517
10518
10519
10520
10521
10522
10523
10524
10525
10526
10527
10528
10529
10530
10531
10532
10533
10534
10535
10536
10537
10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly)
{
// If pMsgHdr is not valid, then just return an empty array.
if (typeof(pMsgHdr) != "object")
return new Array();
var msgHdrInfoLines = new Array();
var kludgeOnly = (typeof(pKludgeOnly) == "boolean" ? pKludgeOnly : false);
if (kludgeOnly)
{
// Kludge lines to display are MSGID, REPLY, CHRS, TZUTC, PID, TID,
// SEEN-BY, and PATH kludge
// TODO: See if Synchronet provides the TZUTC, SEEN-BY, or PATH kludge lines.
// It seems that Synchronet might not provide that information.
// TODO: Type 162 in the field_list array is seen-by, and type 163 is FTN path.
// FidoNet information available in Synchronet message
// headers:
// ftn_msgid FidoNet FTS-9 Message-ID
// ftn_reply FidoNet FTS-9 Reply-ID
// ftn_area FidoNet FTS-4 echomail AREA tag
// ftn_flags FidoNet FSC-53 FLAGS
// ftn_pid FidoNet FSC-46 Program Identifier
// ftn_tid FidoNet FSC-46 Tosser Identifier
// X-FTN-AREA AGN_BBS
// X-FTN-REPLY 46:1/127 47525e2e
// X-FTN-MSGID 46:1/143 562422ad
// X-FTN-Kludge CHRS: UTF-8 2
// X-FTN-TID hpt/lnx 1.9.0-cur 07-09-15
// X-FTN-SEEN-BY 1/100 143 102 104 107 108 109 111 113 2/101 3/101 1/115 116 121 123
// X-FTN-SEEN-BY 1/124 125 126 127 3/102 1/701 132 2/103 1/140 128 133 3/103 1/138 145
// X-FTN-SEEN-BY 1/139 141 142 135 147 148 2/104 1/149 151 152 153 155 150 156 248 159
// X-FTN-SEEN-BY 2/106 1/160 161 118 144 164 2/107 1/163 204 2/109 108 1/166 167 168 0
// X-FTN-PATH 1/143 100
// An array of objects containing the info lines to retrieve (if they
// exist in the message header) and their labels
var hdrInfoLineFields = new Array();
hdrInfoLineFields.push({ field: "ftn_msgid", label: "MSG ID:" });
hdrInfoLineFields.push({ field: "X-FTN-MSGID", label: "MSG ID:" });
hdrInfoLineFields.push({ field: "ftn_reply", label: "Reply ID:" });
hdrInfoLineFields.push({ field: "X-FTN-REPLY", label: "Reply ID:" });
hdrInfoLineFields.push({ field: "ftn_area", label: "Area tag:" });
hdrInfoLineFields.push({ field: "X-FTN-AREA", label: "Area tag:" });
hdrInfoLineFields.push({ field: "ftn_flags", label: "Flags:" });
hdrInfoLineFields.push({ field: "ftn_pid", label: "Program ID:" });
hdrInfoLineFields.push({ field: "ftn_tid", label: "Tosser ID:" });
hdrInfoLineFields.push({ field: "X-FTN-TID", label: "Tosser ID:" });
hdrInfoLineFields.push({ field: "X-FTN-Kludge", label: "Kludge:" });
hdrInfoLineFields.push({ field: "X-FTN-SEEN-BY", label: "Seen-By:" });
hdrInfoLineFields.push({ field: "X-FTN-PATH", label: "Path:" });
// Format the when-written date
if ((typeof(pMsgHdr.when_written_time) == "number") && (typeof(pMsgHdr.when_written_zone) == "number"))
{
msgHdrInfoLines.push("\1nMessage written date & time zone:");
var whenWrittenTimeDateStr = system.timestr(pMsgHdr.when_written_time) + " "
+ system.zonestr(pMsgHdr.when_written_zone);
msgHdrInfoLines.push(whenWrittenTimeDateStr);
msgHdrInfoLines.push("");
}
// Fields specified by hdrInfoLineFields
for (var i = 0; i < hdrInfoLineFields.length; ++i)
{
if (pMsgHdr.hasOwnProperty(hdrInfoLineFields[i].field) && (typeof(pMsgHdr[hdrInfoLineFields[i].field]) == "string"))
{
msgHdrInfoLines.push(hdrInfoLineFields[i].label);
var infoLineWrapped = word_wrap(pMsgHdr[hdrInfoLineFields[i].field], this.msgAreaWidth);
var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
{
if (infoLineWrappedArray[lineIdx].length > 0)
msgHdrInfoLines.push(infoLineWrappedArray[lineIdx]);
}
msgHdrInfoLines.push("");
}
}
// If the header has a field_list array, then get the desired information from it.
// TODO: Should I include all field_list lines, or select ones for "kludge lines only"?
if (pMsgHdr.hasOwnProperty("field_list"))
{
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
// TODO: Some field types can be in the array multiple times but only
// the last is valid. For those, only get the last one:
// 32 (Reply To)
// 33 (Reply To agent)
// 34 (Reply To net type)
// 35 (Reply To net address)
// 36 (Reply To extended)
// 37 (Reply To position)
// 38 (Reply To Organization)
msgHdrInfoLines.push(msgHdrFieldListTypeToLabel(pMsgHdr.field_list[fieldI].type));
var infoLineWrapped = pMsgHdr.field_list[fieldI].data;
var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
{
if (infoLineWrappedArray[lineIdx].length > 0)
{
msgHdrInfoLines.push(infoLineWrappedArray[lineIdx]);
msgHdrInfoLines.push("");
}
}
}
}
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
// If some info lines were added, then insert a header line & blank line to
// the beginning of the array, and remove the last empty line from the array.
if (msgHdrInfoLines.length > 0)
{
msgHdrInfoLines.splice(0, 0, "\1n\1c\1hMessage Information/Kludge Lines\1n");
msgHdrInfoLines.splice(1, 0, "\1n\1g\1h--------------------------------\1n");
if (msgHdrInfoLines[msgHdrInfoLines.length-1].length == 0)
msgHdrInfoLines.pop();
}
}
else
{
// Return all header fields
for (var prop in pMsgHdr)
{
if (prop == "field_list")
{
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
// TODO: Some field types can be in the array multiple times but only
// the last is valid. For those, only get the last one:
// 32 (Reply To)
// 33 (Reply To agent)
// 34 (Reply To net type)
// 35 (Reply To net address)
// 36 (Reply To extended)
// 37 (Reply To position)
// 38 (Reply To Organization)
msgHdrInfoLines.push(msgHdrFieldListTypeToLabel(pMsgHdr.field_list[fieldI].type));
var infoLineWrapped = pMsgHdr.field_list[fieldI].data;
var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
{
if (infoLineWrappedArray[lineIdx].length > 0)
{
msgHdrInfoLines.push(infoLineWrappedArray[lineIdx]);
msgHdrInfoLines.push("");
}
}
}
}
else
{
msgHdrInfoLines.push(prop + ":");
var infoLineWrapped = word_wrap(pMsgHdr[prop], this.msgAreaWidth);
var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
{
if (infoLineWrappedArray[lineIdx].length > 0)
msgHdrInfoLines.push(infoLineWrappedArray[lineIdx]);
}
}
msgHdrInfoLines.push("");
}
}
}
// For the DigDistMsgReader class: Gets & prepares message information for
// the enhanced reader.
//
// Parameters:
// pMsgHdr: The message header
// pWordWrap: Boolean - Whether or not to word-wrap the message to fit into the
// display area. This is optional and defaults to true. This should
// be true for normal use; the only time this should be false is when
// saving the message to a file.
// pDetermineAttachments: Boolean - Whether or not to parse the message text to
// get attachments from it. This is optional and defaults
// to true. If false, then the message text will be left
// intact with any base64-encoded attachments that may be
// in the message text (for multi-part MIME messages).
// pGetB64Data: Boolean - Whether or not to get the Base64-encoded data for
// base64-encoded attachments (i.e., in multi-part MIME emails).
// This is optional and defaults to true. This is only used when
// pDetermineAttachments is true.
// pMsgBody: Optional - A string containing the message body. If this is not included
// or is not a string, then this method will retrieve the message body.
//
// Return value: An object with the following properties:
// msgText: The unaltered message text
// messageLines: An array containing the message lines, wrapped to
// the message area width
// topMsgLineIdxForLastPage: The top message line index for the last page
// msgFractionShown: The fraction of the message shown
// numSolidScrollBlocks: The number of solid scrollbar blocks
// numNonSolidScrollBlocks: The number of non-solid scrollbar blocks
// solidBlockStartRow: The starting row on the screen for the scrollbar blocks
// attachments: An array of the attached filenames (as strings)
function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments,
pGetB64Data, pMsgBody)
{
var retObj = new Object();
var determineAttachments = true;
if (typeof(pDetermineAttachments) == "boolean")
determineAttachments = pDetermineAttachments;
var getB64Data = true;
if (typeof(pGetB64Data) == "boolean")
getB64Data = pGetB64Data;
var msgBody = (typeof(pMsgBody) == "string" ? pMsgBody : this.msgbase.get_msg_body(true, pMsgHdr.offset));
if (determineAttachments)
{
var msgInfo = determineMsgAttachments(pMsgHdr, msgBody, getB64Data);
retObj.msgText = msgInfo.msgText;
retObj.attachments = msgInfo.attachments;
}
else
{
retObj.attachments = [];
}
var msgTextAltered = retObj.msgText; // Will alter the message text, but not yet
// Only interpret @-codes if the user is reading personal email. There
// are many @-codes that do some action such as move the cursor, execute a
// script, etc., and I don't want users on message networks to do anything
// malicious to users on other BBSes.
if (this.readingPersonalEmail)
msgTextAltered = replaceAtCodesInStr(msgTextAltered, pMsgHdr); // Or this.ParseMsgAtCodes(msgTextAltered, pMsgHdr) to replace only some @ codes
msgTextAltered = msgTextAltered.replace(/\t/g, this.tabReplacementText);
// Convert other BBS color codes to Synchronet attribute codes if the settings
// to do so are enabled.
if ((system.settings & SYS_RENEGADE) == SYS_RENEGADE)
msgTextAltered = renegadeAttrsToSyncAttrs(msgTextAltered);
if ((system.settings & SYS_WWIV) == SYS_WWIV)
msgTextAltered = WWIVAttrsToSyncAttrs(msgTextAltered);
if ((system.settings & SYS_CELERITY) == SYS_CELERITY)
msgTextAltered = celerityAttrsToSyncAttrs(msgTextAltered);
if ((system.settings & SYS_PCBOARD) == SYS_PCBOARD)
msgTextAltered = PCBoardAttrsToSyncAttrs(msgTextAltered);
if ((system.settings & SYS_WILDCAT) == SYS_WILDCAT)
msgTextAltered = wildcatAttrsToSyncAttrs(msgTextAltered);
// Convert ANSI codes to Synchronet codes
msgTextAltered = ANSIAttrsToSyncAttrs(msgTextAltered);
10716
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734
10735
10736
10737
10738
10739
10740
10741
10742
10743
10744
10745
10746
10747
10748
10749
10750
10751
10752
10753
var wordWrapTheMsgText = true;
if (typeof(pWordWrap) == "boolean")
wordWrapTheMsgText = pWordWrap;
if (wordWrapTheMsgText)
{
// Wrap the text to fit into the available message area.
// Note: In Synchronet 3.15 (and some beta builds of 3.16), there seemed to
// be a bug in the word_wrap() function where the word wrap length in Linux
// was one less than Windows, so if the BBS is running 3.15 or earlier of
// Synchronet, add 1 to the word wrap length if running in Linux.
var textWrapLen = this.msgAreaWidth;
if (system.version_num <= 31500)
textWrapLen = gRunningInWindows ? this.msgAreaWidth : this.msgAreaWidth + 1;
var msgTextWrapped = word_wrap(msgTextAltered, textWrapLen);
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);
}
10754
10755
10756
10757
10758
10759
10760
10761
10762
10763
10764
10765
10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784
10785
10786
10787
10788
10789
10790
10791
10792
10793
10794
10795
10796
10797
10798
10799
10800
10801
10802
10803
10804
10805
10806
10807
10808
10809
10810
10811
10812
10813
10814
10815
10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827
10828
10829
10830
10831
10832
10833
10834
10835
10836
10837
10838
10839
10840
10841
10842
10843
10844
10845
10846
10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872
10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
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:
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
10964
10965
10966
10967
10968
10969
10970
10971
10972
10973
10974
10975
10976
// 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
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))