Work in progress with new page.js

This commit is contained in:
Deon George 2022-12-09 17:19:33 +11:00 committed by Deon George
parent ef4453b77b
commit 1c80c3951a
19 changed files with 4109 additions and 1416 deletions

View File

@ -20,21 +20,21 @@ auth_post=/api/sqrl
[prefix] [prefix]
key=0@videotex key=0@videotex
logoans=AWgBUkEBR04BQlMBWUkBbgE3AWt0ZXgBbg== logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
logovtx=AUECTgNTBEkHdGV4 logovtx=AUECTgNTBEkHdGV4
user=1 user=1
; System frames are owned by this key ; System frames are owned by this key
[prefix:9] [prefix:9]
key=0@videotex key=0@videotex
logoans=AWgBUkEBR04BQlMBWUkBbgE3AWt0ZXgBbg== logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
logovtx=AUECTgNTBEkHdGV4 logovtx=AUECTgNTBEkHdGV4
user=1 user=1
; Ansitex Help Pages ; ANSItex Help Pages
[prefix:516] [prefix:516]
key=516@videotex key=516@videotex
logoans=AWgBUkEBR04BQlMBWUkBbgE3AWt0ZXgBbg== logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
logovtx=AUECTgNTBEkHdGV4 logovtx=AUECTgNTBEkHdGV4
user=1 user=1

View File

@ -1,6 +1,6 @@
load('smbdefs.js'); load('smbdefs.js');
// Ansitex specific includes // ANSItex specific includes
load('ansitex/load/defs.js'); load('ansitex/load/defs.js');
load('ansitex/load/funcs.js'); load('ansitex/load/funcs.js');

View File

@ -16,7 +16,7 @@ require('key_defs.js','KEY_ESC');
ansi = load({},'ansiterm_lib.js'); ansi = load({},'ansiterm_lib.js');
load('ansitex/load/funcs.js'); load('ansitex/load/funcs.js');
// Ansitex specific includes // ANSItex specific includes
require('ansitex/load/defs.js','ACTION_EXIT'); require('ansitex/load/defs.js','ACTION_EXIT');
require('ansitex/load/frame-ansi.js','FRAME_ANSI'); require('ansitex/load/frame-ansi.js','FRAME_ANSI');
require('ansitex/load/frame-viewdata.js','FRAME_VIEWDATA'); require('ansitex/load/frame-viewdata.js','FRAME_VIEWDATA');

View File

