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
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
11189
11190
11191
11192
11193
11194
11195
11196
11197
11198
11199
11200
11201
11202
11203
11204
11205
11206
11207
11208
11209
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
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
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
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
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
11409
11410
11411
11412
11413
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
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462
11463
11464
11465
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
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11536
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
11587
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
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
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
11975
11976
11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
else
{
if ((grpIdx > 0) && (msg_area.grp_list[grpIdx-1].sub_list.length > 0))
{
--grpIdx;
subIdx = msg_area.grp_list[grpIdx].sub_list.length - 1;
}
else
searchForSubBoard = false;
}
// If we can search, then do it.
if (searchForSubBoard)
{
while (numMsgsInSubBoard(msg_area.grp_list[grpIdx].sub_list[subIdx].code) == 0)
{
if (subIdx > 0)
--subIdx;
else
{
if ((grpIdx > 0) && (msg_area.grp_list[grpIdx-1].sub_list.length > 0))
{
--grpIdx;
subIdx = msg_area.grp_list[grpIdx].sub_list.length - 1;
}
else
break; // Stop searching
}
}
}
}
// If we found a sub-board with messages in it, then set the variables
// in the return object
if (numMsgsInSubBoard(msg_area.grp_list[grpIdx].sub_list[subIdx].code) > 0)
{
retObj.grpIdx = grpIdx;
retObj.subIdx = subIdx;
retObj.subCode = msg_area.grp_list[grpIdx].sub_list[subIdx].code;
retObj.foundSubBoard = true;
retObj.subChanged = ((grpIdx != pStartGrpIdx) || (subIdx != pStartSubIdx));
}
return retObj;
}
// Returns the number of messages in a sub-board.
//
// Parameters:
// pSubBoardCode: The internal code of the sub-board to check
// pIncludeDeleted: Optional boolean - Whether or not to include deleted
// messages in the count. Defaults to false.
//
// Return value: The number of messages in the sub-board
function numMsgsInSubBoard(pSubBoardCode, pIncludeDeleted)
{
var numMessages = 0;
var msgbase = new MsgBase(pSubBoardCode);
if (msgbase.open())
{
var includeDeleted = (typeof(pIncludeDeleted) == "boolean" ? pIncludeDeleted : false);
if (includeDeleted)
numMessages = msgbase.total_msgs;
else
{
// Don't include deleted messages. Go through each message
// in the sub-board and count the ones that aren't marked
// as deleted.
for (var msgIdx = 0; msgIdx < msgbase.total_msgs; ++msgIdx)
{
var msgHdr = msgbase.get_msg_header(true, msgIdx, false);
if ((msgHdr != null) && ((msgHdr.attr & MSG_DELETE) == 0))
++numMessages;
}
}
msgbase.close();
}
return numMessages;
}
// Replaces @-codes in a string and returns the new string.
//
// Parameters:
// pStr: A string in which to replace @-codes
//
// Return value: A version of the string with @-codes interpreted
function replaceAtCodesInStr(pStr)
{
if (typeof(pStr) != "string")
return "";
// This code was originally written by Deuce. I updated it to check whether
// the string returned by bbs.atcode() is null, and if so, just return
// the original string.
return pStr.replace(/@([^@]+)@/g, function(m, code) {
var decoded = bbs.atcode(code);
return (decoded != null ? decoded : "@" + code + "@");
});
}
// Shortens a string, accounting for control/attribute codes. Returns a new
// (shortened) copy of the string.
//
// Parameters:
// pStr: The string to shorten
// pNewLength: The new (shorter) length of the string
//
// Return value: The shortened version of the string
function shortenStrWithAttrCodes(pStr, pNewLength)
{
if (typeof(pStr) != "string")
return "";
if (typeof(pNewLength) != "number")
return pStr;
if (pNewLength >= console.strlen(pStr))
return pStr;
var strCopy = "";
var tmpStr = "";
var strIdx = 0;
var lengthGood = true;
while (lengthGood && (strIdx < pStr.length))
{
tmpStr = strCopy + pStr.charAt(strIdx++);
if (console.strlen(tmpStr) <= pNewLength)
strCopy = tmpStr;
else
lengthGood = false;
}
return strCopy;
}
// Returns whether a given name matches the logged-in user's handle, alias, or
// name.
//
// Parameters:
// pName: A name to match against the logged-in user
//
// Return value: Boolean - Whether or not the given name matches the logged-in
// user's handle, alias, or name
function userHandleAliasNameMatch(pName)
{
if (typeof(pName) != "string")
return false;
var userMatch = false;
var nameUpper = pName.toUpperCase();
if (user.handle.length > 0)
userMatch = (nameUpper.indexOf(user.handle.toUpperCase()) > -1);
if (!userMatch && (user.alias.length > 0))
userMatch = (nameUpper.indexOf(user.alias.toUpperCase()) > -1);
if (!userMatch && (user.name.length > 0))
userMatch = (nameUpper.indexOf(user.name.toUpperCase()) > -1);
return userMatch;
}
// Displays a range of text lines on the screen and allows scrolling through them
// with the up & down arrow keys, PageUp, PageDown, HOME, and END. It is assumed
// that the array of text lines are already truncated to fit in the width of the
// text area, as a speed optimization.
//
// Parameters:
// pTxtLines: The array of text lines to allow scrolling for
// pTopLineIdx: The index of the text line to display at the top
// pTxtAttrib: The attribute(s) to apply to the text lines
// pWriteTxtLines: Boolean - Whether or not to write the text lines (in addition
// to doing the message loop). If false, this will only do the
// the message loop. This parameter is intended as a screen
// refresh optimization.
// pTopLeftX: The upper-left corner column for the text area
// pTopLeftY: The upper-left corner row for the text area
// pWidth: The width of the text area
// pHeight: The height of the text area
// pPostWriteCurX: The X location for the cursor after writing the message
// lines
// pPostWriteCurY: The Y location for the cursor after writing the message
// lines
// 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).
//
// Return value: An object with the following properties:
// lastKeypress: The last key pressed by the user (a string)
// 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, pScrollUpdateFn)
{
// Variables for the top line index for the last page, scrolling, etc.
var topLineIdxForLastPage = pTxtLines.length - pHeight;
if (topLineIdxForLastPage < 0)
topLineIdxForLastPage = 0;
var msgFractionShown = pHeight / pTxtLines.length;
if (msgFractionShown > 1)
msgFractionShown = 1.0;
var fractionToLastPage = 0;
var lastTxtRow = pTopLeftY + pHeight - 1;
var txtLineFormatStr = "%-" + pWidth + "s";
var retObj = new Object();
retObj.lastKeypress = "";
retObj.topLineIdx = pTopLineIdx;
var writeTxtLines = pWriteTxtLines;
var continueOn = true;
while (continueOn)
{
// If we are to write the text lines, then write each of them and also
// clear out the rest of the row on the screen
if (writeTxtLines)
{
// If the scroll update function parameter is a function, then calculate
// the fraction to the last page and call the scroll update function.
if (typeof(pScrollUpdateFn) == "function")
{
if (topLineIdxForLastPage != 0)
fractionToLastPage = retObj.topLineIdx / topLineIdxForLastPage;
pScrollUpdateFn(fractionToLastPage);
}
var screenY = pTopLeftY;
for (var lineIdx = retObj.topLineIdx; (lineIdx < pTxtLines.length) && (screenY <= lastTxtRow); ++lineIdx)
{
console.gotoxy(pTopLeftX, screenY++);
// Print the text line, then clear the rest of the line
console.print(pTxtAttrib + pTxtLines[lineIdx]);
printf("\1n%" + +(pWidth - console.strlen(pTxtLines[lineIdx])) + "s", "");
}
// If there are still some lines left in the message reading area, then
// clear the lines.
console.print("\1n" + pTxtAttrib);
while (screenY <= lastTxtRow)
{
console.gotoxy(pTopLeftX, screenY++);
printf(txtLineFormatStr, "");
}
}
// Get a keypress from the user and take action based on it
console.gotoxy(pPostWriteCurX, pPostWriteCurY);
retObj.lastKeypress = getKeyWithESCChars(K_UPPER|K_NOCRLF|K_NOECHO|K_NOSPIN);
switch (retObj.lastKeypress)
{
case KEY_UP:
if (retObj.topLineIdx > 0) {
--retObj.topLineIdx;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
case KEY_DOWN:
if (retObj.topLineIdx < topLineIdxForLastPage) {
++retObj.topLineIdx;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
case KEY_PAGE_DOWN: // Next page
if (retObj.topLineIdx < topLineIdxForLastPage) {
retObj.topLineIdx += pHeight;
if (retObj.topLineIdx > topLineIdxForLastPage)
retObj.topLineIdx = topLineIdxForLastPage;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
case KEY_PAGE_UP: // Previous page
if (retObj.topLineIdx > 0) {
retObj.topLineIdx -= pHeight;
if (retObj.topLineIdx < 0)
retObj.topLineIdx = 0;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
case KEY_HOME: // First page
if (retObj.topLineIdx > 0) {
retObj.topLineIdx = 0;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
case KEY_END: // Last page
if (retObj.topLineIdx < topLineIdxForLastPage) {
retObj.topLineIdx = topLineIdxForLastPage;
writeTxtLines = true;
}
else
writeTxtLines = false;
break;
default:
continueOn = false;
break;
}
}
return retObj;
}
// Finds the (1-based) page number of an item by number (1-based). If no page
// is found, then the return value will be 0.
//
// Parameters:
// pItemNum: The item number (1-based)
// pNumPerPage: The number of items per page
// pTotoalNum: The total number of items in the list
// pReverseOrder: Boolean - Whether or not the list is in reverse order. If not specified,
// this will default to false.
//
// Return value: The page number (1-based) of the item number. If no page is found,
// the return value will be 0.
function findPageNumOfItemNum(pItemNum, pNumPerPage, pTotalNum, pReverseOrder)
{
if ((typeof(pItemNum) != "number") || (typeof(pNumPerPage) != "number") || (typeof(pTotalNum) != "number"))
return 0;
if ((pItemNum < 1) || (pItemNum > pTotalNum))
return 0;
var reverseOrder = (typeof(pReverseOrder) == "boolean" ? pReverseOrder : false);
var itemPageNum = 0;
if (reverseOrder)
{
var pageNum = 1;
for (var topNum = pTotalNum; ((topNum > 0) && (itemPageNum == 0)); topNum -= pNumPerPage)
{
if ((pItemNum <= topNum) && (pItemNum >= topNum-pNumPerPage+1))
itemPageNum = pageNum;
++pageNum;
}
}
else // Forward order
itemPageNum = Math.ceil(pItemNum / pNumPerPage);
return itemPageNum;
}
// This function converts a search mode string to one of the defined search value
// constants. If the passed-in mode string is unknown, then the return value will
// be SEARCH_NONE (-1).
//
// Parameters:
// pSearchTypeStr: A string describing a search mode ("keyword_search", "from_name_search",
// "to_name_search", "to_user_search", "new_msg_scan", "new_msg_scan_cur_sub",
// "new_msg_scan_cur_grp", "new_msg_scan_all", "to_user_new_scan",
// "to_user_all_scan")
//
// Return value: An integer representing the search value (SEARCH_KEYWORD,
// SEARCH_FROM_NAME, SEARCH_TO_NAME_CUR_MSG_AREA,
// SEARCH_TO_USER_CUR_MSG_AREA), or SEARCH_NONE (-1) if the passed-in
// search type string is unknown.
function searchTypeStrToVal(pSearchTypeStr)
{
if (typeof(pSearchTypeStr) != "string")
return SEARCH_NONE;
var searchTypeInt = SEARCH_NONE;
var modeStr = pSearchTypeStr.toLowerCase();
if (modeStr == "keyword_search")
searchTypeInt = SEARCH_KEYWORD;
else if (modeStr == "from_name_search")
searchTypeInt = SEARCH_FROM_NAME;
else if (modeStr == "to_name_search")
searchTypeInt = SEARCH_TO_NAME_CUR_MSG_AREA;
else if (modeStr == "to_user_search")
searchTypeInt = SEARCH_TO_USER_CUR_MSG_AREA;
else if (modeStr == "new_msg_scan")
searchTypeInt = SEARCH_MSG_NEWSCAN;
else if (modeStr == "new_msg_scan_cur_sub")
searchTypeInt = SEARCH_MSG_NEWSCAN_CUR_SUB;
else if (modeStr == "new_msg_scan_cur_grp")
searchTypeInt = SEARCH_MSG_NEWSCAN_CUR_GRP;
else if (modeStr == "new_msg_scan_all")
searchTypeInt = SEARCH_MSG_NEWSCAN_ALL;
else if (modeStr == "to_user_new_scan")
searchTypeInt = SEARCH_TO_USER_NEW_SCAN;
else if (modeStr == "to_user_new_scan_cur_sub")
searchTypeInt = SEARCH_TO_USER_NEW_SCAN_CUR_SUB;
else if (modeStr == "to_user_new_scan_cur_grp")
searchTypeInt = SEARCH_TO_USER_NEW_SCAN_CUR_GRP;
else if (modeStr == "to_user_new_scan_all")
searchTypeInt = SEARCH_TO_USER_NEW_SCAN_ALL;
else if (modeStr == "to_user_all_scan")
searchTypeInt = SEARCH_ALL_TO_USER_SCAN;
return searchTypeInt;
}
// This function converts a search type value to a string description.
//
// Parameters:
// pSearchType: The search type value to convert
//
// Return value: A string describing the search type value
function searchTypeValToStr(pSearchType)
{
if (typeof(pSearchType) != "number")
return "Unknown (not a number)";
var searchTypeStr = "";
switch (pSearchType)
{
case SEARCH_NONE:
searchTypeStr = "None (SEARCH_NONE)";
break;
case SEARCH_KEYWORD:
searchTypeStr = "Keyword (SEARCH_KEYWORD)";
break;
case SEARCH_FROM_NAME:
searchTypeStr = "'From' name (SEARCH_FROM_NAME)";
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
searchTypeStr = "'To' name (SEARCH_TO_NAME_CUR_MSG_AREA)";
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
searchTypeStr = "To you (SEARCH_TO_USER_CUR_MSG_AREA)";
break;
case SEARCH_MSG_NEWSCAN:
searchTypeStr = "New message scan (SEARCH_MSG_NEWSCAN)";
break;
case SEARCH_MSG_NEWSCAN_CUR_SUB:
searchTypeStr = "New in current message area (SEARCH_MSG_NEWSCAN_CUR_SUB)";
break;
case SEARCH_MSG_NEWSCAN_CUR_GRP:
searchTypeStr = "New in current message group (SEARCH_MSG_NEWSCAN_CUR_GRP)";
break;
case SEARCH_MSG_NEWSCAN_ALL:
searchTypeStr = "Newscan - All (SEARCH_MSG_NEWSCAN_ALL)";
break;
case SEARCH_TO_USER_NEW_SCAN:
searchTypeStr = "To You new scan (SEARCH_TO_USER_NEW_SCAN)";
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_SUB:
searchTypeStr = "To You new scan, current sub-board (SEARCH_TO_USER_NEW_SCAN_CUR_SUB)";
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_GRP:
searchTypeStr = "To You new scan, current group (SEARCH_TO_USER_NEW_SCAN_CUR_GRP)";
break;
case SEARCH_TO_USER_NEW_SCAN_ALL:
searchTypeStr = "To You new scan, all sub-boards (SEARCH_TO_USER_NEW_SCAN_ALL)";
break;
case SEARCH_ALL_TO_USER_SCAN:
searchTypeStr = "All To You scan (SEARCH_ALL_TO_USER_SCAN)";
break;
default:
searchTypeStr = "Unknown (" + pSearchType + ")";
break;
}
return searchTypeStr;
}
// This function converts a reader mode string to one of the defined reader mode
// value constants. If the passed-in mode string is unknown, then the return value
// will be -1.
//
// Parameters:
// pModeStr: A string describing a reader mode ("read", "reader", "list", "lister")
//
// Return value: An integer representing the reader mode value (READER_MODE_READ,
// READER_MODE_LIST), or -1 if the passed-in mode string is unknown.
function readerModeStrToVal(pModeStr)
{
if (typeof(pModeStr) != "string")
return -1;
var readerModeInt = -1;
var modeStr = pModeStr.toLowerCase();
if ((modeStr == "read") || (modeStr == "reader"))
readerModeInt = READER_MODE_READ;
else if ((modeStr == "list") || (modeStr == "lister"))
readerModeInt = READER_MODE_LIST;
return readerModeInt;
}
// This function returns a boolean to signify whether or not the user's
// terminal supports both high-ASCII characters and ANSI codes.
function canDoHighASCIIAndANSI()
{
//return (console.term_supports(USER_ANSI) && (user.settings & USER_NO_EXASCII == 0));
return (console.term_supports(USER_ANSI));
}
// Searches a given range in an open message base and returns an object with arrays
// containing the message headers (0-based indexed and indexed by message number)
// with the message headers of any found messages.
//
// Parameters:
// pSubCode: The internal code of the message sub-board
// pMsgbase: A message base object in which to search messages
// pSearchType: The type of search to do (one of the SEARCH_ values)
// pSearchString: The string to search for.
// pListingPersonalEmailFromUser: Optional boolean - Whether or not we're listing
// personal email sent by the user. This defaults
// to false.
// pStartIndex: The starting message index (0-based). Optional; defaults to 0.
// pEndIndex: One past the last message index. Optional; defaults to the total number
// of messages.
//
// Return value: An object with the following arrays:
// indexed: A 0-based indexed array of message headers
function searchMsgbase(pSubCode, pMsgbase, pSearchType, pSearchString,
pListingPersonalEmailFromUser, pStartIndex, pEndIndex)
{
var msgHeaders = new Object();
msgHeaders.indexed = new Array();
if ((pSubCode != "mail") && ((typeof(pSearchString) != "string") || !searchTypePopulatesSearchResults(pSearchType)))
return msgHeaders;
var startMsgIndex = 0;
var endMsgIndex = pMsgbase.total_msgs;
if (typeof(pStartIndex) == "number")
{
if ((pStartIndex >= 0) && (pStartIndex < pMsgbase.total_msgs))
startMsgIndex = pStartIndex;
}
if (typeof(pEndIndex) == "number")
{
if ((pEndIndex >= 0) && (pEndIndex > startMsgIndex) && (pEndIndex <= pMsgbase.total_msgs))
endMsgIndex = pEndIndex;
}
// Define a search function for the message field we're going to search
var readingPersonalEmailFromUser = (typeof(pListingPersonalEmailFromUser) == "boolean" ? pListingPersonalEmailFromUser : false);
var matchFn = null;
switch (pSearchType)
{
// It might seem odd to have SEARCH_NONE in here, but it's here because
// when reading personal email, we need to search for messages only to
// the current user.
case SEARCH_NONE:
if (pSubCode == "mail")
{
// Set up the match function slightly differently depending on whether
// we're looking for mail from the current user or to the current user.
if (readingPersonalEmailFromUser)
{
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(true, pMsgHdr.offset));
return msgIsFromUser(pMsgHdr);
}
}
else
{
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(true, pMsgHdr.offset));
return msgIsToLoggedInUserNum(pMsgHdr);
}
}
}
break;
case SEARCH_KEYWORD:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(true, pMsgHdr.offset));
//return ((pMsgHdr["subject"].toUpperCase().indexOf(pSearchStr) > -1) || (msgText.toUpperCase().indexOf(pSearchStr) > -1));
var keywordFound = ((pMsgHdr.subject.toUpperCase().indexOf(pSearchStr) > -1) || (msgText.toUpperCase().indexOf(pSearchStr) > -1));
if (pSubBoardCode == "mail")
return keywordFound && msgIsToLoggedInUserNum(pMsgHdr);
else
return keywordFound;
}
break;
case SEARCH_FROM_NAME:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var fromNameFound = (pMsgHdr.from.toUpperCase() == pSearchStr.toUpperCase());
if (pSubBoardCode == "mail")
return fromNameFound && msgIsToLoggedInUserNum(pMsgHdr);
else
return fromNameFound;
}
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
return (pMsgHdr.to.toUpperCase() == pSearchStr);
}
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
case SEARCH_ALL_TO_USER_SCAN:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// See if the message is not marked as deleted and the 'To' name
// matches the user's handle, alias, and/or username.
return (((pMsgHdr.attr & MSG_DELETE) == 0) && userNameHandleAliasMatch(pMsgHdr.to));
}
break;
case SEARCH_TO_USER_NEW_SCAN:
case SEARCH_TO_USER_NEW_SCAN_CUR_SUB:
case SEARCH_TO_USER_NEW_SCAN_CUR_GRP:
case SEARCH_TO_USER_NEW_SCAN_ALL:
if (pSubCode != "mail")
{
// If pStartIndex or pEndIndex aren't specified, then set
// startMsgIndex to the scan pointer and endMsgIndex to one
// past the index of the last message in the sub-board
if (typeof(pStartIndex) != "number")
{
// First, write some messages to the log if verbose logging is enabled
if (gCmdLineArgVals.verboselogging)
{
writeToSysAndNodeLog("New-to-user scan for " +
subBoardGrpAndName(pSubCode) + " -- Scan pointer: " +
msg_area.sub[pSubCode].scan_ptr);
}
//startMsgIndex = absMsgNumToIdx(pMsgbase, msg_area.sub[pSubCode].last_read);
startMsgIndex = absMsgNumToIdx(pMsgbase, msg_area.sub[pSubCode].scan_ptr);
if (startMsgIndex == -1)
{
msg_area.sub[pSubCode].scan_ptr = 0;
startMsgIndex = 0;
}
}
if (typeof(pEndIndex) != "number")
endMsgIndex = (pMsgbase.total_msgs > 0 ? pMsgbase.total_msgs : 0);
}
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// Note: This assumes pSubBoardCode is not "mail" (personal mail).
// See if the message 'To' name matches the user's handle, alias,
// and/or username and is not marked as deleted and is unread.
return (((pMsgHdr.attr & MSG_DELETE) == 0) && ((pMsgHdr.attr & MSG_READ) == 0) && userNameHandleAliasMatch(pMsgHdr.to));
}
break;
case SEARCH_MSG_NEWSCAN:
case SEARCH_MSG_NEWSCAN_CUR_SUB:
case SEARCH_MSG_NEWSCAN_CUR_GRP:
case SEARCH_MSG_NEWSCAN_ALL:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// Note: This assumes pSubBoardCode is not "mail" (personal mail).
// Get the offset of the last read message and compare it with the
// offset of the given message header
var lastReadMsgHdr = pMsgBase.get_msg_header(false, msg_area.sub[pSubBoardCode].last_read, true);
var lastReadMsgOffset = (lastReadMsgHdr != null ? lastReadMsgHdr.offset : 0);
return (pMsgHdr.offset > lastReadMsgOffset);
}
break;
}
// Search the messages
if (matchFn != null)
{
for (var msgIdx = startMsgIndex; msgIdx < endMsgIndex; ++msgIdx)
{
var msgHeader = pMsgbase.get_msg_header(true, msgIdx, true);
// I've seen situations where the message header object is null for
// some reason, so check that before running the search function.
if (msgHeader != null)
{
if (matchFn(pSearchString, msgHeader, pMsgbase, pSubCode))
msgHeaders.indexed.push(msgHeader);
}
}
}
return msgHeaders;
}
// Returns whether or not a message is to the logged-in user and is not deleted.
//
// Parameters:
// pMsgHdr: A message header object
//
// Return value: Boolean - Whether or not the message is to the logged-in user
// and is not deleted.
function msgIsToLoggedInUserNum(pMsgHdr)
{
if (typeof(pMsgHdr) != "object")
return false;
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.to_ext == user.number));
}
// Returns whether or not a message is from the logged-in user and is not deleted.
//
// Parameters:
// pMsgHdr: A message header object
//
// Return value: Boolean - Whether or not the message is from the logged-in user
// and is not deleted.
function msgIsFromUser(pMsgHdr)
{
if (typeof(pMsgHdr) != "object")
return false;
var isFromCurrentUser = false;
if (((pMsgHdr.attr & MSG_DELETE) == 0) && pMsgHdr.hasOwnProperty("from_ext"))
isFromCurrentUser = (pMsgHdr.from_ext == user.number);
return isFromCurrentUser;
}
/////////////////////////////////////////////////////////////////////////
// Functions for converting other BBS color codes to Synchronet attribute codes
// Converts WWIV attribute codes to Synchronet attribute codes.
//
// Parameters:
// pText: A string containing the text to convert
//
// Return value: The text with the color codes converted
function WWIVAttrsToSyncAttrs(pText)
{
// First, see if the text has any WWIV-style attribute codes at
// all. We'll be performing a bunch of search & replace commands,
// so we don't want to do all that work for nothing.. :)
if (/\x03[0-9]/.test(pText))
{
var text = pText.replace(/\x030/g, "\1n"); // Normal
text = text.replace(/\x031/g, "\1n\1c\1h"); // Bright cyan
text = text.replace(/\x032/g, "\1n\1y\1h"); // Bright yellow
text = text.replace(/\x033/g, "\1n\1m"); // Magenta
text = text.replace(/\x034/g, "\1n\1h\1w\14"); // Bright white on blue
text = text.replace(/\x035/g, "\1n\1g"); // Green
text = text.replace(/\x036/g, "\1h\1r\1i"); // Bright red, blinking
text = text.replace(/\x037/g, "\1n\1h\1b"); // Bright blue
text = text.replace(/\x038/g, "\1n\1b"); // Blue
text = text.replace(/\x039/g, "\1n\1c"); // Cyan
return text;
}
else
return pText; // No WWIV-style color attribute found, so just return the text.
}
// Converts PCBoard attribute codes to Synchronet attribute codes.
//
// Parameters:
// pText: A string containing the text to convert
//
// Return value: The text with the color codes converted
function PCBoardAttrsToSyncAttrs(pText)
{
// First, see if the text has any PCBoard-style attribute codes at
// all. We'll be performing a bunch of search & replace commands,
// so we don't want to do all that work for nothing.. :)
if (/@[xX][0-9A-Fa-f]{2}/.test(pText))
{
// Black background
var text = pText.replace(/@[xX]00/g, "\1n\1k\10"); // Black on black
text = text.replace(/@[xX]01/g, "\1n\1b\10"); // Blue on black
text = text.replace(/@[xX]02/g, "\1n\1g\10"); // Green on black
text = text.replace(/@[xX]03/g, "\1n\1c\10"); // Cyan on black
text = text.replace(/@[xX]04/g, "\1n\1r\10"); // Red on black
text = text.replace(/@[xX]05/g, "\1n\1m\10"); // Magenta on black
text = text.replace(/@[xX]06/g, "\1n\1y\10"); // Yellow/brown on black
text = text.replace(/@[xX]07/g, "\1n\1w\10"); // White on black
text = text.replace(/@[xX]08/g, "\1n\1w\10"); // White on black
text = text.replace(/@[xX]09/g, "\1n\1w\10"); // White on black
text = text.replace(/@[xX]08/g, "\1h\1k\10"); // Bright black on black
text = text.replace(/@[xX]09/g, "\1h\1b\10"); // Bright blue on black
text = text.replace(/@[xX]0[Aa]/g, "\1h\1g\10"); // Bright green on black
text = text.replace(/@[xX]0[Bb]/g, "\1h\1c\10"); // Bright cyan on black
text = text.replace(/@[xX]0[Cc]/g, "\1h\1r\10"); // Bright red on black
text = text.replace(/@[xX]0[Dd]/g, "\1h\1m\10"); // Bright magenta on black
text = text.replace(/@[xX]0[Ee]/g, "\1h\1y\10"); // Bright yellow on black
text = text.replace(/@[xX]0[Ff]/g, "\1h\1w\10"); // Bright white on black
// Blinking foreground
// Blue background
text = text.replace(/@[xX]10/g, "\1n\1k\14"); // Black on blue
text = text.replace(/@[xX]11/g, "\1n\1b\14"); // Blue on blue
text = text.replace(/@[xX]12/g, "\1n\1g\14"); // Green on blue
text = text.replace(/@[xX]13/g, "\1n\1c\14"); // Cyan on blue
text = text.replace(/@[xX]14/g, "\1n\1r\14"); // Red on blue
text = text.replace(/@[xX]15/g, "\1n\1m\14"); // Magenta on blue
text = text.replace(/@[xX]16/g, "\1n\1y\14"); // Yellow/brown on blue
text = text.replace(/@[xX]17/g, "\1n\1w\14"); // White on blue
text = text.replace(/@[xX]18/g, "\1h\1k\14"); // Bright black on blue
text = text.replace(/@[xX]19/g, "\1h\1b\14"); // Bright blue on blue
text = text.replace(/@[xX]1[Aa]/g, "\1h\1g\14"); // Bright green on blue
text = text.replace(/@[xX]1[Bb]/g, "\1h\1c\14"); // Bright cyan on blue
text = text.replace(/@[xX]1[Cc]/g, "\1h\1r\14"); // Bright red on blue
text = text.replace(/@[xX]1[Dd]/g, "\1h\1m\14"); // Bright magenta on blue
text = text.replace(/@[xX]1[Ee]/g, "\1h\1y\14"); // Bright yellow on blue
text = text.replace(/@[xX]1[Ff]/g, "\1h\1w\14"); // Bright white on blue
// Green background
text = text.replace(/@[xX]20/g, "\1n\1k\12"); // Black on green
text = text.replace(/@[xX]21/g, "\1n\1b\12"); // Blue on green
text = text.replace(/@[xX]22/g, "\1n\1g\12"); // Green on green
text = text.replace(/@[xX]23/g, "\1n\1c\12"); // Cyan on green
text = text.replace(/@[xX]24/g, "\1n\1r\12"); // Red on green
text = text.replace(/@[xX]25/g, "\1n\1m\12"); // Magenta on green
text = text.replace(/@[xX]26/g, "\1n\1y\12"); // Yellow/brown on green
text = text.replace(/@[xX]27/g, "\1n\1w\12"); // White on green
text = text.replace(/@[xX]28/g, "\1h\1k\12"); // Bright black on green
text = text.replace(/@[xX]29/g, "\1h\1b\12"); // Bright blue on green
text = text.replace(/@[xX]2[Aa]/g, "\1h\1g\12"); // Bright green on green
text = text.replace(/@[xX]2[Bb]/g, "\1h\1c\12"); // Bright cyan on green
text = text.replace(/@[xX]2[Cc]/g, "\1h\1r\12"); // Bright red on green
text = text.replace(/@[xX]2[Dd]/g, "\1h\1m\12"); // Bright magenta on green
text = text.replace(/@[xX]2[Ee]/g, "\1h\1y\12"); // Bright yellow on green
text = text.replace(/@[xX]2[Ff]/g, "\1h\1w\12"); // Bright white on green
// Cyan background
text = text.replace(/@[xX]30/g, "\1n\1k\16"); // Black on cyan
text = text.replace(/@[xX]31/g, "\1n\1b\16"); // Blue on cyan
text = text.replace(/@[xX]32/g, "\1n\1g\16"); // Green on cyan
text = text.replace(/@[xX]33/g, "\1n\1c\16"); // Cyan on cyan
text = text.replace(/@[xX]34/g, "\1n\1r\16"); // Red on cyan
text = text.replace(/@[xX]35/g, "\1n\1m\16"); // Magenta on cyan
text = text.replace(/@[xX]36/g, "\1n\1y\16"); // Yellow/brown on cyan
text = text.replace(/@[xX]37/g, "\1n\1w\16"); // White on cyan
text = text.replace(/@[xX]38/g, "\1h\1k\16"); // Bright black on cyan
text = text.replace(/@[xX]39/g, "\1h\1b\16"); // Bright blue on cyan
text = text.replace(/@[xX]3[Aa]/g, "\1h\1g\16"); // Bright green on cyan
text = text.replace(/@[xX]3[Bb]/g, "\1h\1c\16"); // Bright cyan on cyan
text = text.replace(/@[xX]3[Cc]/g, "\1h\1r\16"); // Bright red on cyan
text = text.replace(/@[xX]3[Dd]/g, "\1h\1m\16"); // Bright magenta on cyan
text = text.replace(/@[xX]3[Ee]/g, "\1h\1y\16"); // Bright yellow on cyan
text = text.replace(/@[xX]3[Ff]/g, "\1h\1w\16"); // Bright white on cyan
// Red background
text = text.replace(/@[xX]40/g, "\1n\1k\11"); // Black on red
text = text.replace(/@[xX]41/g, "\1n\1b\11"); // Blue on red
text = text.replace(/@[xX]42/g, "\1n\1g\11"); // Green on red
text = text.replace(/@[xX]43/g, "\1n\1c\11"); // Cyan on red
text = text.replace(/@[xX]44/g, "\1n\1r\11"); // Red on red
text = text.replace(/@[xX]45/g, "\1n\1m\11"); // Magenta on red
text = text.replace(/@[xX]46/g, "\1n\1y\11"); // Yellow/brown on red
text = text.replace(/@[xX]47/g, "\1n\1w\11"); // White on red
text = text.replace(/@[xX]48/g, "\1h\1k\11"); // Bright black on red
text = text.replace(/@[xX]49/g, "\1h\1b\11"); // Bright blue on red
text = text.replace(/@[xX]4[Aa]/g, "\1h\1g\11"); // Bright green on red
text = text.replace(/@[xX]4[Bb]/g, "\1h\1c\11"); // Bright cyan on red
text = text.replace(/@[xX]4[Cc]/g, "\1h\1r\11"); // Bright red on red
text = text.replace(/@[xX]4[Dd]/g, "\1h\1m\11"); // Bright magenta on red
text = text.replace(/@[xX]4[Ee]/g, "\1h\1y\11"); // Bright yellow on red
text = text.replace(/@[xX]4[Ff]/g, "\1h\1w\11"); // Bright white on red
// Magenta background
text = text.replace(/@[xX]50/g, "\1n\1k\15"); // Black on magenta
text = text.replace(/@[xX]51/g, "\1n\1b\15"); // Blue on magenta
text = text.replace(/@[xX]52/g, "\1n\1g\15"); // Green on magenta
text = text.replace(/@[xX]53/g, "\1n\1c\15"); // Cyan on magenta
text = text.replace(/@[xX]54/g, "\1n\1r\15"); // Red on magenta
text = text.replace(/@[xX]55/g, "\1n\1m\15"); // Magenta on magenta
text = text.replace(/@[xX]56/g, "\1n\1y\15"); // Yellow/brown on magenta
text = text.replace(/@[xX]57/g, "\1n\1w\15"); // White on magenta
text = text.replace(/@[xX]58/g, "\1h\1k\15"); // Bright black on magenta
text = text.replace(/@[xX]59/g, "\1h\1b\15"); // Bright blue on magenta
text = text.replace(/@[xX]5[Aa]/g, "\1h\1g\15"); // Bright green on magenta
text = text.replace(/@[xX]5[Bb]/g, "\1h\1c\15"); // Bright cyan on magenta
text = text.replace(/@[xX]5[Cc]/g, "\1h\1r\15"); // Bright red on magenta
text = text.replace(/@[xX]5[Dd]/g, "\1h\1m\15"); // Bright magenta on magenta
text = text.replace(/@[xX]5[Ee]/g, "\1h\1y\15"); // Bright yellow on magenta
text = text.replace(/@[xX]5[Ff]/g, "\1h\1w\15"); // Bright white on magenta
// Brown background
text = text.replace(/@[xX]60/g, "\1n\1k\13"); // Black on brown
text = text.replace(/@[xX]61/g, "\1n\1b\13"); // Blue on brown
text = text.replace(/@[xX]62/g, "\1n\1g\13"); // Green on brown
text = text.replace(/@[xX]63/g, "\1n\1c\13"); // Cyan on brown
text = text.replace(/@[xX]64/g, "\1n\1r\13"); // Red on brown
text = text.replace(/@[xX]65/g, "\1n\1m\13"); // Magenta on brown
text = text.replace(/@[xX]66/g, "\1n\1y\13"); // Yellow/brown on brown
text = text.replace(/@[xX]67/g, "\1n\1w\13"); // White on brown
text = text.replace(/@[xX]68/g, "\1h\1k\13"); // Bright black on brown
text = text.replace(/@[xX]69/g, "\1h\1b\13"); // Bright blue on brown
text = text.replace(/@[xX]6[Aa]/g, "\1h\1g\13"); // Bright breen on brown
text = text.replace(/@[xX]6[Bb]/g, "\1h\1c\13"); // Bright cyan on brown
text = text.replace(/@[xX]6[Cc]/g, "\1h\1r\13"); // Bright red on brown
text = text.replace(/@[xX]6[Dd]/g, "\1h\1m\13"); // Bright magenta on brown
text = text.replace(/@[xX]6[Ee]/g, "\1h\1y\13"); // Bright yellow on brown
text = text.replace(/@[xX]6[Ff]/g, "\1h\1w\13"); // Bright white on brown
// White background
text = text.replace(/@[xX]70/g, "\1n\1k\17"); // Black on white
text = text.replace(/@[xX]71/g, "\1n\1b\17"); // Blue on white
text = text.replace(/@[xX]72/g, "\1n\1g\17"); // Green on white
text = text.replace(/@[xX]73/g, "\1n\1c\17"); // Cyan on white
text = text.replace(/@[xX]74/g, "\1n\1r\17"); // Red on white
text = text.replace(/@[xX]75/g, "\1n\1m\17"); // Magenta on white
text = text.replace(/@[xX]76/g, "\1n\1y\17"); // Yellow/brown on white
text = text.replace(/@[xX]77/g, "\1n\1w\17"); // White on white
text = text.replace(/@[xX]78/g, "\1h\1k\17"); // Bright black on white
text = text.replace(/@[xX]79/g, "\1h\1b\17"); // Bright blue on white
text = text.replace(/@[xX]7[Aa]/g, "\1h\1g\17"); // Bright green on white
text = text.replace(/@[xX]7[Bb]/g, "\1h\1c\17"); // Bright cyan on white
text = text.replace(/@[xX]7[Cc]/g, "\1h\1r\17"); // Bright red on white
text = text.replace(/@[xX]7[Dd]/g, "\1h\1m\17"); // Bright magenta on white
text = text.replace(/@[xX]7[Ee]/g, "\1h\1y\17"); // Bright yellow on white
text = text.replace(/@[xX]7[Ff]/g, "\1h\1w\17"); // Bright white on white
// Black background, blinking foreground
text = text.replace(/@[xX]80/g, "\1n\1k\10\1i"); // Blinking black on black
text = text.replace(/@[xX]81/g, "\1n\1b\10\1i"); // Blinking blue on black
text = text.replace(/@[xX]82/g, "\1n\1g\10\1i"); // Blinking green on black
text = text.replace(/@[xX]83/g, "\1n\1c\10\1i"); // Blinking cyan on black
text = text.replace(/@[xX]84/g, "\1n\1r\10\1i"); // Blinking red on black
text = text.replace(/@[xX]85/g, "\1n\1m\10\1i"); // Blinking magenta on black
text = text.replace(/@[xX]86/g, "\1n\1y\10\1i"); // Blinking yellow/brown on black
text = text.replace(/@[xX]87/g, "\1n\1w\10\1i"); // Blinking white on black
text = text.replace(/@[xX]88/g, "\1h\1k\10\1i"); // Blinking bright black on black
text = text.replace(/@[xX]89/g, "\1h\1b\10\1i"); // Blinking bright blue on black
text = text.replace(/@[xX]8[Aa]/g, "\1h\1g\10\1i"); // Blinking bright green on black
text = text.replace(/@[xX]8[Bb]/g, "\1h\1c\10\1i"); // Blinking bright cyan on black
text = text.replace(/@[xX]8[Cc]/g, "\1h\1r\10\1i"); // Blinking bright red on black
text = text.replace(/@[xX]8[Dd]/g, "\1h\1m\10\1i"); // Blinking bright magenta on black
text = text.replace(/@[xX]8[Ee]/g, "\1h\1y\10\1i"); // Blinking bright yellow on black
text = text.replace(/@[xX]8[Ff]/g, "\1h\1w\10\1i"); // Blinking bright white on black
// Blue background, blinking foreground
text = text.replace(/@[xX]90/g, "\1n\1k\14\1i"); // Blinking black on blue
text = text.replace(/@[xX]91/g, "\1n\1b\14\1i"); // Blinking blue on blue
text = text.replace(/@[xX]92/g, "\1n\1g\14\1i"); // Blinking green on blue
text = text.replace(/@[xX]93/g, "\1n\1c\14\1i"); // Blinking cyan on blue
text = text.replace(/@[xX]94/g, "\1n\1r\14\1i"); // Blinking red on blue
text = text.replace(/@[xX]95/g, "\1n\1m\14\1i"); // Blinking magenta on blue
text = text.replace(/@[xX]96/g, "\1n\1y\14\1i"); // Blinking yellow/brown on blue
text = text.replace(/@[xX]97/g, "\1n\1w\14\1i"); // Blinking white on blue
text = text.replace(/@[xX]98/g, "\1h\1k\14\1i"); // Blinking bright black on blue
text = text.replace(/@[xX]99/g, "\1h\1b\14\1i"); // Blinking bright blue on blue
text = text.replace(/@[xX]9[Aa]/g, "\1h\1g\14\1i"); // Blinking bright green on blue
text = text.replace(/@[xX]9[Bb]/g, "\1h\1c\14\1i"); // Blinking bright cyan on blue
text = text.replace(/@[xX]9[Cc]/g, "\1h\1r\14\1i"); // Blinking bright red on blue
text = text.replace(/@[xX]9[Dd]/g, "\1h\1m\14\1i"); // Blinking bright magenta on blue
text = text.replace(/@[xX]9[Ee]/g, "\1h\1y\14\1i"); // Blinking bright yellow on blue
text = text.replace(/@[xX]9[Ff]/g, "\1h\1w\14\1i"); // Blinking bright white on blue
// Green background, blinking foreground
text = text.replace(/@[xX][Aa]0/g, "\1n\1k\12\1i"); // Blinking black on green
text = text.replace(/@[xX][Aa]1/g, "\1n\1b\12\1i"); // Blinking blue on green
text = text.replace(/@[xX][Aa]2/g, "\1n\1g\12\1i"); // Blinking green on green
text = text.replace(/@[xX][Aa]3/g, "\1n\1c\12\1i"); // Blinking cyan on green
text = text.replace(/@[xX][Aa]4/g, "\1n\1r\12\1i"); // Blinking red on green
text = text.replace(/@[xX][Aa]5/g, "\1n\1m\12\1i"); // Blinking magenta on green
text = text.replace(/@[xX][Aa]6/g, "\1n\1y\12\1i"); // Blinking yellow/brown on green
text = text.replace(/@[xX][Aa]7/g, "\1n\1w\12\1i"); // Blinking white on green
text = text.replace(/@[xX][Aa]8/g, "\1h\1k\12\1i"); // Blinking bright black on green
text = text.replace(/@[xX][Aa]9/g, "\1h\1b\12\1i"); // Blinking bright blue on green
text = text.replace(/@[xX][Aa][Aa]/g, "\1h\1g\12\1i"); // Blinking bright green on green
text = text.replace(/@[xX][Aa][Bb]/g, "\1h\1c\12\1i"); // Blinking bright cyan on green
text = text.replace(/@[xX][Aa][Cc]/g, "\1h\1r\12\1i"); // Blinking bright red on green
text = text.replace(/@[xX][Aa][Dd]/g, "\1h\1m\12\1i"); // Blinking bright magenta on green
text = text.replace(/@[xX][Aa][Ee]/g, "\1h\1y\12\1i"); // Blinking bright yellow on green
text = text.replace(/@[xX][Aa][Ff]/g, "\1h\1w\12\1i"); // Blinking bright white on green
// Cyan background, blinking foreground
text = text.replace(/@[xX][Bb]0/g, "\1n\1k\16\1i"); // Blinking black on cyan
text = text.replace(/@[xX][Bb]1/g, "\1n\1b\16\1i"); // Blinking blue on cyan
text = text.replace(/@[xX][Bb]2/g, "\1n\1g\16\1i"); // Blinking green on cyan
text = text.replace(/@[xX][Bb]3/g, "\1n\1c\16\1i"); // Blinking cyan on cyan
text = text.replace(/@[xX][Bb]4/g, "\1n\1r\16\1i"); // Blinking red on cyan
text = text.replace(/@[xX][Bb]5/g, "\1n\1m\16\1i"); // Blinking magenta on cyan
text = text.replace(/@[xX][Bb]6/g, "\1n\1y\16\1i"); // Blinking yellow/brown on cyan
text = text.replace(/@[xX][Bb]7/g, "\1n\1w\16\1i"); // Blinking white on cyan
text = text.replace(/@[xX][Bb]8/g, "\1h\1k\16\1i"); // Blinking bright black on cyan
text = text.replace(/@[xX][Bb]9/g, "\1h\1b\16\1i"); // Blinking bright blue on cyan
text = text.replace(/@[xX][Bb][Aa]/g, "\1h\1g\16\1i"); // Blinking bright green on cyan
text = text.replace(/@[xX][Bb][Bb]/g, "\1h\1c\16\1i"); // Blinking bright cyan on cyan
text = text.replace(/@[xX][Bb][Cc]/g, "\1h\1r\16\1i"); // Blinking bright red on cyan
text = text.replace(/@[xX][Bb][Dd]/g, "\1h\1m\16\1i"); // Blinking bright magenta on cyan
text = text.replace(/@[xX][Bb][Ee]/g, "\1h\1y\16\1i"); // Blinking bright yellow on cyan
text = text.replace(/@[xX][Bb][Ff]/g, "\1h\1w\16\1i"); // Blinking bright white on cyan
// Red background, blinking foreground
text = text.replace(/@[xX][Cc]0/g, "\1n\1k\11\1i"); // Blinking black on red
text = text.replace(/@[xX][Cc]1/g, "\1n\1b\11\1i"); // Blinking blue on red
text = text.replace(/@[xX][Cc]2/g, "\1n\1g\11\1i"); // Blinking green on red
text = text.replace(/@[xX][Cc]3/g, "\1n\1c\11\1i"); // Blinking cyan on red
text = text.replace(/@[xX][Cc]4/g, "\1n\1r\11\1i"); // Blinking red on red
text = text.replace(/@[xX][Cc]5/g, "\1n\1m\11\1i"); // Blinking magenta on red
text = text.replace(/@[xX][Cc]6/g, "\1n\1y\11\1i"); // Blinking yellow/brown on red
text = text.replace(/@[xX][Cc]7/g, "\1n\1w\11\1i"); // Blinking white on red
text = text.replace(/@[xX][Cc]8/g, "\1h\1k\11\1i"); // Blinking bright black on red
text = text.replace(/@[xX][Cc]9/g, "\1h\1b\11\1i"); // Blinking bright blue on red
text = text.replace(/@[xX][Cc][Aa]/g, "\1h\1g\11\1i"); // Blinking bright green on red
text = text.replace(/@[xX][Cc][Bb]/g, "\1h\1c\11\1i"); // Blinking bright cyan on red
text = text.replace(/@[xX][Cc][Cc]/g, "\1h\1r\11\1i"); // Blinking bright red on red
text = text.replace(/@[xX][Cc][Dd]/g, "\1h\1m\11\1i"); // Blinking bright magenta on red
text = text.replace(/@[xX][Cc][Ee]/g, "\1h\1y\11\1i"); // Blinking bright yellow on red
text = text.replace(/@[xX][Cc][Ff]/g, "\1h\1w\11\1i"); // Blinking bright white on red1
// Magenta background, blinking foreground
text = text.replace(/@[xX][Dd]0/g, "\1n\1k\15\1i"); // Blinking black on magenta
text = text.replace(/@[xX][Dd]1/g, "\1n\1b\15\1i"); // Blinking blue on magenta
text = text.replace(/@[xX][Dd]2/g, "\1n\1g\15\1i"); // Blinking green on magenta
text = text.replace(/@[xX][Dd]3/g, "\1n\1c\15\1i"); // Blinking cyan on magenta
text = text.replace(/@[xX][Dd]4/g, "\1n\1r\15\1i"); // Blinking red on magenta
text = text.replace(/@[xX][Dd]5/g, "\1n\1m\15\1i"); // Blinking magenta on magenta
text = text.replace(/@[xX][Dd]6/g, "\1n\1y\15\1i"); // Blinking yellow/brown on magenta
text = text.replace(/@[xX][Dd]7/g, "\1n\1w\15\1i"); // Blinking white on magenta
text = text.replace(/@[xX][Dd]8/g, "\1h\1k\15\1i"); // Blinking bright black on magenta
text = text.replace(/@[xX][Dd]9/g, "\1h\1b\15\1i"); // Blinking bright blue on magenta
text = text.replace(/@[xX][Dd][Aa]/g, "\1h\1g\15\1i"); // Blinking bright green on magenta
text = text.replace(/@[xX][Dd][Bb]/g, "\1h\1c\15\1i"); // Blinking bright cyan on magenta
text = text.replace(/@[xX][Dd][Cc]/g, "\1h\1r\15\1i"); // Blinking bright red on magenta
text = text.replace(/@[xX][Dd][Dd]/g, "\1h\1m\15\1i"); // Blinking bright magenta on magenta
text = text.replace(/@[xX][Dd][Ee]/g, "\1h\1y\15\1i"); // Blinking bright yellow on magenta
text = text.replace(/@[xX][Dd][Ff]/g, "\1h\1w\15\1i"); // Blinking bright white on magenta
// Brown background, blinking foreground
text = text.replace(/@[xX][Ee]0/g, "\1n\1k\13\1i"); // Blinking black on brown
text = text.replace(/@[xX][Ee]1/g, "\1n\1b\13\1i"); // Blinking blue on brown
text = text.replace(/@[xX][Ee]2/g, "\1n\1g\13\1i"); // Blinking green on brown
text = text.replace(/@[xX][Ee]3/g, "\1n\1c\13\1i"); // Blinking cyan on brown
text = text.replace(/@[xX][Ee]4/g, "\1n\1r\13\1i"); // Blinking red on brown
text = text.replace(/@[xX][Ee]5/g, "\1n\1m\13\1i"); // Blinking magenta on brown
text = text.replace(/@[xX][Ee]6/g, "\1n\1y\13\1i"); // Blinking yellow/brown on brown
text = text.replace(/@[xX][Ee]7/g, "\1n\1w\13\1i"); // Blinking white on brown
text = text.replace(/@[xX][Ee]8/g, "\1h\1k\13\1i"); // Blinking bright black on brown
text = text.replace(/@[xX][Ee]9/g, "\1h\1b\13\1i"); // Blinking bright blue on brown
text = text.replace(/@[xX][Ee][Aa]/g, "\1h\1g\13\1i"); // Blinking bright green on brown
text = text.replace(/@[xX][Ee][Bb]/g, "\1h\1c\13\1i"); // Blinking bright cyan on brown
text = text.replace(/@[xX][Ee][Cc]/g, "\1h\1r\13\1i"); // Blinking bright red on brown
text = text.replace(/@[xX][Ee][Dd]/g, "\1h\1m\13\1i"); // Blinking bright magenta on brown
text = text.replace(/@[xX][Ee][Ee]/g, "\1h\1y\13\1i"); // Blinking bright yellow on brown
text = text.replace(/@[xX][Ee][Ff]/g, "\1h\1w\13\1i"); // Blinking bright white on brown