/** * ANSItex definitions */ var ACTION_EXIT =99; /* Exit the script */ var ACTION_RELOAD =1; /* Reload the current frame */ var ACTION_GOTO =2; /* Goto a specific frame */ var ACTION_BACKUP =3; /* Goto previous frame */ var ACTION_NEXT =4; /* Goto next frame */ var ACTION_TERMINATE =5; /* Terminate the session */ var ACTION_SUBMITRF =6; /* Submit form contents */ var ACTION_STAR =7; /* Star command entry */ var MODE_BL =1; /* Typing * command on baseline */ var MODE_FIELD =2; /* Field Input */ var MODE_SUBMITRF =3; /* Asking if form should be submitted */ var MODE_RFNOTSENT =4; /* Response frame not sent */ var MODE_RFSENT =5; /* Response frame sent */ var MODE_RFERROR =6; /* Response frame error */ var FRAME_LENGTH =22; /* Length of a frame */ var FRAME_WIDTH =80; /* Width of a frame */ var FRAME_HEADER =56; /* Size of page owner (length) */ var FRAME_PAGENUM =12; /* Size of page number (length with a-z) */ var FRAME_COST = 9; /* Size of cost (length without unit) */ var FRAME_COSTUNIT ='c'; /* Unit of cost */ var FRAME_TYPE_INFO ='i'; // Information Frame, requires no response after viewed var FRAME_TYPE_TERMINATE ='t'; // Terminate Frame, contents displayed and then carrier dropped var FRAME_TYPE_EXTERNAL ='x'; // Frame the calls an External Method // Contents indicate the method to be called with arguments var FRAME_TYPE_RESPONSE ='r'; // Response frame, input fields are embedded in the frame and after input the // response will be submitted to the Service Provider, or to a method var FRAME_TYPE_LOGIN ='l'; // Login frame, enables the user to authenticate to the system, or to a CUG var MSG_SENDORNOT ='\1n\1h\1GKEY 1 TO SEND, 2 NOT TO SEND'; var MSG_LOGON ='\1n\1h\1GKEY 1 TO LOGON, 2 TO RETURN'; var MSG_SENT ='\1n\1h\1GMESSAGE SENT - KEY # TO CONTINUE'; var MSG_NOTSENT ='\1n\1h\1GMESSAGE NOT SENT - KEY # TO CONTINUE'; var ERR_NOT_IMPLEMENTED ='\1n\1h\1RNOT IMPLEMENTED YET?'; var ERR_ROUTE ='\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08'; var ERR_METHOD_NOT_EXIST ='\1n\1h\1WMISTAKE? \1GTRY AGAIN OR TELL US ON *08'; var ACCESS_DENIED ='\1n\1h\1RACCESS DENIED. \1RMISTAKE? TRY AGAIN OR TELL US *08'; var INACTIVITY ='\1n\1h\1RINACTIVITY ALERT'; var INACTIVE ='\1n\1h\1RINACTIVITY DISCONNECT'; var NO_HISTORY_FRAMES =['980a','98b','981a']; var SYSTEM_FRAMES =['AWgBUkEBR04BQlMBWUkBbgE3AWt0ZXgBbg==']; // Our frame object function TexFrame() { this.version=1; // The version of this frame - in case we update functionality and we need to be // backwards compatible this.frame=null; // Frame Number [0-9]+ this.index=null; // Frame Index [a-z] this.owner=''; // The Service Provider owning the frame. this.cost=0; // The cost to view the frame @TODO this.content=''; // The frame 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 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] this.frame_fields = []; // If a response frame, the embedded fields in the frame // Render the frame to the user this.render=function(withHeader) { owner = base64_decode(this.owner); header = '\n\r'; 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)) { header = '\1n'+owner+' '.repeat(FRAME_HEADER-console.strlen(owner))+'\1n '+ '\1W\1H'+this.page+' '.repeat(FRAME_PAGENUM-this.page.length)+' '+ '\1G\1H'+' '.repeat(FRAME_COST-this.cost.toString().length)+this.cost+FRAME_COSTUNIT+ (console.screen_columns > 80 ? '\n\r' : ''); } if ((this.type == FRAME_TYPE_LOGIN) || (this.type == FRAME_TYPE_RESPONSE)) { return header+this.parse(this.content); } else { return header+this.content; } }; 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; } Object.defineProperty(this,'page', { get: function() { if (this.frame == null || this.index == null) return null; return this.frame+this.index; } }); Object.defineProperty(this,'fields', { get: function() { return this.frame_fields; } }); 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: '+(SYSTEM_FRAMES.indexOf(this.owner)>-1)); system_frame = (SYSTEM_FRAMES.indexOf(this.owner)>-1); // user.number 0 is unidentified user. if (user.number) { return ( (this.isAccessible && this.isPublic) || (this.isAccessible && ! this.isPublic && user.isMember) || (user.isOwner) ); } else { return (system_frame && this.isPublic && this.isAccessible); } } }); } // Load a frame from disk (.tex file) TexFrame.prototype.load = function(filename) { log(LOG_DEBUG,'Loading frame from: '+filename); f = new File(system.mods_dir+'ansitex/text/'+filename+'.tex'); if (! f.exists || ! f.open('r')) { return null; } try { var load = JSON.parse(f.read()); for (property in load) { this[property] = load[property]; } this.content = base64_decode(this.content); } catch (error) { log(LOG_ERROR,'Frame error: '+error); return null; } log(LOG_DEBUG,'Loaded 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] 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 */ TexFrame.prototype.parse = function(text) { var c = 1; // column var r = 2; // row (row 1 is the header) var output = ''; this.frame_fields = []; // Default Attributes f = 39; b = 49; i = 0; for(p=0;p= 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; // SOS 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 \ 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 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>FRAME_WIDTH) { c = 1; r++; } /* // @todo - If we are longer than FRAME_LENGTH, move the output into the next frame. if (r>FRAME_LENGTH) { break; } */ } return output; }; this;