679 lines
18 KiB
JavaScript
679 lines
18 KiB
JavaScript
const SESSION_VIEWDATA = (1<<2);
|
|
const SESSION_EXT = 'vtx';
|
|
|
|
const FRAME_WIDTH = 40;
|
|
const FRAME_HEIGHT = 22;
|
|
const FRAME_PROVIDER_LENGTH = 23;
|
|
const FRAME_PAGE_LENGTH = 11;
|
|
const FRAME_COST_LENGTH = 6;
|
|
const FRAME_ATTR_LENGTH = 0; // Space that an attribute takes
|
|
|
|
const VIEWDATA_LEFT = '\x08';
|
|
const VIEWDATA_RIGHT = '\x09';
|
|
const VIEWDATA_DOWN = '\x0a'; // \n
|
|
const VIEWDATA_UP = '\x0b';
|
|
const VIEWDATA_CLS = '\x0c';
|
|
const VIEWDATA_CR = '\x0d'; // \r
|
|
const VIEWDATA_CON = '\x11';
|
|
const VIEWDATA_COFF = '\x14';
|
|
const VIEWDATA_HOME = '\x1e';
|
|
|
|
const VIEWDATA_BLINK = '\x48';
|
|
const VIEWDATA_STEADY = '\x49';
|
|
const VIEWDATA_NORMAL = '\x4c';
|
|
const VIEWDATA_DOUBLE = '\x4d';
|
|
const VIEWDATA_CONCEAL = '\x58';
|
|
const VIEWDATA_BLOCKS = '\x59';
|
|
const VIEWDATA_SEPARATED = '\x5a';
|
|
const VIEWDATA_BLACKBACK = '\x5c';
|
|
const VIEWDATA_NEWBACK = '\x5d';
|
|
const VIEWDATA_HOLD = '\x5e';
|
|
const VIEWDATA_REVEAL = '\x5f';
|
|
|
|
const VIEWDATA_RED = '\x41';
|
|
const VIEWDATA_GREEN = '\x42';
|
|
const VIEWDATA_YELLOW = '\x43'; // C
|
|
const VIEWDATA_BLUE = '\x44';
|
|
const VIEWDATA_MAGENTA = '\x45';
|
|
const VIEWDATA_CYAN = '\x46';
|
|
const VIEWDATA_WHITE = '\x47';
|
|
|
|
const VIEWDATA_MOSIAC_RED = '\x51';
|
|
const VIEWDATA_MOSIAC_GREEN = '\x52';
|
|
const VIEWDATA_MOSIAC_YELLOW = '\x53';
|
|
const VIEWDATA_MOSIAC_BLUE = '\x54';
|
|
const VIEWDATA_MOSIAC_MAGENTA = '\x55';
|
|
const VIEWDATA_MOSIAC_CYAN = '\x56';
|
|
const VIEWDATA_MOSIAC_WHITE = '\x57'; // W
|
|
|
|
/* BINARY DUMP LEVEL 1 ATTRIBUTES */
|
|
const VIEWDATA_BIN_RED = '\x01';
|
|
const VIEWDATA_BIN_GREEN = '\x02';
|
|
const VIEWDATA_BIN_YELLOW = '\x03';
|
|
const VIEWDATA_BIN_BLUE = '\x04';
|
|
const VIEWDATA_BIN_MAGENTA = '\x05';
|
|
const VIEWDATA_BIN_CYAN = '\x06';
|
|
const 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 rawtoattrs(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 SessionProtocol() {
|
|
Session.apply(this,arguments);
|
|
|
|
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 && FRAMES_NO_HISTORY.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;
|
|
}
|
|
|
|
SessionProtocol.prototype = Session.prototype;
|
|
SessionProtocol.prototype.constructor = SessionProtocol;
|