@ -38,7 +38,7 @@ function register() {
this.handle=function(read) { this.handle=function(read) {
// Dont allow existing users to re-register // Dont allow existing users to re-register
if (user.number) { if (user.number) {
fo.sendBaseline('ALREADY_MEMBER',false); fo.baselineSend('ALREADY_MEMBER',false);
return read === '*' ? read : ''; return read === '*' ? read : '';
} }
@ -48,33 +48,33 @@ function register() {
return read; return read;
} }
log(LOG_DEBUG,'- Field '+cf.fname+'('+JSON.stringify(cf)+')'); log(LOG_DEBUG,'- Field '+cf.name+'('+JSON.stringify(cf)+')');
if ((cf.fname === 'TOKEN') && (read === '#' || read === "\r")) { if ((cf.name === 'TOKEN') && (read === '#' || read === "\r")) {
if (cf.fvalue === code) { if (cf.value === code) {
complete = true; complete = true;
} else { } else {
fo.sendBaseline('INVALID_CODE',false); fo.baselineSend('INVALID_CODE',false);
fo.cursorOn(cf.c+cf.fvalue.length,cf.r); fo.cursorOn(cf.c+cf.value.length,cf.r);
fo.attr(cf.attribute); fo.attr(cf.attribute);
read = ''; read = '';
} }
} }
log(LOG_DEBUG,'- Field Value ['+cf.fvalue+'] ('+code+')'); log(LOG_DEBUG,'- Field Value ['+cf.value+'] ('+code+')');
return read; return read;
} }
// Make sure we got an email // Make sure we got an email
this.prefield=function() { this.prefield=function() {
log(LOG_DEBUG,'- prefield: Field '+cf.fname+'('+JSON.stringify(cf)+')'); log(LOG_DEBUG,'- prefield: Field '+cf.name+'('+JSON.stringify(cf)+')');
if (cf.fname === 'TOKEN') { if (cf.name === 'TOKEN') {
if (! code.length) { if (! code.length) {
log(LOG_DEBUG,' - BASELINE '+cf.fname+'('+JSON.stringify(cf)+')'); log(LOG_DEBUG,' - BASELINE '+cf.name+'('+JSON.stringify(cf)+')');
fo.sendBaseline('TOKEN_EMAIL',false); fo.baselineSend('TOKEN_EMAIL',false);
var email = fo.fieldValue('EMAIL'); var email = fo.fieldValue('EMAIL');
var uid = fo.fieldValue('UID'); var uid = fo.fieldValue('UID');
@ -85,13 +85,13 @@ function register() {
// Validate Email hasnt been used // Validate Email hasnt been used
// Validate USER_ID hasnt been used // Validate USER_ID hasnt been used
if ((email.indexOf('@') === -1) || ! EMAIL_REGEX.test(email) || (system.matchuserdata(U_NETMAIL,email) !== 0)) { if ((email.indexOf('@') === -1) || ! EMAIL_REGEX.test(email) || (system.matchuserdata(U_NETMAIL,email) !== 0)) {
fo.sendBaseline('INVALID_EMAIL',false); fo.baselineSend('INVALID_EMAIL',false);
return; return;
} }
if (! system.check_name(uid)) { if (! system.check_name(uid)) {
log(LOG_DEBUG,' - Cannot use user_id: ('+uid+')'); log(LOG_DEBUG,' - Cannot use user_id: ('+uid+')');
fo.sendBaseline('INVALID_UID',false); fo.baselineSend('INVALID_UID',false);
return; return;
} }
@ -106,7 +106,7 @@ function register() {
if (hdrs.to_net_type !== NET_NONE) { if (hdrs.to_net_type !== NET_NONE) {
hdrs.to_net_addr = email; hdrs.to_net_addr = email;
} else { } else {
fo.sendBaseline('CANNOT_SEND_TOKEN',false); fo.baselineSend('CANNOT_SEND_TOKEN',false);
return; return;
} }
hdrs.from=system.name; hdrs.from=system.name;
@ -137,7 +137,7 @@ function register() {
msgbase.close(); msgbase.close();
} }
fo.sendBaseline('TOKEN_SENT',false); fo.baselineSend('TOKEN_SENT',false);
log(LOG_DEBUG,'SENT EMAIL TOKEN ('+code+') ['+JSON.stringify(hdrs)+']'); log(LOG_DEBUG,'SENT EMAIL TOKEN ('+code+') ['+JSON.stringify(hdrs)+']');
} }
} }
@ -149,7 +149,7 @@ function register() {
var newuser = system.new_user(fo.fieldValue('UID')); var newuser = system.new_user(fo.fieldValue('UID'));
} catch (e) { } catch (e) {
fo.sendBaseline('USER_EXISTS',false); fo.baselineSend('USER_EXISTS',false);
log(LOG_ERROR,"New user couldn't be created (user created while signing up)"); log(LOG_ERROR,"New user couldn't be created (user created while signing up)");
log(LOG_ERROR,JSON.stringify(e)); log(LOG_ERROR,JSON.stringify(e));
processed = true; processed = true;
@ -157,7 +157,7 @@ function register() {
} }
if (typeof newuser === 'number') { if (typeof newuser === 'number') {
fo.sendBaseline('USER_CREATE_ERROR',false); fo.baselineSend('USER_CREATE_ERROR',false);
log(LOG_ERROR,"New user couldn't be created (error code "+newuser+")"); log(LOG_ERROR,"New user couldn't be created (error code "+newuser+")");
processed = true; processed = true;
return this.isComplete; return this.isComplete;
@ -172,7 +172,7 @@ function register() {
user.location = fo.fieldValue('CITY')+', '+fo.fieldValue('COUNTRY'); user.location = fo.fieldValue('CITY')+', '+fo.fieldValue('COUNTRY');
user.zipcode = fo.fieldValue('PCODE'); user.zipcode = fo.fieldValue('PCODE');
user.netmail = fo.fieldValue('EMAIL'); user.netmail = fo.fieldValue('EMAIL');
user.comment = 'ANSITEX registered user'; user.comment = 'ANSItex registered user';
bbs.user_sync(); bbs.user_sync();
bbs.logon(); bbs.logon();
@ -183,7 +183,7 @@ function register() {
return this.isComplete; return this.isComplete;
} else { } else {
fo.sendBaseline('LOGIN_ERROR',false); fo.baselineSend('LOGIN_ERROR',false);
log(LOG_INFO,"bbs.login() failed"); log(LOG_INFO,"bbs.login() failed");
user.comment = 'Initial login failed!'; user.comment = 'Initial login failed!';
newuser.settings |= USER_DELETED; newuser.settings |= USER_DELETED;

View File

@ -43,7 +43,7 @@ function sqrllogin() {
var subframe = new Frame((viewdata ? fo.settings.FRAME_WIDTH : fo.settings.FRAME_WIDTH-qr.size-2),2,(viewdata ? qr.size/2 : qr.size+2),22,BG_BLACK|LIGHTGRAY); var subframe = new Frame((viewdata ? fo.settings.FRAME_WIDTH : fo.settings.FRAME_WIDTH-qr.size-2),2,(viewdata ? qr.size/2 : qr.size+2),22,BG_BLACK|LIGHTGRAY);
fo.qrcode(qr,subframe); fo.qrcode(qr,subframe);
fo.sendBaseline('CANCEL_MSG',false); fo.baselineSend('CANCEL_MSG',false);
// Loop and see if the user has logged in // Loop and see if the user has logged in
var nut = http.body.substr(http.body.indexOf('nut='),68); var nut = http.body.substr(http.body.indexOf('nut='),68);
@ -127,7 +127,7 @@ function sqrllogin() {
user.zipcode = '000'; user.zipcode = '000';
user.netmail = username+'@'+system.inet_addr; user.netmail = username+'@'+system.inet_addr;
user.comment = 'ANSITEX registered user - with SQRL'; user.comment = 'ANSItex registered user - with SQRL';
bbs.user_sync(); bbs.user_sync();
} else { } else {

View File

@ -96,7 +96,7 @@ const HOME_FRAME_CONNECT ={frame: 980,index: 'a'};
const FRAME_SYSTEM_ERROR ={frame: 998,index: 'a'}; const FRAME_SYSTEM_ERROR ={frame: 998,index: 'a'};
/* Attributes saved/loaded from files */ /* Attributes saved/loaded from files */
const SAVED_FRAME_ATTRS =['version','frame','frame_fields','index','owner','cost','content','isPublic','isAccessible','type','key']; const SAVED_FRAME_ATTRS =['content','version','frame','input_fields','index','owner','cost','isPublic','isAccessible','type','key','date'];
/* The page that has our echomail area reading template */ /* The page that has our echomail area reading template */
var MAIL_TEMPLATE_FRAME ='199a'; var MAIL_TEMPLATE_FRAME ='199a';
@ -107,4 +107,7 @@ var MAIL_TEMPLATE_AREA_SUMMARY ='198a';
// The maximum size of embedded dynamic fields in frames // The maximum size of embedded dynamic fields in frames
var DYNAMIC_FIELD_SIZE_MAX =50; var DYNAMIC_FIELD_SIZE_MAX =50;
/** ESCAPE CODES **/
const ESC = '\x1b';
this; this;

View File

@ -1,213 +0,0 @@
var FRAME_ANSI = (1<<1);
load('ansitex/load/frame-page.js');
// Our frame object
function FrameAnsi() {
PageFrame.apply(this,arguments);
/* File Extension used for frames */
this.settings.ext = 'tex';
/* Length of a frame */
this.settings.FRAME_LENGTH = 22;
/* Width of a frame */
this.settings.FRAME_WIDTH = 80;
/* Size of page owner (length) */
this.settings.FRAME_HEADER = 56;
/* Size of page number (length with a-z) */
this.settings.FRAME_PAGENUM = 12;
/* Size of cost (length without unit) */
this.settings.FRAME_COST = 9;
this.settings.MSG_SENDORNOT = '\1n\1h\1GKEY 1 TO SEND, 2 NOT TO SEND';
this.settings.MSG_LOGON = '\1n\1h\1GKEY 1 TO LOGON, 2 TO RETURN';
this.settings.MSG_SENT = '\1n\1h\1GMESSAGE SENT - KEY # TO CONTINUE';
this.settings.MSG_NOTSENT = '\1n\1h\1GMESSAGE NOT SENT - KEY # TO CONTINUE';
this.settings.ERR_NO_PARENT = '\1n\1h\1RPARENT FRAME DOESNT EXIST';
this.settings.ERR_NOT_IMPLEMENTED = '\1n\1h\1RNOT IMPLEMENTED YET?';
this.settings.ERR_ROUTE = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08';
this.settings.ERR_METHOD_NOT_EXIST = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08';
this.settings.ACCESS_DENIED = '\1n\1h\1RACCESS DENIED. MISTAKE? TRY AGAIN OR TELL US *08';
this.settings.ALREADY_MEMBER = '\1n\1h\1RALREADY MEMBER OF CUG'
this.settings.INACTIVITY = '\1n\1h\1RINACTIVITY ALERT, DISCONNECT PENDING...';
this.settings.INACTIVE = '\1n\1h\1RINACTIVITY DISCONNECT';
this.settings.NOACTION = '\1n\1h\1RNO ACTION PERFORMED';
this.settings.BASESTAR = '\1N\1G\1H*';
this.settings.INVALID_CODE = '\1n\1h\1RINVAID CODE, PLEASE TRY AGAIN *00';
this.settings.TOKEN_EMAIL = '\1n\1h\1RTOKEN EMAILED TO YOU...';
this.settings.TOKEN_SENT = '\1n\1h\1RTOKEN SENT, PLEASE ENTER TOKEN';
this.settings.INVALID_EMAIL = '\1n\1h\1RINVAID EMAIL, PLEASE TRY AGAIN *00';
this.settings.INVALID_UID = '\1n\1h\1RINVAID USER ID, PLEASE TRY AGAIN *00';
this.settings.CANNOT_SEND_TOKEN = '\1n\1h\1RCANNOT SEND VALIDATION CODE, PLEASE TRY AGAIN *00';
this.settings.USER_EXISTS = '\1n\1h\1RERROR USER EXISTS, PLEASE TRY AGAIN *00';
this.settings.USER_CREATE_ERROR = '\1n\1h\1RERROR CREATING USER, PLEASE TRY AGAIN *00';
this.settings.LOGIN_ERROR = '\1n\1h\1RERROR LOGGING IN, PLEASE TRY AGAIN *00';
this.settings.CANCEL_MSG = '\1n\1h\1GPRESS 2 TO CANCEL';
this.settings.SYS_ERROR = '\1n\1h\1RSYSTEM ERROR DETECTED - TRY AGAIN OR TELL US *08';
this.settings.LOADING = '\1n\1h\1Kloading...';
/**
* Set the attribute at the current position
*/
this.attr=function(field) {
console.write(ascii(27)+'['+field.i+';'+field.f+';'+field.b+'m');
}
/**
* Turn off the cursor
*/
this.cursorOff=function() {
ansi.send('ext_mode','clear','cursor');
this.gotoxy(0,24);
}
this.cursorOn=function(x,y) {
ansi.send('ext_mode','set','cursor');
if (x && y)
this.gotoxy(x,y);
}
// Field backspace, that leaves the field filler char
this.fieldbs=function(char) {
console.write(ascii(27)+'[D'+char+ascii(27)+'[D');
}
this.gotoxy=function(x,y) {
console.gotoxy(x,y);
}
// Render the frame to the user
this.render=function(context,withoutHeader) {
log(LOG_DEBUG,'- ANSI FRAME');
owner = base64_decode(this.owner);
const frame = new Frame(1,1,this.settings.FRAME_WIDTH,this.settings.FRAME_LENGTH+2,LIGHTGRAY);
frame.open();
// Dont show the page number on system login page
if ((! withoutHeader) && (user.number || (this.type !== FRAME_TYPE_LOGIN && NO_HISTORY_FRAMES.indexOf(this.page) === -1))) {
log(LOG_DEBUG,' - Owner: ['+this.pageowner+']');
cost = (this.isAccessible ? this.cost+FRAME_COSTUNIT : ' -');
header = '\1n'+this.pageownerlogo+' '.repeat(this.settings.FRAME_HEADER-console.strlen(this.pageownerlogo))+'\1n '+
(this.isAccessible ? '\1W' : '\1R')+'\1H'+this.page+' '.repeat(this.settings.FRAME_PAGENUM-this.page.length)+' '+
'\1G\1H'+' '.repeat(this.settings.FRAME_COST-cost.toString().length+1)+cost+'\1n'+
(console.screen_columns > 80 ? '\n\r' : '');
frame.putmsg(header);
}
contentgraphic = new Graphic(this.settings.FRAME_WIDTH);
contentgraphic.auto_extend = true;
contentgraphic.atcodes = false;
contentgraphic.ANSI = this.parse(base64_decode(this.content),context);
var contentframe = new Frame(1,2,this.settings.FRAME_WIDTH,this.settings.FRAME_LENGTH,LIGHTGRAY,frame);
contentframe.open();
contentframe.lf_strict = false;
contentframe.atcodes = false;
contentframe.word_wrap = false
contentframe.putmsg(contentgraphic.MSG)
contentframe.scrollTo(0,0);
frame.cycle();
return contentframe;
};
this.qrcode=function(qr,subframe) {
// SMALL Image
var full = ascii(0xdb);
var top = ascii(0xdf);
var bot = ascii(0xdc);
var blank = ' ';
var qrcode = '';
/*
// Render the top line
var line = ascii(27)+'[1;37m'+bot;
for (var y = 0; y < qr.size; y++) {
line += bot;
}
qrcode += line+bot+bot+ascii(27)+'[0m'+"\r\n";
*/
// Render the body
for (var x = -1; x < qr.size; x=x+2) {
line = ascii(27)+'[1;37m'+full;
for (var y = 0; y < qr.size; y++) {
// Top is white
if (! ((x===-1)? 0 : qr.getModule(x, y))) {
line += (qr.getModule(x+1, y)) ? top : full;
// Top is black
} else {
line += (qr.getModule(x+1, y)) ? blank : bot;
}
}
qrcode += line+full+ascii(27)+'[0m'+"\r\n";
}
// Render the bottom
line = ascii(27)+'[1;37m'+top;
for (var y = 0; y < qr.size; y++) {
line += top;
}
qrcode += line+top+ascii(27)+'[0m'+"\r\n";
ans2bin(fo.parse(qrcode),subframe);
subframe.open();
subframe.cycle();
};
this.save=function() {
file = system.mods_dir+'ansitex/text/'+this.page+'.tex';
w = new File(file);
if (! w.open('w')) {
log(LOG_ERROR,'! ERROR: Unable to create TEX file for '+this.page);
exit(1);
}
w.write(JSON.stringify(this));
w.close();
log(LOG_DEBUG,'Saved file: '+this.page+'.tex');
}
/**
* Send a message to the baseline.
*
* @param text
* @param reposition
*/
this.sendBaseline=function(text,reposition) {
console.pushxy();
console.gotoxy(0,24);
console.print(this.getMessage(text));
console.cleartoeol();
if (! reposition) {
console.popxy();
}
}
this.clearBaseline=function(reposition) {
console.pushxy();
console.gotoxy(0,24);
console.print('');
console.cleartoeol();
if (! reposition) {
console.popxy();
}
}
}
FrameAnsi.prototype = PageFrame.prototype;
FrameAnsi.prototype.constructor = FrameAnsi;

View File

@ -1,603 +0,0 @@
// Our frame object
function PageFrame() {
'use strict';
/* Frame type settings */
this.settings = {};
/* Frame Version */
this.version = 1;
/* Frame Number [0-9] */
this.frame = null;
/* Frame Index [a-z] */
this.index = null;
/* The Service Provider owning the frame. */
this.owner = 0;
/* The cost to view the frame @todo to implement */
this.cost = 0;
/* The frame content, base64 encoded */
this.content = '';
// Frame's owned by the system where:
// isPublic is FALSE - the user must be logged in to view it
// isPublic is TRUE - can be viewed by non-logged in users
// Frame's owned by Service Providers where:
// isPublic is FALSE - can only be viewed if a user is
// a member of the Service Providers CUG
// isPublic is TRUE - can be viewed by users (if logged in)
this.isPublic = false; // Is this frame accessible to non CUG users
// If FALSE user must be a member of the CUG to view the frame
// All users, including unauthenticated, are members of 'system' (owner = 0)
this.isAccessible = false; // Is this frame available to be accessed
// If FALSE, only the SP can view/edit the frame
this.type = FRAME_TYPE_INFO; // The frame type - see FRAME_TYPES above
this.key = [null,null,null,null,null,null,null,null,null,null]; // Key actions [0-9]
/**
* Determine if this frame is accessible to the current user
*/
Object.defineProperty(this,'accessible',{
get: function() {
log(LOG_DEBUG,'- Checking if user can access frame: '+this.page);
log(LOG_DEBUG,' - User: '+JSON.stringify(user.number));
log(LOG_DEBUG,' - Frame Owner: '+JSON.stringify(this.owner)+', System Frame: '+(this.pageowner === SYSTEM_OWNER));
// user.number 0 is unidentified user.
if (user.number) {
return (
(this.isAccessible && this.pageowner === SYSTEM_OWNER && ! this.isPublic) ||
(this.isAccessible && this.isPublic) ||
(this.isAccessible && ! this.isPublic && this.isMember) ||
(pageEditor(this.frame))
);
} else {
return (this.isAccessible && this.pageowner === SYSTEM_OWNER && this.isPublic);
}
}
});
/**
* Return the frames fields
*/
Object.defineProperty(this,'fields', {
get: function() {
return this.frame_fields;
}
});
/**
* Check if the user is already a member of the CUG
*/
Object.defineProperty(this,'isMember',{
get: function() {
log(LOG_DEBUG,'- Checking if user is a member of frame: '+this.page);
if (user.number) {
return (
(this.pageowner === SYSTEM_OWNER)
);
} else {
return false;
}
}
})
/**
* Return the page number
*/
Object.defineProperty(this,'page', {
get: function() {
if (this.frame === null || this.index === null) return null;
return this.frame.toString()+this.index;
}
});
Object.defineProperty(this,'pageowner', {
get: function() {
return pageOwner(this.frame).prefix;
}
})
Object.defineProperty(this,'pageownerlogo', {
get: function() {
return base64_decode(this.settings.ext === 'tex' ? pageOwner(this.frame).logoans : pageOwner(this.frame).logovtx);
}
})
}
/**
* Enable pulling out submitted value by its name
*
* @param key
* @returns {null|string|*}
*/
PageFrame.prototype.fieldValue = function(key) {
for each (var k in this.frame_fields) {
log(LOG_DEBUG,' - k:'+JSON.stringify(k));
if (k.fname === key) {
return k.fvalue;
}
}
return null;
}
/**
* Return the message for a index
*
* @param index
* @returns {string|*}
*/
PageFrame.prototype.getMessage = function(index) {
eval('var msg = this.settings.'+index);
return msg;
}
/**
* Load a frame
*
* @param filename
* @returns {null}
*/
PageFrame.prototype.load = function(filename) {
log(LOG_DEBUG,'Loading FRAME from: '+filename+'.'+this.settings.ext);
this.sendBaseline('LOADING');
f = new File(system.mods_dir+'ansitex/text/'+filename+'.'+this.settings.ext);
if (! f.exists || ! f.open('r')) {
return null;
}
try {
var load = JSON.parse(f.read());
for (property in SAVED_FRAME_ATTRS) {
this[SAVED_FRAME_ATTRS[property]] = load[SAVED_FRAME_ATTRS[property]];
}
// If the page doesnt match the filename, throw an error
if (this.page !== filename)
throw 'Frame doesnt match filename';
} catch (error) {
log(LOG_ERROR,'Frame error: '+error);
// Load our system error frame.
this.load(pageStr(FRAME_SYSTEM_ERROR));
return null;
}
log(LOG_DEBUG,'Loaded frame: ['+this.frame+']['+this.index+'] ('+this.page+')');
};
/**
* Load a message frame
*
* @param page
*/
PageFrame.prototype.loadMessage = function(page) {
this.frame = ''+page;
this.index = 'a';
this.owner = 1;
this.isPublic = true;
this.isAccessible = true;
// @todo Keys should map to next/previous/send, etc as indicated in the template frame.
this.key = [this.frame.substr(0,7)+'1',null,null,null,null,null,null,null,null,null];
// @todo validate that FRAME_TYPE_MESSAGE is a message template
this.type = FRAME_TYPE_MESSAGE;
// Load our message
var ma = new MsgAreas()
var area = ma.getArea(this.frame);
var msg = ma.getMessage(this.frame);
var msg_header;
if (! msg)
return undefined;
// @todo Search 1zzzzEE..., 1zzzz...
var to = viewdata ? new FrameViewdata() : new FrameAnsi();
to.load(MAIL_TEMPLATE_FRAME);
// @todo Check that this is a frame of type "m" and report error if not
// @todo Take the cost from the template
this.cost = 5;
if (! to) {
log(LOG_ERROR,'Echomail template missing :['+MAIL_TEMPLATE_FRAME+'] ?');
msg_header = 'TO: '+msg.to.substr(0,72)+"\n\r";
msg_header += 'FROM: '+msg.from.substr(0,72)+"\n\r";
msg_header += 'DATE: '+msg.date.substr(0,72)+"\n\r";
msg_header += 'SUBJECT: '+msg.subject.substr(0,72)+"\n\r";
} else {
// @todo change this to use atcode()
msg_header = base64_decode(to.content).replace(/@(.*)@/g,
function (str, code, offset, s) {
var length = code.split(':')[1];
switch(code.split(':')[0]) {
case 'DATE': return msg.date.substr(0,length);
case 'TO': return msg.to.substr(0,length);
case 'FROM': return msg.from.substr(0,length);
case 'SUBJECT': return msg.subject.substr(0,length);
}
}
);
}
//log(LOG_DEBUG,'Loaded message: '+msg_header+msg.content);
this.content = base64_encode(msg_header+msg.content);
// Update the user's pointers
var stats = ma.getUserStats(this.frame);
// if this message is to the user, and the msg number > scan_ptr and it is the next message on the user's new mail list
var newmsgs = area.newMsgsToMe();
var next;
log(LOG_DEBUG,'User has: '+newmsgs.length-1+' msgs to read to ME');
if (newmsgs.length) {
next = newmsgs[1];
//log(LOG_DEBUG,'- NEXT is: '+next.tags+', this is: '+msg.tags);
if (next && (next.tags === msg.tags)) {
log(LOG_DEBUG,'- Updating scan_ptr to: '+next.number);
stats.scan_ptr = next.number;
}
// Last message
next = newmsgs[0];
if (next !== undefined) {
log(LOG_DEBUG,'- LAST TO ME is: '+next.tags);
this.key[1] = area.page(next.tags);
}
// Next new message
next = newmsgs[2];
if (next !== undefined) {
log(LOG_DEBUG,'- NEXT TO ME is: '+next.tags);
this.key[2] = area.page(next.tags);
}
}
// if this message is the next message, update last_read
newmsgs = area.newMsgs();
log(LOG_DEBUG,'User has: '+newmsgs.length+' msgs to read');
if (newmsgs.length) {
next = newmsgs[0];
log(LOG_DEBUG,'- NEXT is: '+next.tags+', this is: '+msg.tags);
if (next.tags === msg.tags) {
log(LOG_DEBUG,'- Updating last_read to: '+next.number);
stats.last_read = next.number;
}
}
// Previous Message
x = area.MessagePrev(this.frame);
if (x)
this.key[4] = area.page(x.tags);
// Next Message
x = area.MessageNext(this.frame);
if (x)
this.key[6] = area.page(x.tags);
log(LOG_DEBUG,'Built frame: ['+this.frame+']['+this.index+'] ('+this.page+')');
}
/**
* Parse the page text, and return the frame as 2 arrays:
* + First array is all the characters and the position on the frame
* + Second array is the array of the control codes that changes the color of the character
*
* The purpose of this function is to convert any special char sequences there are interpreted directly by Ansitex
* Currently they are:
* + ESC _ <value>[;value] ESC \
*
* Additionally, for response frames, if the cursor is moved to a field, its to determine what attributes (eg: color)
* should apply for that field.
*
* @param text
* @param context
*/
PageFrame.prototype.parse = function(text,context) {
log(LOG_DEBUG,'Parsing Frame ...');
if (this.type === FRAME_TYPE_MESSAGE) {
log(LOG_DEBUG,'- Not parsing message frame');
return text;
}
/** Column
* @type {number}
*/
var c = 1;
/** Row - row 1 is the header
* @type {number}
*/
var r = 2;
/**
* The output to send to the screen
* @type {string}
*/
var output = '';
/**
* Fields within the frame
* @type {{
* ftype: fieldtype,
* flength: fieldlen,
* fchar: fieldchar,
* fname: field,
* r: r,
* c: c,
* attribute: {i:i,f:f,b:b},
* fvalue: '',
* }}
*/
this.frame_fields = [];
// If there is no text return
if (! text)
return output;
// Default Attributes
// @note - this has no affect for viewdata frames
/* Foreground */
var f = 39;
/* Background */
var b = 49;
/* Intensity */
var i = 0;
for(var p=0;p<text.length;p++) {
// Look for a special character until the end of the frame width
cte = (r*this.settings.FRAME_WIDTH - ((r-1)*this.settings.FRAME_WIDTH+c));
match = text.substr(p,cte).match(/[\r\n\x1b]/);
//log(LOG_DEBUG,'SPECIAL CHAR ['+r+'x'+c+'] ['+p+'-'+cte+'] for: '+match);
if (match === null || match.index) {
advance = match ? match.index : cte;
//log(LOG_DEBUG,'INCLUDE: '+text.substr(p,advance));
output += text.substr(p,advance);
p += advance;
c += advance;
advance = 0;
}
// If the frame is not big enough, fill it with spaces.
byte = text.charAt(p);
advance = 0;
switch (byte) {
// Carriage Return
case "\r":
// log(LOG_DEBUG,'CR');
c=1;
break;
// New line
case "\n":
// log(LOG_DEBUG,'LF');
r++;
break;
// ESC
case KEY_ESC:
advance = 1;
// Is the next byte something we know about
nextbyte = text.charAt(p+advance);
//log(LOG_DEBUG,'ESC ['+r+'x'+c+'] NEXT: '+nextbyte);
switch (nextbyte) {
// CSI
case '[':
advance++;
chars = '';
// Find our end CSI param in the next 50 chars
matches = text.substr(p+advance,50).match(/([0-9]+[;]?)+([a-zA-Z])/);
//log(LOG_DEBUG,'CSI ['+r+'x'+c+'] ADVANCE: '+advance+', MATCHES: '+matches+', STRING: '+text.substr(p+advance,50));
if (! matches) {
chars += nextbyte;
break;
}
advance += matches[0].length-1;
chars += nextbyte+matches[0];
//log(LOG_DEBUG,'CSI ['+r+'x'+c+'] ADVANCE: '+advance+', CHARS: '+chars+', CHARSLEN: '+chars.length);
switch (matches[2]) {
// Color CSIs
case 'm':
//log(LOG_DEBUG,'CSI m ['+r+'x'+c+'] MATCHES: '+matches[0]+', LENGTH : '+matches[0].length);
csi = matches[0].substr(0,matches[0].length-1).split(';');
var num = null;
for (num in csi) {
//log(LOG_DEBUG,'CSI m ['+r+'x'+c+'] NUM: '+num+', CSI: '+csi[num]);
// Intensity
if (csi[num] >= 0 && csi[num] <= 8) {
i = csi[num];
f = 39;
b = 49;
// Forground Color
} else if (csi[num] >= 30 && csi[num] <= 39) {
f = csi[num];
// Background Color
} else if (csi[num] >= 40 && csi[num] <= 49) {
b = num;
}
}
break;
// Advance characters
case 'C':
//log(LOG_DEBUG,'CSI C ['+r+'x'+c+'] CHARS: '+matches[1]);
c += parseInt(matches[1]); // Advance our position
break;
default:
log(LOG_DEBUG,'? CSI: '+matches[2]);
}
break;
case ' ':
log(LOG_DEBUG,'LOOSE ESC? ['+r+'x'+c+'] '+advance);
break;
// X - dynamic fields, terminated with ESC
// Dynamic fields are defined with ESC X FIELD;LENGTH;PAD ESC \
// @note FIELD & LENGTH are required
// @note Dynamic field is can only be DYNAMIC_FIELD_SIZE_MAX chars in size
case 'X':
//log(LOG_DEBUG,'DF ['+r+'x'+c+'] '+advance);
advance++;
// Find our end ST param in the next DYNAMIC_FIELD_SIZE_MAX chars
matches = text.substr(p+advance,DYNAMIC_FIELD_SIZE_MAX).match(/(([a-zA-Z._:^;]+[0-9]?;-?[0-9^;]+)([;]?[^;]+)?)\x1b\\/);
//log(LOG_DEBUG,'- DF ['+r+'x'+c+'] ADVANCE: '+advance+', MATCHES: '+JSON.stringify(matches)+', LENGTH: '+(matches ? matches[0].length : 0)+', STRING: '+text.substr(p+advance,50));
if (! matches) {
chars += nextbyte;
break;
}
advance += matches[0].length-1;
var df = matches[0].substr(0,matches[0].length-2).split(';');
//log(LOG_DEBUG,'- DF ['+r+'x'+c+'] ADVANCE: '+advance+', DF: '+df);
log(LOG_DEBUG,'- DF found at ['+r+'x'+(c-1)+'], Field: '+df[0]+', Length: '+df[1]+', Attrs: '+JSON.stringify({i:i,f:f,b:b}));
chars = atcode(df[0],df[1],df[2],context);
byte = '';
break;
// _ - has our fields that take input
case '_':
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] '+advance);
advance++;
// Find our end ST param in the next 50 chars
matches = text.substr(p+advance,50).match(/(([A-Z]+;[0-9a-z]+)([;]?.+)?)\x1b\\/);
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] ADVANCE: '+advance+', MATCHES: '+matches+', LENGTH: '+matches[0].length+', STRING: '+text.substr(p+advance,50));
if (! matches) {
chars += nextbyte;
break;
}
advance += matches[0].length-1;
// The last 2 chars of matches[0] are the ESC \
var sos = matches[0].substr(0,matches[0].length-2).split(';');
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] ADVANCE: '+advance+', SOS: '+sos);
var num = null;
var field = null;
var fieldlen = null;
for (num in sos) {
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] NUM: '+num+', SOS: '+sos[num]);
switch (num) {
// First value is the field name
case '0':
field = sos[num];
break;
// Second value is the length/type of the field
case '1':
x = sos[num].match(/([0-9]+)([a-z])/);
if (! x) {
log(LOG_ERROR,'SOS FAILED PARSING FIELD LENGTH/TYPE. ['+r+'x'+c+'] '+sos[num]);
break;
}
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] NUM CHARS: '+x[1]+', TYPE: '+x[2]);
fieldlen = x[1];
fieldtype = x[2];
break;
// Third field is the char to to use
case '2':
fieldchar = sos[num];
break;
default:
log(LOG_ERROR,'IGNORING ADDITIONAL SOS FIELDS. ['+r+'x'+c+'] '+sos[num]);
}
}
if (fieldlen) {
chars = fieldchar.repeat(fieldlen);
}
byte = '';
this.frame_fields.push({
ftype: fieldtype,
flength: fieldlen,
fchar: fieldchar,
fname: field,
r: r,
c: c,
attribute: {i:i,f:f,b:b},
fvalue: '',
});
log(LOG_DEBUG,'SOS Field found at ['+r+'x'+(c-1)+'], Type: '+fieldtype+', Length: '+fieldlen+', Attrs: '+JSON.stringify({i:i,f:f,b:b}));
break;
default:
log(LOG_DEBUG,'DEFAULT ['+r+'x'+c+'] '+advance);
}
break;
default:
c++;
}
output += byte;
if (advance) {
//log(LOG_DEBUG,'ADVANCE P ['+r+'x'+c+'] '+advance+', NEXT CHAR: '+text.charAt(p+advance)+' ('+text.charCodeAt(p+advance)+')');
output += chars;
p += advance;
}
if (c>this.settings.FRAME_WIDTH) {
c = 1;
r++;
}
/*
// @todo - If we are longer than FRAME_LENGTH, move the output into the next frame.
if (r>this.settings.FRAME_LENGTH) {
break;
}
*/
}
return output;
};

View File

@ -1,257 +0,0 @@
var FRAME_VIEWDATA = (1<<2);
var VIEWDATA_LEFT = ascii(0x08);
var VIEWDATA_RIGHT = ascii(0x09);
var VIEWDATA_DOWN = ascii(0x0a); // \n
var VIEWDATA_UP = ascii(0x0b);
var VIEWDATA_CLS = ascii(0x0c);
var VIEWDATA_CR = ascii(0x0d); // \r
var VIEWDATA_CON = ascii(0x11);
var VIEWDATA_COFF = ascii(0x14);
var VIEWDATA_HOME = ascii(0x1e);
var VIEWDATA_MOSIAC_RED = ascii(27)+ascii(0x51);
var VIEWDATA_MOSIAC_GREEN = ascii(27)+ascii(0x52);
var VIEWDATA_MOSIAC_YELLOW = ascii(27)+ascii(0x53);
var VIEWDATA_MOSIAC_BLUE = ascii(27)+ascii(0x54);
var VIEWDATA_MOSIAC_MAGENTA = ascii(27)+ascii(0x55);
var VIEWDATA_MOSIAC_CYAN = ascii(27)+ascii(0x56);
var VIEWDATA_MOSIAC_WHITE = ascii(27)+ascii(0x57);
load('ansitex/load/frame-page.js');
// Our frame object
function FrameViewdata() {
PageFrame.apply(this,arguments);
/* File Extension used for frames */
this.settings.ext = 'vtx';
/* Length of a frame */
this.settings.FRAME_LENGTH = 22;
/* Width of a frame */
this.settings.FRAME_WIDTH = 40;
/* Size of page owner (length) */
this.settings.FRAME_HEADER = 23;
/* Size of page number (length with a-z) */
this.settings.FRAME_PAGENUM = 11;
/* Size of cost (length without unit) */
this.settings.FRAME_COST = 3;
this.settings.MSG_SENDORNOT = ascii(27)+'BKEY 1 TO SEND, 2 NOT TO SEND';
this.settings.MSG_LOGON = ascii(27)+'BKEY 1 TO LOGON, 2 TO RETURN';
this.settings.MSG_SENT = ascii(27)+'BMESSAGE SENT - KEY _ TO CONTINUE';
this.settings.MSG_NOTSENT = ascii(27)+'BMESSAGE NOT SENT - KEY _ TO CONTINUE';
this.settings.ERR_NO_PARENT = ascii(27)+'APARENT FRAME DOESNT EXIST';
this.settings.ERR_NOT_IMPLEMENTED = ascii(27)+'ANOT IMPLEMENTED YET?';
this.settings.ERR_ROUTE = ascii(27)+'GMISTAKE?'+ascii(27)+'BTRY AGAIN OR TELL US ON *08';
this.settings.ERR_METHOD_NOT_EXIST = ascii(27)+'GMISTAKE?'+ascii(27)+'BTRY AGAIN OR TELL US ON *08';
this.settings.ACCESS_DENIED = ascii(27)+'AACCESS DENIED.';
this.settings.ALREADY_MEMBER = ascii(27)+'AALREADY MEMBER OF CUG'
this.settings.INACTIVITY = ascii(27)+'AINACTIVITY ALERT, DISCONNECT PENDING...';
this.settings.INACTIVE = ascii(27)+'AINACTIVITY DISCONNECT';
this.settings.NOACTION = ascii(27)+'ANO ACTION PERFORMED';
this.settings.BASESTAR = ascii(27)+'B*';
this.settings.INVALID_CODE = ascii(27)+'AINVAID CODE, PLEASE TRY AGAIN *00';
this.settings.TOKEN_EMAIL = ascii(27)+'ATOKEN EMAILED TO YOU...';
this.settings.TOKEN_SENT = ascii(27)+'ATOKEN SENT, PLEASE ENTER TOKEN';
this.settings.INVALID_EMAIL = ascii(27)+'AINVAID EMAIL, PLEASE TRY AGAIN *00';
this.settings.INVALID_UID = ascii(27)+'AINVAID USER ID, PLEASE TRY AGAIN *00';
this.settings.CANNOT_SEND_TOKEN = ascii(27)+'ACANNOT SEND VALIDATION CODE, PLEASE TRY AGAIN *00';
this.settings.USER_EXISTS = ascii(27)+'AERROR USER EXISTS, PLEASE TRY AGAIN *00';
this.settings.USER_CREATE_ERROR = ascii(27)+'AERROR CREATING USER, PLEASE TRY AGAIN *00';
this.settings.LOGIN_ERROR = ascii(27)+'AERROR LOGGING IN, PLEASE TRY AGAIN *00';
this.settings.CANCEL_MSG = ascii(27)+'BPRESS 2 TO CANCEL';
this.settings.SYS_ERROR = ascii(27)+'ASYS ERR, TRY AGAIN OR TELL US ON *08';
this.settings.LOADING = VIEWDATA_MOSIAC_WHITE+'loading...';
var blp=0; // Length of data on the bottom line
/**
* Set the attribute at the current position
*/
this.attr=function(field) {
//NOOP
}
/**
* Turn off the cursor
*/
this.cursorOff=function() {
write_raw(VIEWDATA_COFF);
this.gotoxy(1,1);
}
this.cursorOn=function(x,y) {
write_raw(VIEWDATA_CON);
this.gotoxy(x,y);
}
// Field backspace, that leaves the field filler char
this.fieldbs=function(char) {
console.write(VIEWDATA_LEFT+char+VIEWDATA_LEFT);
}
this.gotoxy=function(x,y) {
// @todo This could be optimised to go the shortest route
write_raw(VIEWDATA_HOME);
if (x>0)
write_raw(VIEWDATA_RIGHT.repeat(x));
if (y>0)
write_raw(VIEWDATA_DOWN.repeat(y));
}
this.strlen=function(str) {
return str.replace(/\x1b/g,'').length;
};
// Render the frame to the user
this.render=function(withHeader) {
log(LOG_DEBUG,'- VIEWDATA FRAME');
owner = base64_decode(this.owner);
header = VIEWDATA_DOWN;
//log(LOG_DEBUG,' - FRAME User: ['+JSON.stringify(user)+']');
// Dont show the page number on system login page
if (user.number || (this.type !== FRAME_TYPE_LOGIN && NO_HISTORY_FRAMES.indexOf(this.page) === -1)) {
log(LOG_DEBUG,' - Owner: ['+this.pageowner+'] ('+this.strlen(videotex(this.pageownerlogo))+')');
cost = (this.isAccessible ? this.cost+FRAME_COSTUNIT : ' -');
header = videotex(this.pageownerlogo)+' '.repeat(this.settings.FRAME_HEADER-this.strlen(videotex(this.pageownerlogo)))+
(this.isAccessible ? ascii(27)+'G' : ascii(27)+'A')+this.page+' '.repeat(this.settings.FRAME_PAGENUM-this.page.length)+
ascii(27)+'B'+' '.repeat(this.settings.FRAME_COST-cost.toString().length+1)+cost;
}
//console.status |= CON_RAW_IN;
write_raw(VIEWDATA_CLS);
write_raw(header);
return write_raw(videotex(base64_decode(this.content)));
};
this.qrcode = function(qr) {
// Render the body
var qrcode = VIEWDATA_HOME+VIEWDATA_DOWN.repeat(5);
var offset = this.settings.FRAME_WIDTH-Math.ceil(qr.size/2)-1;
for (var x = -1; x < qr.size; x=x+3) {
var line = VIEWDATA_RIGHT.repeat(offset ? offset-1 : 0)+VIEWDATA_MOSIAC_WHITE;
for (var y = -1; y < qr.size; y=y+2) {
var char = 0;
//TL
char |= ((x===-1) || (y===-1) || ! qr.getModule(x,y)) ? (1<<0) : (0<<0);
//TR
char |= ((x===-1) || (y === qr.size-1) || ! qr.getModule(x,y+1)) ? (1<<1) : (0<<1);
//ML
char |= ((y===-1) || ! qr.getModule(x+1,y)) ? (1<<2) : (0<<2);
//MR
char |= ((y === qr.size-1) || ! qr.getModule(x+1,y+1)) ? (1<<3) : (0<<3);
//BL
char |= ((x===qr.size-2) || (y===-1) || ! qr.getModule(x+2,y)) ? (1<<4) : (0<<4);
//BR
char |= ((x===qr.size-2) || (y === qr.size-1) || ! qr.getModule(x+2,y+1)) ? (1<<5) : (0<<5);
char += 0x20;
if (char > 0x3f)
char += 0x20;
line += ascii(char);
}
// Render the right column
if (y%2)
line += ascii(0x35);
repeat_count = this.settings.FRAME_WIDTH-Math.ceil(qr.size/2)-offset-(offset ? 1 : 2)-(y%2 === 1 ? 0 : 1);
qrcode += line+' '.repeat(repeat_count > 0 ? repeat_count : 0);
// To fix some terminals where moving right from col 40 doesnt advance to col 1 on the next line
qrcode +=VIEWDATA_LEFT+VIEWDATA_CR+VIEWDATA_DOWN;
}
log(LOG_DEBUG,'WIDTH:'+this.settings.FRAME_WIDTH);
log(LOG_DEBUG,'QR :'+(Math.ceil(qr.size/2)+1));
log(LOG_DEBUG,'OFF :'+offset);
log(LOG_DEBUG,'Y :'+(y%2 ? 0 : 1));
log(LOG_DEBUG,'X :'+(x%3 ? 0 : 1));
// Render the bottom
if (x%3) {
line = VIEWDATA_RIGHT.repeat(offset ? offset-1 : 0)+VIEWDATA_MOSIAC_WHITE;
for (var y = 0; y < qr.size; y=y+2) {
line += ascii(0x23);
}
// Render the right column
if (y%2 === 0) {
line += ascii(0x21);
}
qrcode += line+' '.repeat(repeat_count > 0 ? repeat_count : 0);
}
write_raw(qrcode);
};
this.save=function() {
file = system.mods_dir+'ansitex/text/'+this.page+'.tex';
w = new File(file);
if (! w.open('w')) {
log(LOG_ERROR,'! ERROR: Unable to create TEX file for '+this.page);
exit(1);
}
w.write(JSON.stringify(this));
w.close();
log(LOG_DEBUG,'Saved file: '+this.page+'.tex');
}
/**
* Send a message to the baseline.
*
* @param text
* @param reposition
*/
this.sendBaseline=function(text,reposition) {
var msg = this.getMessage(text);
var x = this.strlen(msg);
write_raw(VIEWDATA_HOME+VIEWDATA_UP+msg+
((blp > x)
? (' '.repeat(blp-x)+(reposition ? VIEWDATA_HOME+VIEWDATA_UP+VIEWDATA_RIGHT.repeat(x) : ''))
: '')
);
blp = x;
}
this.clearBaseline=function(reposition) {
msg = '';
write_raw(VIEWDATA_HOME+VIEWDATA_UP+msg+
((blp > msg.length)
? (' '.repeat(blp-msg.length)+(reposition ? VIEWDATA_HOME+VIEWDATA_UP+VIEWDATA_RIGHT.repeat(msg.length) : ''))
: '')
);
blp = msg.length;
}
}
function videotex(data) {
var output = '';
//$output .= ($byte < 32) ? ESC.chr($byte+64) : chr($byte);
for (var i = 0; i < data.length; i++) {
output += (data.charCodeAt(i) < 32) ? "\x1b"+String.fromCharCode(data.charCodeAt(i)+64) : String.fromCharCode(data.charCodeAt(i));
}
return output;
}
FrameViewdata.prototype = PageFrame.prototype;
FrameViewdata.prototype.constructor = FrameViewdata;

View File

@ -104,8 +104,16 @@ function ans2bin(ansi,frame) {
} }
} }
// Dynamic Field Processing /**
// @note bbs.atcodes() cannot process modifiers, so this function is a replacement. * Dynamic Field Processing
*
* @param field field we want to get a value for
* @param length length to fill this field
* @param pad the padding character if field is less than length
* @param context a context value to determine the value from
* @returns {string|*|null}
* @note bbs.atcodes() cannot process modifiers, so this function is a replacement.
*/
function atcode(field,length,pad,context) { function atcode(field,length,pad,context) {
'use strict'; 'use strict';
@ -299,7 +307,7 @@ function atcode(field,length,pad,context) {
break; break;
default: default:
result = bbs.atcode(field); result = (typeof bbs === 'undefined') ? '*'.repeat(Math.abs(length)) : bbs.atcode(field);
} }
if ((result === null) || (typeof result === 'undefined')) if ((result === null) || (typeof result === 'undefined'))
@ -379,7 +387,7 @@ function getNodeID() {
} }
} }
return (matches.length === 0) ? '-' : padright(matches[0],3,'0')+padright(matches[1],3,'0')+padright(bbs.node_num,2,'0'); return (matches.length === 0) ? '-' : padright(matches[0],3,'0')+padright(matches[1],3,'0')+padright((typeof bbs === 'undefined') ? 0 : bbs.node_num,2,'0');
} }
function getPageOwners() { function getPageOwners() {

1098
load/page.js Normal file

File diff suppressed because it is too large Load Diff

666
load/session-ansitex.js Normal file
View File

@ -0,0 +1,666 @@
var SESSION_ANSITEX = (1<<1);
var SESSION_EXT = 'tex';
var ANSI_FRAME_WIDTH = 80;
var ANSI_FRAME_HEIGHT = 22;
var ANSI_FRAME_PROVIDER_LENGTH = 55;
var ANSI_FRAME_PAGE_LENGTH = 13;
var ANSI_FRAME_COST_LENGTH = 10;
/**
* This function converts ANSI text into an array of attributes
*
* We include the attribute for every character, so that if a window is placed on top of this window, the edges
* render correctly.
*
* @param contents - Our ANSI content to convert
* @param width - The width before wrapping to the next line
* @param yoffset - fields offset as discovered
* @param xoffset - fields offset as discovered
* @param debug - Enable debug mode
*/
function anstoattrs(contents,width,yoffset,xoffset,debug) {
if (debug)
writeln('DEBUG active: '+debug);
lines = (''+contents).split(/\r\n/);
var i = 0;
var bg = BG_BLACK;
var fg = LIGHTGRAY;
var attr = fg + bg + i;
var y = 0;
// Saving cursor positions
var saved = {};
var frame = {
content: [],
dynamic_fields: [],
input_fields: [],
};
// @todo temp hack, rework ansi variable - perhaps have a function that converts an attribute back into an ANSI sequence
var ansi = { i: 0, f: 37, b: 40 };
while (lines.length > 0) {
var x = 0;
var line = lines.shift();
if ((debug !== undefined) && (y > debug)) {
exit(1);
}
if (debug) {
log(LOG_DEBUG,'y:'+y);
log(LOG_DEBUG,'line:'+line);
}
while (line.length > 0) {
/* parse an attribute sequence*/
var m = line.match(/^\x1b\[((\d+)(;?(\d+))*)+m/);
if (m !== null) {
line = line.substr(m.shift().length);
m = m.shift().split(';').sort(function(a,b) { return Number(a) < Number(b) ? -1 : 1; });
if ((debug !== undefined) && debug === y) {
//writeln();
log(LOG_DEBUG,'m:'+JSON.stringify(m));
}
// Reset
if (Number(m[0]) === 0) {
bg = BG_BLACK;
fg = LIGHTGRAY;
i = 0;
ansi = { i:0, b:37, f:40};
m.shift();
if (debug)
log(LOG_DEBUG,' - RESET');
}
// High Intensity
if (Number(m[0]) === 1) {
i += (((i === 0) || (i === BLINK)) ? HIGH : 0);
m.shift();
ansi.i = 1;
if (debug && (debug === y))
writeln('fg:'+fg+', bg:'+bg+' i:'+i);
}
// Blink
if (Number(m[0]) === 5) {
i += (((i === 0) || (i === HIGH)) ? BLINK : 0);
m.shift();
}
// Foreground
if ((Number(m[0]) >= 30) && (Number(m[0]) <= 37)) {
ansi.f = Number(m[0]);
switch(Number(m.shift())) {
case 30:
fg = BLACK;
break;
case 31:
fg = RED;
break;
case 32:
fg = GREEN;
break;
case 33:
fg = BROWN;
break;
case 34:
fg = BLUE;
break;
case 35:
fg = MAGENTA;
break;
case 36:
fg = CYAN;
break;
case 37:
fg = LIGHTGRAY;
break;
}
}
// Background
if ((Number(m[0]) >= 40) && (Number(m[0]) <= 47)) {
ansi.b = Number(m[0]);
switch (Number(m.shift())) {
case 40:
bg = BG_BLACK;
break;
case 41:
bg = BG_RED;
break;
case 42:
bg = BG_GREEN;
break;
case 43:
bg = BG_BROWN;
break;
case 44:
bg = BG_BLUE;
break;
case 45:
bg = BG_MAGENTA;
break;
case 46:
bg = BG_CYAN;
break;
case 47:
bg = BG_LIGHTGRAY;
break;
}
}
if (debug) {
log(LOG_DEBUG,'fg:'+fg+', bg:'+bg+' i:'+i);
log(LOG_DEBUG,'ansi:'+JSON.stringify(ansi));
}
attr = bg + fg + i;
continue;
}
if (debug)
log(LOG_DEBUG,'= Current attr:'+attr);
/* parse absolute character position */
var m = line.match(/^\x1b\[(\d*);?(\d*)[Hf]/);
if (m !== null) {
line = line.substr(m.shift().length);
if (m.length === 0) {
x = 0;
y = 0;
} else {
if(m[0])
y = Number(m.shift())-1;
if(m[0])
x = Number(m.shift())-1;
}
continue;
}
/* ignore a bullshit sequence */
var n = line.match(/^\x1b\[\?7h/);
if (n !== null) {
line = line.substr(n.shift().length);
continue;
}
/* parse an up positional sequence */
var n = line.match(/^\x1b\[(\d*)A/);
if (n !== null) {
line = line.substr(n.shift().length);
var chars = n.shift();
y -= (chars < 1) ? 0 : Number(chars);
continue;
}
/* parse a down positional sequence */
var n = line.match(/^\x1b\[(\d*)B/);
if (n !== null) {
line = line.substr(n.shift().length);
var chars = n.shift();
y += (chars < 1) ? 0 : Number(chars);
continue;
}
/* parse a forward positional sequence */
var n = line.match(/^\x1b\[(\d*)C/);
if (n !== null) {
line = line.substr(n.shift().length);
var chars = n.shift();
x += (chars < 1) ? 0 : Number(chars);
continue;
}
/* parse a backward positional sequence */
var n = line.match(/^\x1b\[(\d*)D/);
if (n !== null) {
line = line.substr(n.shift().length);
var chars = n.shift()
x -= (chars < 1) ? 0 : Number(chars);
continue;
}
/* parse a clear screen sequence */
var n = line.match(/^\x1b\[2J/);
if (n !== null) {
line = line.substr(n.shift().length);
continue;
}
/* parse save cursor sequence */
var n = line.match(/^\x1b\[s/);
if (n !== null) {
line = line.substr(n.shift().length);
saved.x = x;
saved.y = y;
continue;
}
/* parse restore cursor sequence */
var n = line.match(/^\x1b\[u/);
if (n !== null) {
line = line.substr(n.shift().length);
x = saved.x;
y = saved.y;
continue;
}
/* parse an input field */
// Input field 'FIELD;valueTYPE;input char'
// @todo remove the trailing ESC \ to end the field, just use a control code ^B \x02 (Start of Text) and ^C \x03
var m = line.match(/^\x1b_(([A-Z]+;[0-9a-z]+)([;]?.+)?)\x1b\\/);
if (m !== null) {
log(LOG_DEBUG,'Got input field: '+JSON.stringify(m));
log(LOG_DEBUG,'ansi:'+JSON.stringify(ansi));
// full string that matched
match = m.shift();
// thus, the rest of the line
line = line.substr(match.length);
//writeln('rest of line:'+JSON.stringify(line));
// We are interested in our field match
var sos = m.shift().split(';');
//writeln('sos:'+JSON.stringify(sos));
for (var num in sos) {
switch (num) {
// First value is the field name
case '0':
field = sos[num];
break;
// Second value is the length/type of the field, nnX nn=size in chars, T=type (lower case)
case '1':
var c = sos[num].match(/([0-9]+)([a-z])/);
if (! c) {
log(LOG_ERROR,'SOS FAILED PARSING FIELD LENGTH/TYPE. ['+r+'x'+c+'] '+sos[num]);
break;
}
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] NUM CHARS: '+x[1]+', TYPE: '+x[2]);
fieldlen = c[1];
fieldtype = c[2];
break;
// Third field is the char to to use
case '2':
fieldchar = sos[num];
break;
default:
log(LOG_ERROR,'IGNORING ADDITIONAL SOS FIELDS. ['+r+'x'+c+'] '+sos[num]);
}
}
// If we are padding our field with a char, we need to add that back to line
// @todo validate if this goes beyond our width (and if scrolling not enabled)
if (fieldlen)
line = fieldchar.repeat(fieldlen)+line;
frame.input_fields.push({
type: fieldtype,
length: Number(fieldlen),
char: fieldchar,
name: field,
attribute: JSON.parse(JSON.stringify(ansi)),
x: Number(x+(xoffset !== undefined ? xoffset : 0)),
y: Number(y+(yoffset !== undefined ? yoffset : 0)),
value: '',
});
log(LOG_DEBUG,'input_field:'+JSON.stringify(frame.input_fields.last));
}
/* parse dynamic value field */
// @todo remove the trailing ESC \ to end the field, just use a control code ie: ^E \x05 (Enquiry) or ^Z \x26 (Substitute)
var m = line.match(/^\x1bX(([a-zA-Z._:^;]+[0-9]?;-?[0-9^;]+)([;]?[^;]+)?)\x1b\\/);
if (m !== null) {
// full string that matched
match = m.shift();
// thus, the rest of the line
line = line.substr(match.length);
//writeln('rest of line:'+JSON.stringify(line));
// We are interested in our field match
var df = m.shift().split(';');
log(LOG_DEBUG,'- DF found at ['+x+'x'+y+'], Field: '+df[0]+', Length: '+df[1]+', Pad:'+df[2]);
// If we are padding our field with a char, we need to add that back to line
// @todo validate if this goes beyond our width (and if scrolling not enabled)
line = (df[2] ? df[2] : '_').repeat(Math.abs(df[1]))+line;
frame.dynamic_fields.push({
name: df[0],
length: df[1],
pad: df[2],
x: x+(xoffset !== undefined ? xoffset : 0),
y: y+(yoffset !== undefined ? yoffset : 0),
value: undefined,
});
}
/* set character and attribute */
var ch = line[0];
line = line.substr(1);
if (debug && debug === y) {
log(LOG_DEBUG,'Got char: '+ch);
//write(ch);
}
/* validate position */
if (y < 0)
y = 0;
if (x < 0)
x = 0;
if (x >= width) {
x = 0;
y++;
}
/* set character and attribute */
if (! frame.content[y+1])
frame.content[y+1]=[];
if (attr === null)
throw new Error('Attribute is null?');
frame.content[y+1][x+1] = new Char(ch,attr);
x++;
}
// If we got a BG_BLACK|LIGHTGRAY ESC [0m, but not character, we include it as it resets any background that was going on
if ((attr === BG_BLACK|LIGHTGRAY) && frame.content[y+1] && (frame.content[y+1][x].__properties__.attr !== attr))
frame.content[y+1][x+1] = new Char(undefined,attr);
y++;
}
return frame;
}
load('ansitex/load/session.js');
// Our frame object
function SessionAnsitex() {
Session.apply(this,arguments);
/* File Extension used for frames */
this.settings.ext = 'tex';
/* Length of a frame */
this.settings.FRAME_LENGTH = 22;
/* Width of a frame */
this.settings.FRAME_WIDTH = 80;
/* Size of page owner (length) */
this.settings.FRAME_HEADER = 56;
/* Size of page number (length with a-z) */
this.settings.FRAME_PAGENUM = 12;
/* Size of cost (length without unit) */
this.settings.FRAME_COST = 9;
this.settings.MSG_SENDORNOT = '\1n\1h\1GKEY 1 TO SEND, 2 NOT TO SEND';
this.settings.MSG_LOGON = '\1n\1h\1GKEY 1 TO LOGON, 2 TO RETURN';
this.settings.MSG_SENT = '\1n\1h\1GMESSAGE SENT - KEY # TO CONTINUE';
this.settings.MSG_NOTSENT = '\1n\1h\1GMESSAGE NOT SENT - KEY # TO CONTINUE';
this.settings.ERR_NO_PARENT = '\1n\1h\1RPARENT FRAME DOESNT EXIST';
this.settings.ERR_NOT_IMPLEMENTED = '\1n\1h\1RNOT IMPLEMENTED YET?';
this.settings.ERR_ROUTE = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08';
this.settings.ERR_METHOD_NOT_EXIST = '\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08';
this.settings.ACCESS_DENIED = '\1n\1h\1RACCESS DENIED. MISTAKE? TRY AGAIN OR TELL US *08';
this.settings.ALREADY_MEMBER = '\1n\1h\1RALREADY MEMBER OF CUG'
this.settings.INACTIVITY = '\1n\1h\1RINACTIVITY ALERT, DISCONNECT PENDING...';
this.settings.INACTIVE = '\1n\1h\1RINACTIVITY DISCONNECT';
this.settings.NOACTION = '\1n\1h\1RNO ACTION PERFORMED';
this.settings.BASESTAR = '\1N\1G\1H*';
this.settings.INVALID_CODE = '\1n\1h\1RINVAID CODE, PLEASE TRY AGAIN *00';
this.settings.TOKEN_EMAIL = '\1n\1h\1RTOKEN EMAILED TO YOU...';
this.settings.TOKEN_SENT = '\1n\1h\1RTOKEN SENT, PLEASE ENTER TOKEN';
this.settings.INVALID_EMAIL = '\1n\1h\1RINVAID EMAIL, PLEASE TRY AGAIN *00';
this.settings.INVALID_UID = '\1n\1h\1RINVAID USER ID, PLEASE TRY AGAIN *00';
this.settings.CANNOT_SEND_TOKEN = '\1n\1h\1RCANNOT SEND VALIDATION CODE, PLEASE TRY AGAIN *00';
this.settings.USER_EXISTS = '\1n\1h\1RERROR USER EXISTS, PLEASE TRY AGAIN *00';
this.settings.USER_CREATE_ERROR = '\1n\1h\1RERROR CREATING USER, PLEASE TRY AGAIN *00';
this.settings.LOGIN_ERROR = '\1n\1h\1RERROR LOGGING IN, PLEASE TRY AGAIN *00';
this.settings.CANCEL_MSG = '\1n\1h\1GPRESS 2 TO CANCEL';
this.settings.SYS_ERROR = '\1n\1h\1RSYSTEM ERROR DETECTED - TRY AGAIN OR TELL US *08';
this.settings.LOADING = '\1n\1h\1Kloading...';
this.settings.PROCESSING = '\1n\1h\1Kprocessing...';
/**
* Set the attribute at the current position
*/
this.attr = function(field) {
write('\x1b['+field.i+';'+field.f+';'+field.b+'m');
}
this.baselineClear = function(reposition) {
this.cursorSave();
this.gotoxy(0,24);
this.cleareol();
if (! reposition)
this.cursorRestore();
}
/**
* Send a message to the baseline.
*
* @param text
* @param reposition
*/
this.baselineSend = function(text,reposition) {
this.cursorSave();
this.gotoxy(0,24);
write(this.getMessage(text));
this.cleareol();
if (! reposition)
this.cursorRestore();
}
/**
* Turn off the cursor
*/
this.cursorOff = function() {
write('\x1b[?25l');
write('\x1b[24;0H');
}
/**
* Turn on cursor
* @param x
* @param y
*/
this.cursorOn = function(x,y) {
write('\x1b[?25h');
if (x && y)
this.gotoxy(x,y);
}
this.cursorRestore = function() {
write('\x1b[u');
}
this.cursorSave = function() {
write('\x1b[s');
}
this.cleareol = function() {
write('\x1b[0K');
}
// Field backspace, that leaves the field filler char
this.fieldbs = function(char) {
write('\x1b[D'+char+'\x1b[D');
}
/**
* Position the cursor in a specific location
*
* @param x
* @param y
*/
this.gotoxy = function(x,y) {
write('\x1b['+y+';'+x+'H');
}
/*
// Render the frame to the user
this.render=function(context,withoutHeader) {
log(LOG_DEBUG,'- ANSI FRAME');
owner = base64_decode(this.owner);
const frame = new Frame(1,1,this.settings.FRAME_WIDTH,this.settings.FRAME_LENGTH+2,LIGHTGRAY);
frame.open();
// Dont show the page number on system login page
if ((! withoutHeader) && (user.number || (this.type !== FRAME_TYPE_LOGIN && NO_HISTORY_FRAMES.indexOf(this.page) === -1))) {
log(LOG_DEBUG,' - Owner: ['+this.pageowner+']');
cost = (this.isAccessible ? this.cost+FRAME_COSTUNIT : ' -');
header = '\1n'+this.pageownerlogo+' '.repeat(this.settings.FRAME_HEADER-console.strlen(this.pageownerlogo))+'\1n '+
(this.isAccessible ? '\1W' : '\1R')+'\1H'+this.page+' '.repeat(this.settings.FRAME_PAGENUM-this.page.length)+' '+
'\1G\1H'+' '.repeat(this.settings.FRAME_COST-cost.toString().length+1)+cost+'\1n'+
(console.screen_columns > 80 ? '\n\r' : '');
frame.putmsg(header);
}
contentgraphic = new Graphic(this.settings.FRAME_WIDTH);
contentgraphic.auto_extend = true;
contentgraphic.atcodes = false;
contentgraphic.ANSI = this.parse(base64_decode(this.content),context);
var contentframe = new Frame(1,2,this.settings.FRAME_WIDTH,this.settings.FRAME_LENGTH,LIGHTGRAY,frame);
contentframe.open();
contentframe.lf_strict = false;
contentframe.atcodes = false;
contentframe.word_wrap = false
contentframe.putmsg(contentgraphic.MSG)
contentframe.scrollTo(0,0);
frame.cycle();
return contentframe;
};
*/
this.qrcode=function(qr,subframe) {
// SMALL Image
var full = ascii(0xdb);
var top = ascii(0xdf);
var bot = ascii(0xdc);
var blank = ' ';
var qrcode = '';
/*
// Render the top line
var line = ascii(27)+'[1;37m'+bot;
for (var y = 0; y < qr.size; y++) {
line += bot;
}
qrcode += line+bot+bot+ascii(27)+'[0m'+"\r\n";
*/
// Render the body
for (var x = -1; x < qr.size; x=x+2) {
line = ascii(27)+'[1;37m'+full;
for (var y = 0; y < qr.size; y++) {
// Top is white
if (! ((x===-1)? 0 : qr.getModule(x, y))) {
line += (qr.getModule(x+1, y)) ? top : full;
// Top is black
} else {
line += (qr.getModule(x+1, y)) ? blank : bot;
}
}
qrcode += line+full+ascii(27)+'[0m'+"\r\n";
}
// Render the bottom
line = ascii(27)+'[1;37m'+top;
for (var y = 0; y < qr.size; y++) {
line += top;
}
qrcode += line+top+ascii(27)+'[0m'+"\r\n";
ans2bin(fo.parse(qrcode),subframe);
subframe.open();
subframe.cycle();
};
/*
this.save=function() {
file = system.mods_dir+'ansitex/text/'+this.page+'.tex';
w = new File(file);
if (! w.open('w')) {
log(LOG_ERROR,'! ERROR: Unable to create TEX file for '+this.page);
exit(1);
}
w.write(JSON.stringify(this));
w.close();
log(LOG_DEBUG,'Saved file: '+this.page+'.tex');
}
*/
}
SessionAnsitex.prototype = Session.prototype;
SessionAnsitex.prototype.constructor = SessionAnsitex;

691
load/session-viewdata.js Normal file
View File

@ -0,0 +1,691 @@
var SESSION_VIEWDATA = (1<<2);
var SESSION_EXT = 'vtx';
var VIEWDATA_FRAME_WIDTH = 40;
var VIEWDATA_FRAME_HEIGHT = 22;
var VIEWDATA_FRAME_PROVIDER_LENGTH = 23;
var VIEWDATA_FRAME_PAGE_LENGTH = 11;
var VIEWDATA_FRAME_COST_LENGTH = 6;
var VIEWDATA_LEFT = '\x08';
var VIEWDATA_RIGHT = '\x09';
var VIEWDATA_DOWN = '\x0a'; // \n
var VIEWDATA_UP = '\x0b';
var VIEWDATA_CLS = '\x0c';
var VIEWDATA_CR = '\x0d'; // \r
var VIEWDATA_CON = '\x11';
var VIEWDATA_COFF = '\x14';
var VIEWDATA_HOME = '\x1e';
var VIEWDATA_BLINK = '\x48';
var VIEWDATA_STEADY = '\x49';
var VIEWDATA_NORMAL = '\x4c';
var VIEWDATA_DOUBLE = '\x4d';
var VIEWDATA_CONCEAL = '\x58';
var VIEWDATA_BLOCKS = '\x59';
var VIEWDATA_SEPARATED = '\x5a';
var VIEWDATA_BLACKBACK = '\x5c';
var VIEWDATA_NEWBACK = '\x5d';
var VIEWDATA_HOLD = '\x5e';
var VIEWDATA_REVEAL = '\x5f';
var VIEWDATA_RED = '\x41';
var VIEWDATA_GREEN = '\x42';
var VIEWDATA_YELLOW = '\x43'; // C
var VIEWDATA_BLUE = '\x44';
var VIEWDATA_MAGENTA = '\x45';
var VIEWDATA_CYAN = '\x46';
var VIEWDATA_WHITE = '\x47';
var VIEWDATA_MOSIAC_RED = '\x51';
var VIEWDATA_MOSIAC_GREEN = '\x52';
var VIEWDATA_MOSIAC_YELLOW = '\x53';
var VIEWDATA_MOSIAC_BLUE = '\x54';
var VIEWDATA_MOSIAC_MAGENTA = '\x55';
var VIEWDATA_MOSIAC_CYAN = '\x56';
var VIEWDATA_MOSIAC_WHITE = '\x57'; // W
/* BINARY DUMP LEVEL 1 ATTRIBUTES */
var VIEWDATA_BIN_RED = '\x01';
var VIEWDATA_BIN_GREEN = '\x02';
var VIEWDATA_BIN_YELLOW = '\x03';
var VIEWDATA_BIN_BLUE = '\x04';
var VIEWDATA_BIN_MAGENTA = '\x05';
var VIEWDATA_BIN_CYAN = '\x06';
var VIEWDATA_BIN_WHITE = '\x07';
/**
* ViewData characters are 7bit (0x00-0x7f)
*
* Chars 0x00-0x1f are control characters (display attributes) and are sent to the terminal with 0x1b
* + 0x00-0x07 are foreground colors
* + 0x08-0x09 flash/steady
* + 0x0a-0x0b end/start box (?) *
* + 0x0c-0x0d normal/double height
* + 0x0e-0x0f double width (?) *
* + 0x10-0x17 are foreground graphics (mosiac) colors
* + 0x18/0x1f conceal/reveal
* + 0x19-0x1a solid/seperated graphics
* + 0x1b unused
* + 0x1c-1x1d Black/New Background (new background converts color foreground to background)
* + 0x1e-0x1f graphics hold/release (enables changing color and repeats previous graphics char)
* Chars 0x20-0x7f are normal printed ASCII chars
* Chars 0x20-0x3f & 0x60-0x7f when activated with a MOSIAC color sends a 2x3 pixel character
*
* We can map these into cga_defs with the following amendments:
* 0x00-0x0f = foreground/background colors (4 bits) (8 foreground/8 background colors)
* 0x10 - mosiac (bit 4)
* 0x20 - conceal (bit 5)
* 0x40 - seperated graphics (bit 6)
* 0x80 - flash (bit 7)
* 0x100 - double height (bit 8)
* 0x200 - hold (bit 9)
* 0x400 - new background (bits 10/11)
* 0x800 - black background (bits 10/11)
* 0xc00 - unused (bits 10/11)
* bits (12-15) unused
*
* @type {number}
*/
var MOSIAC = 0x10;
// Toggles
var CONCEAL = 0x20;
var REVEAL = 0x2000; // @temp Turns off Conceal
var SEPARATED = 0x40;
var BLOCKS = 0x4000; // @temp Turns off Separated
var STEADY = 0x8000; // @temp (turn off flash)
var DOUBLE = 0x100;
var NORMAL = 0x1000; // @temp Turns off Double Height
var HOLD = 0x200;
var RELEASE = 0x20000; // @temp turns off Hold
var NEWBACK = 0x400;
var BLACKBACK = 0x800;
/**
* This function converts ANSI text into an array of attributes
*
* @param contents - Our ANSI content to convert
* @param width - The width before wrapping to the next line
* @param yoffset - fields offset as discovered
* @param xoffset - fields offset as discovered
* @param debug - Enable debug mode
*/
function bintoattrs(contents,width,yoffset,xoffset,debug) {
if (debug)
writeln('DEBUG active: '+debug);
lines = (''+contents).split(/\r\n/);
var i = 0;
var bg = BG_BLACK;
var fg = LIGHTGRAY;
var attr = fg + bg + i;
// Attribute state on a new line
var new_line = attr;
var y = 0;
var frame = {
content: [],
dynamic_fields: [],
input_fields: [],
};
// @todo temp hack, rework ansi variable - perhaps have a function that converts an attribute back into an ANSI sequence
var ansi = { i: 0, f: 37, b: 40 };
while (lines.length > 0) {
var x = 0;
var line = lines.shift();
if ((debug !== undefined) && (y > debug)) {
exit(1);
}
if (debug) {
log(LOG_DEBUG,'y:'+y);
log(LOG_DEBUG,'line:'+line);
write('y:'+y+', line:'+line);
}
while (line.length > 0) {
if (x >= width) {
x = 0;
// Each new line, we reset the attrs
attr = new_line;
y++;
}
//writeln('next ch:'+line[0].charCodeAt(0));
/* parse control codes */
var m = line.match(/^([\x00-\x1f])/);
if (m !== null) {
line = line.substr(m[0].length);
attr = 0;
match = m.shift().charCodeAt(0);
/*
writeln('- match:'+match);
writeln('- match 0x0f:'+(match & 0x0f));
if (match & 0x10) {
writeln(' - got mosiac');
attr += MOSIAC;
}
*/
//if (match < 0x0f) {
//switch(match & 0x07) {
switch(match) {
case 0x00:
attr += BLACK;
break;
case 0x01:
attr += RED;
break;
case 0x02:
attr += GREEN;
break;
case 0x03:
attr += YELLOW;
break;
case 0x04:
attr += BLUE;
break;
case 0x05:
attr += MAGENTA;
break;
case 0x06:
attr += CYAN;
break;
case 0x07:
attr += LIGHTGRAY;
break;
case 0x08:
attr = BLINK;
break;
case 0x09:
attr = STEADY;
break;
/*
case 0x0a:
//attr = ENDBOX; // End Box (Unused?)
break;
case 0x0b:
//attr = STARTBOX; // Start Box (Unused?)
break;
*/
case 0x0c:
//attr &= ~DOUBLE;
attr = NORMAL;
break;
case 0x0d:
attr = DOUBLE;
break;
case 0x0e:
attr = NORMAL; // @todo Double Width (Unused)?
break;
case 0x0f:
attr = NORMAL; // @todo Double Width (Unused?)
break;
case 0x10:
attr = MOSIAC|BLACK;
break;
case 0x11:
attr += MOSIAC|RED;
break;
case 0x12:
attr += MOSIAC|GREEN;
break;
case 0x13:
attr += MOSIAC|YELLOW;
break;
case 0x14:
attr += MOSIAC|BLUE;
break;
case 0x15:
attr += MOSIAC|MAGENTA;
break;
case 0x16:
attr += MOSIAC|CYAN;
break;
case 0x17:
attr += MOSIAC|LIGHTGRAY;
break;
case 0x18:
attr = CONCEAL;
break;
case 0x19:
attr = BLOCKS;
break;
case 0x1a:
attr = SEPARATED;
break;
/*
case 0x1b:
//attr = NORMAL; // CSI
break;
*/
case 0x1c:
attr = BLACKBACK; // Black Background
break;
case 0x1d:
attr = NEWBACK; // New Background
break;
case 0x1e:
attr = HOLD; // Mosiac Hold
break;
case 0x1f:
attr = RELEASE; // Mosiac Release
break;
// Catch all for other codes
default:
attr = 0xff00;
}
if (debug)
writeln(' - got control code:'+attr+'['+y+','+x+'] - length:'+attr.length);
store(x++,y,null,attr);
attr = undefined;
continue;
}
/* parse an input field */
// Input field 'FIELD;valueTYPE;input char'
// @todo remove the trailing ESC \ to end the field, just use a control code ^B \x02 (Start of Text) and ^C \x03
var m = line.match(/^\x1b_(([A-Z]+;[0-9a-z]+)([;]?.+)?)\x1b\\/);
if (m !== null) {
log(LOG_DEBUG,'Got input field: '+JSON.stringify(m));
log(LOG_DEBUG,'ansi:'+JSON.stringify(ansi));
// full string that matched
match = m.shift();
// thus, the rest of the line
line = line.substr(match.length);
//writeln('rest of line:'+JSON.stringify(line));
// We are interested in our field match
var sos = m.shift().split(';');
//writeln('sos:'+JSON.stringify(sos));
for (var num in sos) {
switch (num) {
// First value is the field name
case '0':
field = sos[num];
break;
// Second value is the length/type of the field, nnX nn=size in chars, T=type (lower case)
case '1':
var c = sos[num].match(/([0-9]+)([a-z])/);
if (! c) {
log(LOG_ERROR,'SOS FAILED PARSING FIELD LENGTH/TYPE. ['+r+'x'+c+'] '+sos[num]);
break;
}
//log(LOG_DEBUG,'SOS ['+r+'x'+c+'] NUM CHARS: '+x[1]+', TYPE: '+x[2]);
fieldlen = c[1];
fieldtype = c[2];
break;
// Third field is the char to to use
case '2':
fieldchar = sos[num];
break;
default:
log(LOG_ERROR,'IGNORING ADDITIONAL SOS FIELDS. ['+r+'x'+c+'] '+sos[num]);
}
}
// If we are padding our field with a char, we need to add that back to line
// @todo validate if this goes beyond our width (and if scrolling not enabled)
if (fieldlen)
line = fieldchar.repeat(fieldlen)+line;
frame.input_fields.push({
type: fieldtype,
length: Number(fieldlen),
char: fieldchar,
name: field,
attribute: JSON.parse(JSON.stringify(ansi)),
x: Number(x+(xoffset !== undefined ? xoffset : 0)),
y: Number(y+(yoffset !== undefined ? yoffset : 0)),
value: '',
});
log(LOG_DEBUG,'input_field:'+JSON.stringify(frame.input_fields.last));
}
/* parse dynamic value field */
// @todo remove the trailing ESC \ to end the field, just use a control code ie: ^E \x05 (Enquiry) or ^Z \x26 (Substitute)
var m = line.match(/^\x1bX(([a-zA-Z._:^;]+[0-9]?;-?[0-9^;]+)([;]?[^;]+)?)\x1b\\/);
if (m !== null) {
// full string that matched
match = m.shift();
// thus, the rest of the line
line = line.substr(match.length);
//writeln('rest of line:'+JSON.stringify(line));
// We are interested in our field match
var df = m.shift().split(';');
log(LOG_DEBUG,'- DF found at ['+x+'x'+y+'], Field: '+df[0]+', Length: '+df[1]+', Pad:'+df[2]);
// If we are padding our field with a char, we need to add that back to line
// @todo validate if this goes beyond our width (and if scrolling not enabled)
line = (df[2] ? df[2] : '_').repeat(Math.abs(df[1]))+line;
frame.dynamic_fields.push({
name: df[0],
length: df[1],
pad: df[2],
x: x+(xoffset !== undefined ? xoffset : 0),
y: y+(yoffset !== undefined ? yoffset : 0),
value: undefined,
});
}
/* set character and attribute */
var ch = line[0];
line = line.substr(1);
if (debug && (debug === y)) {
writeln('y:'+y+', x:'+x+', ch:'+ch);
}
/* validate position */
if (y < 0)
y = 0;
if (x < 0)
x = 0;
store(x,y,ch,undefined);
x++;
}
// Each new line, we reset the attrs
attr = undefined;
y++;
}
return frame;
function store(x,y,ch,attr) {
/* set character and attribute */
if (! frame.content[y+1])
frame.content[y+1]=[];
frame.content[y+1][x+1] = new Char(ch,attr,SESSION_EXT);
}
}
load('ansitex/load/session.js');
// Our frame object
function SessionViewdata() {
Session.apply(this,arguments);
/* File Extension used for frames */
this.settings.ext = 'vtx';
/* Length of a frame */
this.settings.FRAME_LENGTH = 22;
/* Width of a frame */
this.settings.FRAME_WIDTH = 40;
/* Size of page owner (length) */
this.settings.FRAME_HEADER = 23;
/* Size of page number (length with a-z) */
this.settings.FRAME_PAGENUM = 11;
/* Size of cost (length without unit) */
this.settings.FRAME_COST = 3;
this.settings.MSG_SENDORNOT = ascii(27)+'BKEY 1 TO SEND, 2 NOT TO SEND';
this.settings.MSG_LOGON = ascii(27)+'BKEY 1 TO LOGON, 2 TO RETURN';
this.settings.MSG_SENT = ascii(27)+'BMESSAGE SENT - KEY _ TO CONTINUE';
this.settings.MSG_NOTSENT = ascii(27)+'BMESSAGE NOT SENT - KEY _ TO CONTINUE';
this.settings.ERR_NO_PARENT = ascii(27)+'APARENT FRAME DOESNT EXIST';
this.settings.ERR_NOT_IMPLEMENTED = ascii(27)+'ANOT IMPLEMENTED YET?';
this.settings.ERR_ROUTE = ascii(27)+'GMISTAKE?'+ascii(27)+'BTRY AGAIN OR TELL US ON *08';
this.settings.ERR_METHOD_NOT_EXIST = ascii(27)+'GMISTAKE?'+ascii(27)+'BTRY AGAIN OR TELL US ON *08';
this.settings.ACCESS_DENIED = ascii(27)+'AACCESS DENIED.';
this.settings.ALREADY_MEMBER = ascii(27)+'AALREADY MEMBER OF CUG'
this.settings.INACTIVITY = ascii(27)+'AINACTIVITY ALERT, DISCONNECT PENDING...';
this.settings.INACTIVE = ascii(27)+'AINACTIVITY DISCONNECT';
this.settings.NOACTION = ascii(27)+'ANO ACTION PERFORMED';
this.settings.BASESTAR = ascii(27)+'B*';
this.settings.INVALID_CODE = ascii(27)+'AINVAID CODE, PLEASE TRY AGAIN *00';
this.settings.TOKEN_EMAIL = ascii(27)+'ATOKEN EMAILED TO YOU...';
this.settings.TOKEN_SENT = ascii(27)+'ATOKEN SENT, PLEASE ENTER TOKEN';
this.settings.INVALID_EMAIL = ascii(27)+'AINVAID EMAIL, PLEASE TRY AGAIN *00';
this.settings.INVALID_UID = ascii(27)+'AINVAID USER ID, PLEASE TRY AGAIN *00';
this.settings.CANNOT_SEND_TOKEN = ascii(27)+'ACANNOT SEND VALIDATION CODE, PLEASE TRY AGAIN *00';
this.settings.USER_EXISTS = ascii(27)+'AERROR USER EXISTS, PLEASE TRY AGAIN *00';
this.settings.USER_CREATE_ERROR = ascii(27)+'AERROR CREATING USER, PLEASE TRY AGAIN *00';
this.settings.LOGIN_ERROR = ascii(27)+'AERROR LOGGING IN, PLEASE TRY AGAIN *00';
this.settings.CANCEL_MSG = ascii(27)+'BPRESS 2 TO CANCEL';
this.settings.SYS_ERROR = ascii(27)+'ASYS ERR, TRY AGAIN OR TELL US ON *08';
this.settings.LOADING = ascii(27)+'Cloading...';
this.settings.PROCESSING = ESC+VIEWDATA_YELLOW+'processing...';
var blp = 0; // Length of data on the bottom line
/**
* Set the attribute at the current position
*/
this.attr = function(field) {
//NOOP - the terminal takes care of this
}
this.baselineClear = function(reposition) {
msg = '';
log(LOG_DEBUG,'- Clear Bottom Line ['+blp+'] - reposition ['+reposition+']');
write_raw(VIEWDATA_HOME+VIEWDATA_UP+msg+
((blp > msg.length)
? (' '.repeat(blp-msg.length)+(reposition ? VIEWDATA_HOME+VIEWDATA_UP+VIEWDATA_RIGHT.repeat(msg.length) : ''))
: '')
);
blp = msg.length;
}
/**
* Send a message to the baseline.
*
* @param text
* @param reposition
*/
this.baselineSend = function(text,reposition) {
var msg = this.getMessage(text);
var x = this.strlen(msg);
log(LOG_DEBUG,'- Bottom Line ['+msg+'] ('+x+') - reposition ['+reposition+'] BLP:'+blp);
write_raw(VIEWDATA_HOME+VIEWDATA_UP+msg+
((blp > x)
? (' '.repeat(blp-x)+(reposition ? VIEWDATA_HOME+VIEWDATA_UP+VIEWDATA_RIGHT.repeat(x) : ''))
: '')
);
blp = x;
}
/**
* Turn off the cursor
*/
this.cursorOff = function() {
write_raw(VIEWDATA_COFF);
}
/**
* Turn on cursor
* @param x
* @param y
*/
this.cursorOn = function(x,y) {
write_raw(VIEWDATA_CON);
if (x && y)
this.gotoxy(x,y);
}
// Field backspace, that leaves the field filler char
this.fieldbs = function(char) {
log(LOG_DEBUG,'- Field backspace with char:'+char);
write_raw(VIEWDATA_LEFT+char+VIEWDATA_LEFT);
}
this.gotoxy = function(x,y) {
log(LOG_DEBUG,'- Moving cursor to y:'+y+', x:'+x);
// @todo This could be optimised to go the shortest route
write_raw(VIEWDATA_HOME);
if (x > 0)
write_raw(VIEWDATA_RIGHT.repeat(x));
if (y > 0)
write_raw(VIEWDATA_DOWN.repeat(y));
}
this.strlen = function(str) {
return str.replace(/\x1b/g,'').length;
};
/*
// Render the frame to the user
this.render=function(withHeader) {
log(LOG_DEBUG,'- VIEWDATA FRAME');
owner = base64_decode(this.owner);
header = VIEWDATA_DOWN;
//log(LOG_DEBUG,' - FRAME User: ['+JSON.stringify(user)+']');
// Dont show the page number on system login page
if (user.number || (this.type !== FRAME_TYPE_LOGIN && NO_HISTORY_FRAMES.indexOf(this.page) === -1)) {
log(LOG_DEBUG,' - Owner: ['+this.pageowner+'] ('+this.strlen(videotex(this.pageownerlogo))+')');
cost = (this.isAccessible ? this.cost+FRAME_COSTUNIT : ' -');
header = videotex(this.pageownerlogo)+' '.repeat(this.settings.FRAME_HEADER-this.strlen(videotex(this.pageownerlogo)))+
(this.isAccessible ? ascii(27)+'G' : ascii(27)+'A')+this.page+' '.repeat(this.settings.FRAME_PAGENUM-this.page.length)+
ascii(27)+'B'+' '.repeat(this.settings.FRAME_COST-cost.toString().length+1)+cost;
}
//console.status |= CON_RAW_IN;
write_raw(VIEWDATA_CLS);
write_raw(header);
return write_raw(videotex(base64_decode(this.content)));
};
*/
this.qrcode = function(qr) {
// Render the body
var qrcode = VIEWDATA_HOME+VIEWDATA_DOWN.repeat(5);
var offset = this.settings.FRAME_WIDTH-Math.ceil(qr.size/2)-1;
for (var x = -1; x < qr.size; x=x+3) {
var line = VIEWDATA_RIGHT.repeat(offset ? offset-1 : 0)+ESC+VIEWDATA_MOSIAC_WHITE;
for (var y = -1; y < qr.size; y=y+2) {
var char = 0;
//TL
char |= ((x===-1) || (y===-1) || ! qr.getModule(x,y)) ? (1<<0) : (0<<0);
//TR
char |= ((x===-1) || (y === qr.size-1) || ! qr.getModule(x,y+1)) ? (1<<1) : (0<<1);
//ML
char |= ((y===-1) || ! qr.getModule(x+1,y)) ? (1<<2) : (0<<2);
//MR
char |= ((y === qr.size-1) || ! qr.getModule(x+1,y+1)) ? (1<<3) : (0<<3);
//BL
char |= ((x===qr.size-2) || (y===-1) || ! qr.getModule(x+2,y)) ? (1<<4) : (0<<4);
//BR
char |= ((x===qr.size-2) || (y === qr.size-1) || ! qr.getModule(x+2,y+1)) ? (1<<5) : (0<<5);
char += 0x20;
if (char > 0x3f)
char += 0x20;
line += ascii(char);
}
// Render the right column
if (y%2)
line += '\x35';
repeat_count = this.settings.FRAME_WIDTH-Math.ceil(qr.size/2)-offset-(offset ? 1 : 2)-(y%2 === 1 ? 0 : 1);
qrcode += line+' '.repeat(repeat_count > 0 ? repeat_count : 0);
// To fix some terminals where moving right from col 40 doesnt advance to col 1 on the next line
qrcode +=VIEWDATA_LEFT+VIEWDATA_CR+VIEWDATA_DOWN;
}
log(LOG_DEBUG,'WIDTH:'+this.settings.FRAME_WIDTH);
log(LOG_DEBUG,'QR :'+(Math.ceil(qr.size/2)+1));
log(LOG_DEBUG,'OFF :'+offset);
log(LOG_DEBUG,'Y :'+(y%2 ? 0 : 1));
log(LOG_DEBUG,'X :'+(x%3 ? 0 : 1));
// Render the bottom
if (x%3) {
line = VIEWDATA_RIGHT.repeat(offset ? offset-1 : 0)+ESC+VIEWDATA_MOSIAC_WHITE;
for (var y = 0; y < qr.size; y=y+2) {
line += '\x23';
}
// Render the right column
if (y%2 === 0) {
line += '\x21';
}
qrcode += line+' '.repeat(repeat_count > 0 ? repeat_count : 0);
}
write_raw(qrcode);
};
/*
this.save=function() {
file = system.mods_dir+'ansitex/text/'+this.page+'.tex';
w = new File(file);
if (! w.open('w')) {
log(LOG_ERROR,'! ERROR: Unable to create TEX file for '+this.page);
exit(1);
}
w.write(JSON.stringify(this));
w.close();
log(LOG_DEBUG,'Saved file: '+this.page+'.tex');
}
*/
}
function videotex(data) {
var output = '';
//$output .= ($byte < 32) ? ESC.chr($byte+64) : chr($byte);
for (var i = 0; i < data.length; i++) {
output += (data.charCodeAt(i) < 32) ? "\x1b"+String.fromCharCode(data.charCodeAt(i)+64) : String.fromCharCode(data.charCodeAt(i));
}
return output;
}
SessionViewdata.prototype = Session.prototype;
SessionViewdata.prototype.constructor = SessionViewdata;

252
load/session.js Normal file
View File

@ -0,0 +1,252 @@
/**
* This handles session specific aspects of each frame type, eg: sending to the baseline, clearing, accepting
* input, moving around the frame (and windows within one if any)
*
* @constructor
*/
function Session() {
'use strict';
// Our page object
this.page = undefined;
/* Frame type settings */
this.settings = {};
/* Frame Version */
this.version = 1;
/* Frame Number [0-9] */
this.frame = null;
/* Frame Index [a-z] */
this.index = null;
/* The Service Provider owning the frame. */
this.owner = 0;
/* The cost to view the frame @todo to implement */
this.cost = 0;
/* The frame content, base64 encoded */
this.content = '';
// Frame's owned by the system where:
// isPublic is FALSE - the user must be logged in to view it
// isPublic is TRUE - can be viewed by non-logged in users
// Frame's owned by Service Providers where:
// isPublic is FALSE - can only be viewed if a user is
// a member of the Service Providers CUG
// isPublic is TRUE - can be viewed by users (if logged in)
this.isPublic = false; // Is this frame accessible to non CUG users
// If FALSE user must be a member of the CUG to view the frame
// All users, including unauthenticated, are members of 'system' (owner = 0)
this.isAccessible = false; // Is this frame available to be accessed
// If FALSE, only the SP can view/edit the frame
this.type = FRAME_TYPE_INFO; // The frame type - see FRAME_TYPES above
this.key = [null,null,null,null,null,null,null,null,null,null]; // Key actions [0-9]
/**
* Return the frames fields
*/
Object.defineProperty(this,'fields', {
get: function() {
return this.frame_fields;
}
});
/**
* Check if the user is already a member of the CUG
*/
Object.defineProperty(this,'isMember',{
get: function() {
log(LOG_DEBUG,'- Checking if user is a member of frame: '+this.page);
if (user.number) {
return (
(this.pageowner === SYSTEM_OWNER)
);
} else {
return false;
}
}
})
Object.defineProperty(this,'pageownerlogo', {
get: function() {
return base64_decode(this.settings.ext === 'tex' ? pageOwner(this.frame).logoans : pageOwner(this.frame).logovtx);
}
})
/**
* Enable pulling out submitted value by its name
*
* @param key
* @returns {null|string|*}
*/
this.fieldValue = function(key) {
for each (var k in this.frame_fields) {
log(LOG_DEBUG,' - k:'+JSON.stringify(k));
if (k.fname === key) {
return k.fvalue;
}
}
return null;
}
this.get = function(page) {
if (!(page instanceof PageObject))
throw new Error('page must be a PageObject');
switch (this.settings.ext) {
case 'tex':
case 'vtx':
this.page = new Page(this.settings.ext);
break;
default:
throw new Error(this.settings.ext+' has not been implemented');
}
this.baselineSend('LOADING');
this.page.get(page,this.settings.ext);
this.baselineClear();
}
/**
* Load a message frame
*
* @param page
*/
this.loadMessage = function(page) {
this.frame = ''+page;
this.index = 'a';
this.owner = 1;
this.isPublic = true;
this.isAccessible = true;
// @todo Keys should map to next/previous/send, etc as indicated in the template frame.
this.key = [this.frame.substr(0,7)+'1',null,null,null,null,null,null,null,null,null];
// @todo validate that FRAME_TYPE_MESSAGE is a message template
this.type = FRAME_TYPE_MESSAGE;
// Load our message
var ma = new MsgAreas()
var area = ma.getArea(this.frame);
var msg = ma.getMessage(this.frame);
var msg_header;
if (! msg)
return undefined;
// @todo Search 1zzzzEE..., 1zzzz...
var to = viewdata ? new FrameViewdata() : new FrameAnsi();
to.load(MAIL_TEMPLATE_FRAME);
// @todo Check that this is a frame of type "m" and report error if not
// @todo Take the cost from the template
this.cost = 5;
if (! to) {
log(LOG_ERROR,'Echomail template missing :['+MAIL_TEMPLATE_FRAME+'] ?');
msg_header = 'TO: '+msg.to.substr(0,72)+"\n\r";
msg_header += 'FROM: '+msg.from.substr(0,72)+"\n\r";
msg_header += 'DATE: '+msg.date.substr(0,72)+"\n\r";
msg_header += 'SUBJECT: '+msg.subject.substr(0,72)+"\n\r";
} else {
// @todo change this to use atcode()
msg_header = base64_decode(to.content).replace(/@(.*)@/g,
function (str, code, offset, s) {
var length = code.split(':')[1];
switch(code.split(':')[0]) {
case 'DATE': return msg.date.substr(0,length);
case 'TO': return msg.to.substr(0,length);
case 'FROM': return msg.from.substr(0,length);
case 'SUBJECT': return msg.subject.substr(0,length);
}
}
);
}
//log(LOG_DEBUG,'Loaded message: '+msg_header+msg.content);
this.content = base64_encode(msg_header+msg.content);
// Update the user's pointers
var stats = ma.getUserStats(this.frame);
// if this message is to the user, and the msg number > scan_ptr and it is the next message on the user's new mail list
var newmsgs = area.newMsgsToMe();
var next;
log(LOG_DEBUG,'User has: '+newmsgs.length-1+' msgs to read to ME');
if (newmsgs.length) {
next = newmsgs[1];
//log(LOG_DEBUG,'- NEXT is: '+next.tags+', this is: '+msg.tags);
if (next && (next.tags === msg.tags)) {
log(LOG_DEBUG,'- Updating scan_ptr to: '+next.number);
stats.scan_ptr = next.number;
}
// Last message
next = newmsgs[0];
if (next !== undefined) {
log(LOG_DEBUG,'- LAST TO ME is: '+next.tags);
this.key[1] = area.page(next.tags);
}
// Next new message
next = newmsgs[2];
if (next !== undefined) {
log(LOG_DEBUG,'- NEXT TO ME is: '+next.tags);
this.key[2] = area.page(next.tags);
}
}
// if this message is the next message, update last_read
newmsgs = area.newMsgs();
log(LOG_DEBUG,'User has: '+newmsgs.length+' msgs to read');
if (newmsgs.length) {
next = newmsgs[0];
log(LOG_DEBUG,'- NEXT is: '+next.tags+', this is: '+msg.tags);
if (next.tags === msg.tags) {
log(LOG_DEBUG,'- Updating last_read to: '+next.number);
stats.last_read = next.number;
}
}
// Previous Message
x = area.MessagePrev(this.frame);
if (x)
this.key[4] = area.page(x.tags);
// Next Message
x = area.MessageNext(this.frame);
if (x)
this.key[6] = area.page(x.tags);
log(LOG_DEBUG,'Built frame: ['+this.frame+']['+this.index+'] ('+this.page+')');
}
// Render the page
this.render = function() {
this.gotoxy(0,0);
write(so.page.display().join(''));
}
}
/**
* Return the message for a index
*
* @param index
* @returns {string|*}
*/
Session.prototype.getMessage = function(index) {
eval('var msg = this.settings.'+index);
return msg;
}

991
load/windows.js Normal file
View File

@ -0,0 +1,991 @@
/**
* Windows are elements of a Page object
*
* @param x - (int) starting x of it's parent [1..]
* @param y - (int) starting y of it's parent [1..]
* @param width - (int) full width of the window (text content will be smaller if there are scroll bars/boarder)
* @param height - (int) full height of the window (text content will be smaller if there are scroll bars/boarder)
* @param name - (string) internal name for the window (useful for debugging)
* @param parent - (object) parent of this window
* @param debug - (int) debug mode, which fills the window with debug content
* @constructor
*
* Pages have the following attributes:
* - bx/by - (int) right/bottom most boundary of the window representing the start + width/height of the window
* - child - (array) children in this window
* - height - (int) Window's height
* - name - (string) Windows name (useful for internal debugging)
* - parent - (object) Parent that this window belongs to
* - x/y - (int) start position of the window
* - visible - (bool) whether this window is visible
* - width - (int) Window's width
* - z - (int) Window's depth indicator
*
* Windows have the following public functions
* - build - Compile the frame for rendering
* - debug - Useful for debugging with properties of this Window
* - draw - Draw a part of this Window
* - drawline - Draw a y line for this Window
* - visibleChildren - Children that will be included when this window is rendered
*/
function Window(x,y,width,height,name,parent,debug) {
this.__properties__ = {
x: undefined, // X offset of parent that the canvas starts [1..width]
y: undefined, // Y offset of parent that the canvas starts [1..height]
z: 0, // Window top-bottom position, higher z is shown [0..]
ox: 0, // When canvas width > width, this is the offset we display [0..]
oy: 0, // When canvas height > height, this is the offset we display [0..]
width: undefined, // Display Width + (1 char if scrollbars = true)
height: undefined, // Display Height
canvaswidth: undefined, // Width of Canvas (default display width)
canvasheight: undefined, // Height of Canvas (default display height)
content: [], // Window content - starting at 0,0 = 1,1
visible: true, // Is this window visible
};
/*
this.__settings__ = {
checkbounds: true, // Can this frame move outside of the parent
v_scroll: true, // Can the content scroll vertically (takes up 1 line) [AUTO DETERMINE IF canvas > width]
h_scroll: false, // Can the content scroll horizontally (takes up 1 char) [AUTO DETERMINE IF canvas > height]
delay: 0, // Delay while rendering
word_wrap: false, // Word wrap content
pageable: false, // Overflowed content is rendered with the next page
};
*/
this.__relations__ = {
parent: undefined,
child: [],
};
/*
this.__position__ = {
cursor: undefined,
};
*/
/*
Validation to implement:
+ X BOUNDARY
- x cannot be < parent.x if checkbounds is true [when moving window]
- x+width(-1 if h_scroll is true) cannot be greater than parent.width if checkbounds is true
- v_scroll must be true for canvaswidth > width
- when scrolling ox cannot be > width-x
- when layout.pageable is true, next page will only have windows included that have a y in the range
ie: if height is 44 (window is 22), next page is 23-44 and will only include children where y=23-44
+ Y BOUNDARY
- y cannot be < parent.y if checkbounds is true [when moving window]
- y+height(-1 if v_scroll is true) cannot be greater than parent.height if checkbounds is true
- h_scroll must be true for canvasheight > height
- when scrolling oy cannot be > height-y
- when layout.pageable is true, children height cannot be greater than parent.height - y.
*/
function init(x,y,width,height,name,parent,debug) {
if (parent instanceof Window) {
this.z = parent.__relations__.child.length+1;
this.parent = parent;
parent.child = this;
// Check that our height/widths is not outside of our parent
if ((this.x < 1) || (width > this.parent.width))
throw new Error('Window: '+name+' width ['+width+'] is beyond our parent\'s width ['+this.parent.width+'].');
if ((x > this.parent.bx) || (x+width-1 > this.parent.bx))
throw new Error('Window: '+name+' start x ['+x+'] and width ['+width+'] is beyond our parent\'s end x ['+this.parent.bx+'].');
if ((this.y < 1) || (height > this.parent.height))
throw new Error('Window: '+name+' height ['+height+'] is beyond our parent\'s height ['+this.parent.height+'].');
if ((y > this.parent.by) || (y+height-1 > this.parent.by))
throw new Error('Window: '+name+' start y ['+y+'] and height ['+height+'] is beyond our parent\'s end y ['+this.parent.by+'].');
} else if (parent instanceof Page) {
this.parent = parent;
} else {
throw new Error('INVALID Parent Type: '+parent);
}
this.name = name;
this.x = x;
this.y = y;
this.__properties__.width = this.__properties__.canvaswidth = width;
this.__properties__.height = this.__properties__.canvasheight = height;
if (debug) {
this.__properties__.canvaswidth = width*2;
this.__properties__.canvasheight = height*2;
}
// Fill with data
for(var y=1;y<=this.__properties__.canvasheight;y++) {
for(var x=1;x<=this.__properties__.canvaswidth;x++) {
if (this.__properties__.content[y] == undefined)
this.__properties__.content[y] = [];
this.__properties__.content[y][x] = debug
? new Char((x > this.width) || (y > this.height) ? this.name[0].toUpperCase() : this.name[0].toLowerCase(),undefined)
: new Char(undefined,undefined);
}
}
}
init.apply(this,arguments);
// Windows boundary (right most width) of parent [1..]
Window.prototype.__defineGetter__('bx',function() {
return this.x+this.width-1;
});
// Windows boundary (bottom most height) of parent [1..]
Window.prototype.__defineGetter__('by',function() {
return this.y+this.height-1;
});
/*
// Can this window be moved outside of the parents visible area
Window.prototype.__defineGetter__('checkbounds',function() {
return this.__settings__.checkbounds;
});
Window.prototype.__defineSetter__('checkbounds',function(bool) {
this.__settings__.checkbounds = bool;
});
*/
// Management of children objects, in highest z order
Window.prototype.__defineGetter__('child',function() {
// Return the children sorted in z order lowest to highest
return this.__relations__.child.sort(function(a,b) { return (a.z < b.z) ? -1 : ((b.z < a.z) ? 1 : 0); });
});
Window.prototype.__defineSetter__('child',function(obj) {
if(obj instanceof Window) {
this.__relations__.child.push(obj);
} else
throw new Error('child not an instance of Window()');
});
// Window name
Window.prototype.__defineGetter__('name',function() {
return this.__properties__.name;
});
Window.prototype.__defineSetter__('name',function(string) {
if (this.__properties__.name)
throw new Error('name already DEFINED');
return this.__properties__.name = string;
});
// Parent window object
Window.prototype.__defineGetter__('parent',function() {
return this.__relations__.parent;
});
Window.prototype.__defineSetter__('parent',function(obj) {
if (this.__relations__.parent)
throw new Error('parent already DEFINED');
return this.__relations__.parent = obj;
});
// Window's display height
Window.prototype.__defineGetter__('height',function() {
return this.__properties__.height;
});
Window.prototype.__defineSetter__('height',function(int) {
if (this.__properties__.height)
throw new Error('height already DEFINED');
this.__properties__.height = int;
});
// Window's display width
Window.prototype.__defineGetter__('width',function() {
return this.__properties__.width;
});
Window.prototype.__defineSetter__('width',function(int) {
if (this.__properties__.width)
throw new Error('width already DEFINED');
this.__properties__.width = int;
});
// Window's start position on it's parent (width)
Window.prototype.__defineGetter__('x',function() {
return this.__properties__.x;
});
Window.prototype.__defineSetter__('x',function(int) {
if (this.__properties__.x)
throw new Error('x already DEFINED');
this.__properties__.x = int;
});
// Window's start position on it's parent (height)
Window.prototype.__defineGetter__('y',function() {
return this.__properties__.y;
});
Window.prototype.__defineSetter__('y',function(int) {
if (this.__properties__.y)
throw new Error('y already DEFINED');
this.__properties__.y = int;
});
// Is the current window visible
Window.prototype.__defineGetter__('visible',function() {
return this.__properties__.visible;
});
// What position is this window (highest is visible)
Window.prototype.__defineSetter__('z',function(int) {
if (this.__properties__.z)
throw new Error('z already DEFINED');
this.__properties__.z = int;
});
Window.prototype.__defineGetter__('z',function() {
return this.__properties__.z;
});
/**
* Build this window, returning an array of Char that will be rendered by Page
*
* @param xoffset - (int) This windows x position for its parent
* @param yoffset - (int) This windows y position for its parent
* @param debug - (int) debug mode, which fills the window with debug content
* @returns {*[]}
*/
Window.prototype.build = function(xoffset,yoffset,debug) {
var display = [];
if (debug) {
writeln('********* ['+this.name+'] *********');
writeln('name :'+this.name);
writeln('xoff :'+xoffset);
writeln('yoff :'+yoffset);
writeln('x :'+this.x);
writeln('bx :'+this.bx)
writeln('ox :'+this.__properties__.ox);
writeln('y :'+this.y);
writeln('by :'+this.by)
writeln('oy :'+this.__properties__.oy);
writeln('lines :'+this.__properties__.content.length);
writeln('content:'+JSON.stringify(Object.keys(this.__properties__.content).join(',')));
}
if (debug)
writeln('-------------');
for (y=1;y<=this.height;y++) {
if (debug)
write(padright(y,2,0)+':');
var sy = this.y-1+y+yoffset-1;
for (x=1;x<=this.width;x++) {
if (debug)
writeln('- Checking :'+this.name+', y:'+(y+this.__properties__.oy)+', x:'+(x+this.__properties__.ox));
var sx = this.x-1+x+xoffset-1;
if (display[sy] === undefined)
display[sy] = [];
if ((this.__properties__.content[y+this.__properties__.oy] !== undefined) && (this.__properties__.content[y+this.__properties__.oy][x+this.__properties__.ox] !== undefined)) {
display[sy][sx] = this.__properties__.content[y+this.__properties__.oy][x+this.__properties__.ox];
if (debug)
writeln('- storing in y:'+(sy)+', x:'+(sx)+', ch:'+display[sy][sx].ch);
} else {
//display[sy][sx] = new Char(null,BG_BLACK|LIGHTGRAY);
display[sy][sx] = new Char();
if (debug)
writeln('- nothing for y:'+(sy)+', x:'+(sx)+', ch:'+display[sy][sx].ch);
}
}
if (debug)
writeln();
}
if (debug) {
writeln('Window:'+this.name+', has ['+this.child.filter(function(child) { return child.visible; }).length+'] children');
this.child.forEach(function(child) {
writeln(' - child:'+child.name+', visible:'+child.visible);
})
}
// Fill the array with our values
var that = this;
this.child.filter(function(child) { return child.visible; }).forEach(function(child) {
if (debug) {
writeln('=========== ['+child.name+'] =============');
writeln('xoff :'+xoffset);
writeln('yoff :'+yoffset);
writeln('this.x :'+that.x);
writeln('this.y :'+that.y);
}
draw = child.build(that.x+xoffset-1,that.y+yoffset-1,debug);
if (debug) {
writeln('draw y:'+JSON.stringify(Object.keys(draw).join(',')));
writeln('draw 1:'+JSON.stringify(Object.keys(draw[1]).join(',')));
}
for (var y in draw)
for (var x in draw[y]) {
if (display[y] === undefined)
display[y] = [];
display[y][x] = draw[y][x];
}
if (debug) {
writeln('draw 1:'+JSON.stringify(Object.keys(draw[1]).join(',')));
writeln('=========== END ['+child.name+'] =============')
}
})
if (debug) {
writeln('this.name:'+this.name);
writeln('this.y:'+this.y);
writeln('display now:'+Object.keys(display[this.y]).join(','));
writeln('********* END ['+this.name+'] *********');
}
return display;
}
Window.prototype.debug = function(text) {
return '- '+text+': '+this.name+'('+this.x+'->'+(this.bx)+') width:'+this.width+' ['+this.y+'=>'+this.by+'] with z:'+this.z;
};
/**
* Render this window
*
* @param start - (int) Starting x position
* @param end - (int) Ending x position
* @param y - (int) Line to render
* @param color - (bool) Whether to include color
* @returns {{x: number, content: string}}
*/
Window.prototype.draw = function(start,end,y,color) {
var content = '';
for (x=start;x<=end;x++) {
var rx = this.__properties__.ox+x;
var ry = this.__properties__.oy+y;
// Check if we have an attribute to draw
if (! (ry in this.__properties__.content) || ! (rx in this.__properties__.content[ry])) {
content += ' ';
continue;
}
if (color === undefined || color === true) {
// Only write a new attribute if it has changed
if ((this.last === undefined) || (this.last !== this.__properties__.content[ry][rx].attr)) {
this.last = this.__properties__.content[ry][rx].attr;
content += (this.last === null ? BG_BLACK|LIGHTGRAY : this.last);
}
}
try {
content += (this.__properties__.content[ry][rx].ch !== null ? this.__properties__.content[ry][rx].ch : ' ');
} catch (e) {
writeln(e);
writeln('---');
writeln('x:'+(x-this.x));
writeln('y:'+(y-this.y));
writeln('ox:'+this.__properties__.ox);
writeln('oy:'+this.__properties__.oy);
writeln('rx:'+rx);
writeln('ry:'+ry);
exit();
}
}
return { content: content, x: end - start + 1 };
}
/**
* DRAW a line for this Window
*
* @param startx - the start position to render this window [1..]
* @param endx - the stop position to stop rendinering this window [1..]
* @param y - the current windows line number [1..]
* @param color - output color
* @param debug - turn on debugging
*
* Other Attributes:
* @param x - Our current X position
*
* When rendering, for each children on a y axis, the one with a character in x and the highest z wins.
*
* = Thus, if there is only 1 child on a y line, we render that child.width, with optional padding on the left/right.
*
* = If there are more children:
* + build an array of each childs x starting position sorted by z (asc).
* + if we need, we pad until the first child to rendered
* + after the child is rendered, we pad on the right if it didnt render to window.width
*
* + we render the a.x -> a.bx, unless there are other children with a higher z and it's x < a.bx
* + "x" keeps track of where we are
* + repeat until all children are processed, repeating from the begining again if necessary where children with a lower z have a wider width
*/
Window.prototype.drawline = function(startx,endx,y,color,debug) {
// Some sanity checking
if (startx < 1)
throw new Error('Nope, startx < 1:'+startx);
if (endx < startx)
throw new Error('Nope, endx:'+endx+' < startx:'+startx);
if (y < 1)
throw new Error('Nope, y < 1:'+y);
// Advance x if required
var x = startx;
// Get any of our children
var children = this.visibleChildren();
// Find children with something on this line.
var that = this;
var kids = children.filter(function(child) {
return (y-that.y+1 >= child.y) && (y-that.y+1 <= child.by) && (child.x < endx) && (child.bx > startx);
});
var draw;
var content = '';
if (debug !== false && (y > debug)) {
writeln()
writeln('********* drawline for ['+this.name+'] ***********');
writeln('* chars :'+startx+'->'+endx);
writeln('* line :'+y);
writeln('* parents start line :'+(this.parent.y !== undefined ? this.parent.y : '-no parent-'));
writeln(this.debug('drawline'));
writeln('* we have children :'+children.length);
children.forEach(function(child) {
if (y > debug) writeln(child.debug('CHILD'));
})
writeln('* relevant :'+kids.length);
kids.forEach(function(child) {
writeln(child.debug('KID'));
})
writeln('*************************************'+'*'.repeat(this.name.length));
}
var child = null;
// Only 1 child, so we render the line with it
if (kids.length === 1) {
child = kids.pop();
if (debug !== false && (y > debug)) {
writeln(': Only 1 child :'+child.name);
}
if (child.x > x) {
if (debug !== false && (y > debug)) {
writeln(': Padding until :'+x+'->'+(endx < child.x-1 ? endx : child.x-1)+' on:'+(y-this.y+1)+' with:'+this.name);
writeln(': Padded :'+((endx < child.x-1 ? endx : child.x-1)-x));
}
draw = this.draw(x,endx < child.x-1 ? endx : child.x-1,y-this.y+1,color);
content += draw.content;
x += draw.x;
if (debug !== false && (y > debug)) writeln();
}
if (debug !== false && (y > debug)) {
writeln(child.debug('THIS'));
writeln(': x :'+x);
writeln(': endx :'+endx);
writeln(': size of child :'+(child.bx-child.x+1));
writeln(': draw :'+(x-child.x+1)+'->'+((endx-startx+1 < child.width ? endx : child.bx)-child.x+1)+' on:'+(y-this.y+1))
}
if (x < endx) {
draw = child.drawline(x-child.x+1,(endx < child.bx ? endx : child.bx)-child.x+1,y-this.y+1,color,(debug !== false ? debug-this.y+1 : debug));
content += draw.content;
x += draw.x;
}
if (debug !== false && (y > debug)) writeln();
} else if (kids.length !== 0) {
if (debug !== false && (y > debug)) writeln('| multiple children :'+kids.length);
// Sort the kids in x start order
kids = kids.sort(function(a,b) {
return (a.x < b.x) ? -1 : ((b.x < a.x) ? 1 : 0);
});
if (debug !== false)
kids.forEach(function(child) {
if (y > debug) writeln(child.debug('SORT'));
})
var c = 0; // Our kids index
var C = null; // If we need to come back and reprocess the list
while (c < kids.length) {
child = kids[c++];
var drawendx = endx;
if (debug !== false && (y > debug)) {
writeln('| -------------- ['+child.name+'] -----------');
writeln('| x :'+x);
writeln('| child.x :'+child.x);
writeln('| child.bx:'+child.bx);
writeln('| startx :'+startx);
writeln(child.debug('CHILD'));
}
// If this child cannot be rendered, skip it (because it is underneath another window)
if (x > child.bx) {
if (debug !== false && (y > debug)) writeln('| skipping:'+child.name);
continue;
}
// Pad the beginning of this child
if (child.x > x) {
if (debug !== false && (y > debug)) {
writeln('| Padding :'+x+'->'+(child.x-1)+' for: '+this.name);
}
draw = this.draw(x,child.x-1,y-this.y+1,color);
content += draw.content;
x += draw.x;
if (debug !== false && (y > debug)) writeln();
}
/*
// If this child cannot be rendered, skip it
if (x < child.x) {
if (debug !== false && (y > debug)) writeln('| skipping:'+child.name);
continue;
}
*/
if (kids[c] !== undefined) {
if (debug !== false && (y > debug)) writeln(kids[c].debug('AFTER'));
// If the next child has a higher z and the same x skip this one
if ((kids[c].x === x) && (kids[c].z > child.z)) {
if (debug !== false && (y > debug)) writeln('|- Skipping:'+child.name);
C = kids[c].x;
continue;
}
if (debug !== false && (y > debug)) writeln('| FILTERING: x le:'+x+' bx lt:'+(child.bx)+' and z gt:'+child.z);
var nextkid = kids.filter(function(kid) {
if (debug !== false && (y > debug)) writeln('| - EVAL: '+kid.name+': x:'+(kid.x)+' bx:'+(kid.bx)+' and z:'+kid.z);
return ((kid.bx > x+startx-1)) && (kid.x < child.bx) && (kid.z > child.z);
});
if (debug !== false && (y > debug)) writeln('| Got next children: '+nextkid.length);
if (debug !== false)
nextkid.forEach(function(child) {
if (y > debug) writeln(child.debug('NEXT'));
})
// If a next child who starts before we finish and has a higher z, we'll stop at that next child
if (nextkid.length) {
// If nextkid should already be showing, we'll skip to it
if (x > nextkid[0].x) {
C = nextkid[0].bx;
if (debug !== false && (y > debug)) writeln('| NEXTKID should have started, skipping to it:'+nextkid[0].x+' (C:'+C+')');
continue;
}
drawendx = (endx-child.x+1 < nextkid[0].x-1 ? endx : nextkid[0].x-1);
if (debug !== false && (y > debug)) {
writeln('| x :'+x);
writeln('| startx :'+startx);
writeln('| childx :'+child.x);
}
if (debug !== false && (y > debug)) {
writeln(nextkid[0].debug('NEXTKID'));
writeln('| draw partial width of me:'+child.name+', drawendx:'+drawendx+' because nextkid starts:'+nextkid[0].name+' at:'+nextkid[0].x);
}
// If I need to continue after next kid, we'll come back
if ((! C) && (nextkid[0].bx < child.bx)) {
C = nextkid[0].bx;
if (debug !== false && (y > debug)) writeln('| coming back to:'+child.name+' at:'+C);
}
// If C is set, we need to push it out.
if (C)
C = drawendx;
// No next children
} else {
if (debug !== false && (y > debug)) {
writeln('| x :'+x);
writeln('| startx :'+startx);
writeln('| endx :'+endx);
writeln('| child :'+child.name);
writeln('| childx :'+child.x);
writeln('| childbx:'+child.bx);
writeln('| calcsta:'+(endx-child.x+1));
}
drawendx = (endx-child.x+1 < child.bx ? x+child.width : child.bx);
if (debug !== false && (y > debug)) writeln('| draw full width of me:'+child.name+', from:'+(x-child.x+1)+' until:'+drawendx);
}
// No other children
} else {
if (debug !== false && (y > debug)) {
writeln('| No other child');
writeln(child.debug('DRAW'));
}
/*
// If there is a gap, we'll need to pad it.
if (x < child.x) {
write(':'.repeat(xx=child.x-x));
x += xx;
}
*/
drawendx = (endx-child.x+1 < child.width ? endx : child.bx);
if (debug !== false && (y > debug)) {
writeln('| will render:'+child.name+', from:'+(x-child.x+1)+'->'+(drawendx-child.x+1)+' on:'+(y-this.y+1));
}
}
if (x < endx) {
draw = child.drawline(x-child.x+1,(x < endx ? drawendx : child.bx)-child.x+1,y-this.y+1,color,(debug !== false ? debug-this.y+1 : debug));
content += draw.content;
x += draw.x;
}
if (debug !== false && (y > debug)) {
writeln();
writeln('| C :'+C);
writeln('| x :'+x);
}
if (C && (x > C)) {
c = 0;
C = null;
if (debug !== false && (y > debug)) writeln('! Resetting c back');
}
}
}
if (debug !== false && (y > debug)) {
writeln('= x :'+x);
writeln('= startx :'+startx);
}
// Pad the beginning of this child
if (x < startx) {
if (debug !== false && (y > debug)) writeln('! Padding until:'+this.x);
write(' '.repeat(xx=this.x-startx));
x += xx;
}
// Render our item
if (debug !== false && (y > debug)) writeln('= drawing:'+this.name+' ('+x+'->'+endx+')');
draw = this.draw(x,endx,y-this.y+1,color);
content += draw.content;
x += draw.x;
if (debug !== false && (y > debug)) {
writeln();
writeln('- DONE, x now :'+x+' (endx:'+endx+') for:'+this.name);
writeln('- DREW :'+(x-startx));
writeln('************** end for ['+this.name+'] ***********');
}
if (debug !== false && y > debug+1) exit();
return { content: content, x: endx-startx+1 };
}
// Return the visible children (child should have sort by z)
Window.prototype.visibleChildren = function() {
return this.child.filter(function(child) {
return child.visible;
});
}
}
/**
* Each character in a window
*
* @todo Need to add a Viewdata implementation
* @param ch
* @param attr
* @param ext - tex = ANSItex, vtx = ViewDasta
* @constructor
*/
function Char(ch,attr) {
this.__properties__ = {
attr: attr, // - Attributes for the character (ie: color)
ch: ch, // - Character to be shown
//changed: false, // - Has this ch or attr been changed?
};
/**
* Return the color codes required to draw the current character
*
* @todo Implement Viewdata
* @param last - last rendered char
* @param ext - service we are rendering for
* @param debug - debug mode
* @returns {string|undefined}
*/
Char.prototype.attr = function(last,ext,debug) {
// If our attr is undefined, we'll return
if (this.__properties__.attr === undefined)
return;
// @todo This condition appears to fail?
if (this.__properties__.attr === null)
throw new Error('Attributes shouldnt be null');
var ansi = [];
var c;
var l;
var r = '';
if (debug) {
writeln();
writeln('- last:'+last+', this:'+this.__properties__.attr);
}
switch (ext) {
case 'tex':
if (debug) {
writeln(' - this BG_BLACK:'+(this.__properties__.attr & BG_BLACK));
writeln(' - last BG_BLACK:'+(last & BG_BLACK));
writeln(' - this HIGH:'+(this.__properties__.attr & HIGH));
writeln(' - last HIGH:'+(last & HIGH));
writeln(' - this BLINK:'+(this.__properties__.attr & BLINK));
writeln(' - last BLINK:'+(last & BLINK));
}
if (
(((this.__properties__.attr & BG_BLACK) !== (last & BG_BLACK))
|| ((this.__properties__.attr & HIGH) !== (last & HIGH))
|| ((this.__properties__.attr & BLINK) !== (last & BLINK))))
{
ansi.push('0');
last = BG_BLACK|LIGHTGRAY;
}
if ((this.__properties__.attr & HIGH) && ((this.__properties__.attr & HIGH) !== (last & HIGH))) {
ansi.push('1');
}
if ((this.__properties__.attr & BLINK) && ((this.__properties__.attr & BLINK) !== (last & BLINK))) {
ansi.push('5');
}
c = (this.__properties__.attr & 0x07);
l = (last & 0x07);
// Foreground
switch (c) {
case BLACK:
r = 30;
break;
case RED:
r = 31;
break;
case GREEN:
r = 32;
break;
case BROWN:
r = 33;
break;
case BLUE:
r = 34;
break;
case MAGENTA:
r = 35;
break;
case CYAN:
r = 36;
break;
case LIGHTGRAY:
r = 37;
break;
}
//writeln('r:'+r+', l:'+l+', c:'+c);
if (r && (c !== l))
ansi.push(r);
// Background
if (this.__properties__.attr & 0x70) {
c = (this.__properties__.attr & 0x70);
l = (last & 0x70);
switch (this.__properties__.attr & 0x70) {
case BG_BLACK:
r = 40;
break;
case BG_RED:
r = 41;
break;
case BG_GREEN:
r = 42;
break;
case BG_BROWN:
r = 43;
break;
case BG_BLUE:
r = 44;
break;
case BG_MAGENTA:
r = 45;
break;
case BG_CYAN:
r = 46;
break;
case BG_LIGHTGRAY:
r = 47
break;
}
if (r && (c !== l))
ansi.push(r);
}
if (false && debug)
writeln(' - ansi:'+ansi);
return ansi.length ? (debug ? '': '\x1b')+'['+ansi.join(';')+'m' : undefined;
case 'vtx':
log(LOG_DEBUG,'+ last:'+last+', attr ('+this.__properties__.attr+')');
switch (this.__properties__.attr) {
// \x08
case BLINK:
r = VIEWDATA_BLINK;
break;
// \x09
case STEADY:
r = VIEWDATA_STEADY;
break;
// \x0c
case NORMAL:
r = VIEWDATA_NORMAL;
break;
// \x0d
case DOUBLE:
r = VIEWDATA_DOUBLE;
break;
// \x18
case CONCEAL:
r = VIEWDATA_CONCEAL;
break;
// \x19
case BLOCKS:
r = VIEWDATA_BLOCKS;
break;
// \x1a
case SEPARATED:
r = VIEWDATA_SEPARATED;
break;
// \x1c
case BLACKBACK:
r = VIEWDATA_BLACKBACK;
break;
// \x1d
case NEWBACK:
r = VIEWDATA_NEWBACK;
break;
// \x1e
case HOLD:
r = VIEWDATA_HOLD;
break;
// \x1f
case RELEASE:
r = VIEWDATA_REVEAL;
break;
// Not handled
// \x0a-b,\x0e-f,\x1b
case 0xff00:
return '?';
default:
var mosiac = (this.__properties__.attr & MOSIAC);
c = (this.__properties__.attr & 0x07);
// Color control \x00-\x07, \x10-\x17
switch (c) {
case BLACK:
r = VIEWDATA_BLACKBACK;
break;
case RED:
r = mosiac ? VIEWDATA_MOSIAC_RED : VIEWDATA_RED;
break;
case GREEN:
r = mosiac ? VIEWDATA_MOSIAC_GREEN : VIEWDATA_GREEN;
break;
case BROWN:
r = mosiac ? VIEWDATA_MOSIAC_YELLOW : VIEWDATA_YELLOW;
break;
case BLUE:
r = mosiac ? VIEWDATA_MOSIAC_BLUE : VIEWDATA_BLUE;
break;
case MAGENTA:
r = mosiac ? VIEWDATA_MOSIAC_MAGENTA : VIEWDATA_MAGENTA;
break;
case CYAN:
r = mosiac ? VIEWDATA_MOSIAC_CYAN : VIEWDATA_CYAN;
break;
case LIGHTGRAY:
r = mosiac ? VIEWDATA_MOSIAC_WHITE : VIEWDATA_WHITE;
break;
default:
log(LOG_DEBUG,'Not a color?:'+c);
return '?';
}
}
log(LOG_DEBUG,'= result:'+r.charCodeAt(0)+', ('+r+')');
return ESC+r;
default:
throw new Error(this.__properties__.type+': has not been implemented');
}
};
Char.prototype.__defineGetter__('ch',function() {
return this.__properties__.ch;
});
Char.prototype.__defineSetter__('ch',function(char) {
if (typeof char !== 'string')
throw new Error('ch is not a string')
if (char.length !== 1)
throw new Error('ch can only be 1 character');
this.__properties__.ch = char;
})
}

673
main.js

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Ansitex specific includes // ANSItex specific includes
load('ansitex/load/defs.js'); load('ansitex/load/defs.js');
load('ansitex/load/funcs.js'); load('ansitex/load/funcs.js');

View File

@ -1 +1 @@
{"version":1,"frame":981,"index":"a","owner":9,"cost":0,"content":"ICARLGwSfGwUfCwTLBc3a38jMzdrIzUgICAgICACMDAwMTAwMDEwMSAgEX9rEn9qFC98E38XNWhvIDw9JCw1ByAgICAgICAgICAgICAgICAgIBEvLhIvKhQsLxMvF3VwenBxdXpwNSAgICAgICAgICAgICAgICAgICAgVmlkZW90ZXggICAgICAgICAgICAgICAgICAgIA1SZWdpc3RlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICACKiogB3RvIGNsZWFyIGlucHV0ICAgICAgICAgICAgICAgICAgICAgAiowMAd0byBzdGFydCBhZ2FpbiAgICAgICBVc2UCXwd0byBFbnRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICABRW1haWwgICAgOgcuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgAVVzZXIgSUQgIDoHLi4uLi4uLi4uLi4uLi4uICAgICAgICAgICAgIAFQYXNzd29yZCA6By4uLi4uLi4uLi4uLi4uLiAgICAgICAgICAgICABRnVsbCBOYW1lOgcuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgAUNpdHkgICAgIDoHLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLiAgIAFDb3VudHJ5ICA6By4uLiAgICAgICAgICAgICAgICAgICAgICAgICABUG9zdCBDb2RlOgcuLi4uLi4uLi4uICAgICAgICAgICAgICAgICAgAVRva2VuICAgIDoHLi4uLi4uICAgICAgICAgICAgICAgICAgICAgIBoXODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ2lzdGVyaW5nIGFuZCB1c2luZyB0aGlzIHN5c3RlbSwgeW91ICBhZ3JlZSB0byBhYmlkZSBieSB0aGUgc3lzdGVtIHJ1bGVzLiBTZWUCKjk4OF8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"r","key":[980,"register",null,null,null,null,null,null,null,null],"frame_fields":[{"ftype":"t","flength":25,"fchar":".","fname":"EMAIL","r":9,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":15,"fchar":".","fname":"UID","r":10,"c":12,"attribute":{},"fvalue":""},{"ftype":"p","flength":15,"fchar":".","fname":"PASS","r":11,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":25,"fchar":".","fname":"FULLNAME","r":12,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":25,"fchar":".","fname":"CITY","r":13,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":3,"fchar":".","fname":"COUNTRY","r":14,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":10,"fchar":".","fname":"PCODE","r":15,"c":12,"attribute":{},"fvalue":""},{"ftype":"t","flength":10,"fchar":".","fname":"TOKEN","r":16,"c":12,"attribute":{},"fvalue":""}],"date":"2020-07-08T05:17:35.174Z"} {"version":1,"frame":981,"index":"a","owner":9,"cost":0,"content":"ICARLGwSfGwUfCwTLBc3a38jMzdrIzUgICAgICACMDAwMTAwMDEwMSAgEX9rEn9qFC98E38XNWhvIDw9JCw1ByAgICAgICAgICAgICAgICAgIBEvLhIvKhQsLxMvF3VwenBxdXpwNSAgICAgICAgICAgICAgICAgICAgVmlkZW90ZXggICAgICAgICAgICAgICAgICAgIA1SZWdpc3RlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICACKiogB3RvIGNsZWFyIGlucHV0ICAgICAgICAgICAgICAgICAgICAgAiowMAd0byBzdGFydCBhZ2FpbiAgICAgICBVc2UCXwd0byBFbnRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICABRW1haWwgICAgOgcuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgAVVzZXIgSUQgIDoHLi4uLi4uLi4uLi4uLi4uICAgICAgICAgICAgIAFQYXNzd29yZCA6By4uLi4uLi4uLi4uLi4uLiAgICAgICAgICAgICABRnVsbCBOYW1lOgcuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uICAgAUNpdHkgICAgIDoHLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLiAgIAFDb3VudHJ5ICA6By4uLiAgICAgICAgICAgICAgICAgICAgICAgICABUG9zdCBDb2RlOgcuLi4uLi4uLi4uICAgICAgICAgICAgICAgICAgAVRva2VuICAgIDoHLi4uLi4uICAgICAgICAgICAgICAgICAgICAgIBoXODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ2lzdGVyaW5nIGFuZCB1c2luZyB0aGlzIHN5c3RlbSwgeW91ICBhZ3JlZSB0byBhYmlkZSBieSB0aGUgc3lzdGVtIHJ1bGVzLiBTZWUCKjk4OF8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"r","key":[980,"register",null,null,null,null,null,null,null,null],"input_fields":[{"type":"t","length":25,"char":".","name":"EMAIL","y":9,"x":12,"attribute":{},"value":""},{"type":"t","length":15,"char":".","name":"UID","y":10,"x":12,"attribute":{},"value":""},{"type":"p","length":15,"char":".","name":"PASS","y":11,"x":12,"attribute":{},"value":""},{"type":"t","length":25,"char":".","name":"FULLNAME","y":12,"x":12,"attribute":{},"value":""},{"type":"t","length":25,"char":".","name":"CITY","y":13,"x":12,"attribute":{},"value":""},{"type":"t","length":3,"char":".","name":"COUNTRY","y":14,"x":12,"attribute":{},"value":""},{"type":"t","length":10,"char":".","name":"PCODE","y":15,"x":12,"attribute":{},"value":""},{"type":"t","length":10,"char":".","name":"TOKEN","y":16,"x":12,"attribute":{},"value":""}],"date":"2020-07-08T05:17:35.174Z"}

View File

@ -1 +1 @@
{"version":1,"frame":98,"index":"a","owner":9,"cost":0,"content":"ICARLGwSfGwUfCwTLBc3a38jMzdrIzUgICAgICACMDAwMTAwMDEwMSAgEX9rEn9qFC98E38XNWhvIDw9JCw1ByAgICAgICAgICAgICAgICAgIBEvLhIvKhQsLxMvF3VwenBxdXpwNSAgICAgICAgICAgICAgICAgICAgVmlkZW90ZXggICAgICAgICAgICAgICAgICAgICANU2lnbiBJbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAVVzZXIHLi4uLi4uLi4uLi4uLi4uICAgICAgICAgICAgICAgICAgIAFQYXNzBy4uLi4uLi4uLi4uLi4uLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbnRlcgJORVcHdG8gcmVnaXN0ZXIgbmV3IGFjY291bnQgICAgICAgUHJlc3MCKioHdG8gY2xlYXIgeW91ciBpbnB1dCAgICAgICAgICAgIFByZXNzAiowMAd0byBzdGFydCBhZ2FpbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"l","key":[null,"login",null,null,null,null,null,null,null,null],"frame_fields": [{"ftype":"t","flength":15,"fchar":".","fname":"USER","r":10,"c":15,"attribute":{},"fvalue":""},{"ftype":"p","flength":15,"fchar":".","fname":"PASS","r":11,"c":15,"attribute":{},"fvalue":""}],"date":"2020-07-08T05:17:35.174Z"} {"version":1,"frame":98,"index":"a","owner":9,"cost":0,"content":"ICARLGwSfGwUfCwTLBc3a38jMzdrIzUgICAgICACMDAwMTAwMDEwMSAgEX9rEn9qFC98E38XNWhvIDw9JCw1ByAgICAgICAgICAgICAgICAgIBEvLhIvKhQsLxMvF3VwenBxdXpwNSAgICAgICAgICAgICAgICAgICAgVmlkZW90ZXggICAgICAgICAgICAgICAgICAgICANU2lnbiBJbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAVVzZXIHLi4uLi4uLi4uLi4uLi4uICAgICAgICAgICAgICAgICAgIAFQYXNzBy4uLi4uLi4uLi4uLi4uLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbnRlcgJORVcHdG8gcmVnaXN0ZXIgbmV3IGFjY291bnQgICAgICAgUHJlc3MCKioHdG8gY2xlYXIgeW91ciBpbnB1dCAgICAgICAgICAgIFByZXNzAiowMAd0byBzdGFydCBhZ2FpbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"l","key":[null,"login",null,null,null,null,null,null,null,null],"input_fields": [{"type":"t","length":15,"char":".","name":"USER","y":10,"x":15,"attribute":{},"value":""},{"type":"p","length":15,"char":".","name":"PASS","y":11,"x":15,"attribute":{},"value":""}],"date":"2020-07-08T05:17:35.174Z"}