2022-12-09 06:19:33 +00:00
/ * *
PAGE . js handles ANSItex and ViewData page frames
It also handles windows , horizontal / vertical scrolling and content paging .
This is inspired by frame . js provided by Synchronet
Objects :
+ Page - our display page , build with Windows
- Line 1 - Title ( Fixed )
- Line 2. . 23 - Content ( Scrollable )
- Line 24 - Status / Command Input ( Fixed )
2023-12-21 23:25:32 +00:00
= @ todo When scrolling is disabled , and the canvas is greater than the window , then "nextpage" returns the next frame
= Pageable windows cannot have children [ b - z frames ] , only "CONTENT" is paged
= @ todo Pageable windows are pagable when scrolling is false and window canvas . height > window . height and canvas . width = window . width
2022-12-09 06:19:33 +00:00
+ Window - size W x H , where W / H can be larger than the Screen
- Window holds all the content to be shown
- x , y - attributes define the position of the window in it ' s parent [ 1. . ]
- z - determines which layer the window is on , higher z is shown [ 0. . ]
- width / height - determines the physical size of the window ( cannot be larger than it ' s parent )
- canvas width / height - determines the logical size of the window , which if larger than physical enables scrolling
- ox / oy - determines the start of the canvas that is shown , relative to canvas width / height
- service - Current supported are ANSItex ( 80 x24 ) and ViewData ( 40 x24 )
- content - array of Chars height / width order
- visible - determines if the window ( and it ' s children ) are renderable
= Windows can be have children , and the z determines the layer shown relative to its parent
= Swapping z values determines which windows are hidden by others
+ Char - object holding each character , and it ' s color when rendered
= Rendering
- ANSItex
+ Each attribute can have it ' s own color ( colors take up no positional space )
+ We only change render control colors to change attributes when it actually changes , otherwise we render just the character
- ViewData
+ An attribute Foreground or Background or Special Function takes up a character
+ Character must be set to NULL when it ' s a control character
= EXAMPLE :
2023-12-24 09:11:40 +00:00
a = new Page ( ) // root frame 80 x 24 for ANSItex
2022-12-09 06:19:33 +00:00
b = new Window ( 1 , 1 , 40 , 22 , a . content ) // b frame 40 x 22 - starting at 1,1
c = new Window ( 41 , 1 , 40 , 22 , a . content ) // c frame 40 x 22 - starting at 41,1 (child of a)
d = new Window ( 1 , 1 , 21 , 10 , c ) // d frame 20 x 11 - starting at 1,1 of c
e = new Window ( 25 , 12 , 10 , 5 , c ) // e frame 10 x 5 - starting at 25,12 of c
f = new Window ( 15 , 8 , 13 , 7 , c ) // f frame 13 x 7 - starting at 15,8 of c
-- : _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ |
01 : TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
02 : 22222222222222222222222222222222222222224444444444444444444443333333333333333333
03 : 2 24 43 3
04 : 2 24 43 3
05 : 2 24 43 3
06 : 2 24 43 3
07 : 2 24 43 3
08 : 2 24 444444443333333 3
09 : 2 24 466666666666663 3
10 : 2 24 46 63 3
11 : 2 2444444444444446 63 3
12 : 2 2333333333333336 633333333 3
13 : 2 23 36 665555553 3
14 : 2 23 36 65 53 3
15 : 2 23 366666666666665 53 3
16 : 2 23 333333333335555 53 3
17 : 2 23 355555555553 3
18 : 2 23 333333333333 3
19 : 2 23 3
20 : 2 23 3
21 : 2 23 3
22 : 2 23 3
23 : 22222222222222222222222222222222222222223333333333333333333333333333333333333333
24 : PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
-- : _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ | _ _ _ _ . _ _ _ _ |
* /
2023-12-21 23:25:32 +00:00
load ( 'ansitex/load/windows.js' ) ; // Our supporting window class
require ( 'ansitex/load/msgbases.js' , 'MAX_PAGE_NUM' ) ; // To read/write to message bases
2023-12-24 06:44:02 +00:00
require ( 'sbbsdefs.js' , 'SS_USERON' ) ; // Need for our ANSI colors eg: BG_*
2022-12-09 06:19:33 +00:00
/ * *
* This object represents a full page that interacts with the user
*
* @ param service - Type of page ( tex = ANSI , vtx = VIEWDATA )
* @ param debug - Whether to dump some debug information . This is an int , and will start debugging on line debug
* @ constructor
*
* Pages have the following attributes :
* - dimensions - ( string ) representation of their width x height
* - dynamic _fields - ( array ) Location of fields that are dynamically filled
* - height - ( Number ) page height
* - input _fields - ( array ) Location of fields that take input
* - page - ( string ) Full page number ( frame + index )
* - width - ( Number ) page width
*
* Pages have the following settings :
* - cost - ( int ) Cost to view the page
* - page - ( object ) Frame / index of the page
* - provider - ( string ) Name of the frame provider
* - showHeader - ( boolean ) Whether to show the header when rendering the frame
* - type - ( TEX / VTX ) Type of frame
*
* Pages have the following public functions
* - build - Compile the frame for rendering
* - display - Display the compiled frame
2023-12-21 23:25:32 +00:00
* - import - Load a frame from a file source
* - save - Save the frame to the msgbase
* - load - Load the frame from the msgbase
2022-12-09 06:19:33 +00:00
* /
2023-12-24 09:11:40 +00:00
function Page ( debug ) {
2023-12-21 23:25:32 +00:00
this . _ _window _ _ = {
2022-12-09 06:19:33 +00:00
layout : undefined , // Window - Full page content
header : undefined , // Window - Page Title
provider : undefined , // Page provider (*)
pagenum : undefined , // Our page number (*)
cost : undefined , // Page cost (*)
2023-12-21 23:25:32 +00:00
body : undefined , // Window - Page body
} ;
this . _ _properties _ _ = {
name : new PageObject ,
2023-12-24 06:44:02 +00:00
type : undefined , // Frame type
2022-12-09 06:19:33 +00:00
input _fields : [ ] , // Array of our input fields
dynamic _fields : [ ] , // Array of our dynamic fields
isAccessible : undefined , // Is this page visible to all users
2023-12-27 11:24:20 +00:00
// If FALSE, only the SP can view/edit the frame
2022-12-09 06:19:33 +00:00
isPublic : undefined , // Is this page visible to public (not CUG) users
2023-12-27 11:24:20 +00:00
// If FALSE user must be a member of the CUG to view the frame
// All users, including unauthenticated, are members of 'system' (owner = 0)
// 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)
2023-12-24 06:44:02 +00:00
key : [ ] , // Key actions
2023-12-27 11:24:20 +00:00
raw : undefined , // Page raw content
2022-12-09 06:19:33 +00:00
} ;
this . _ _defaults _ _ = {
attr : BG _BLACK | LIGHTGRAY ,
} ;
this . _ _compiled _ _ = {
2023-12-21 23:25:32 +00:00
build : undefined , // Our page compiled content
2022-12-09 06:19:33 +00:00
} ;
/ *
this . _ _settings _ _ = {
pageable : false , // If the virtual window is larger that height (and width is the same) next page is the next content
contenttitle : undefined , // Template (window) for 1st page (a)
contentsubtitle : undefined , // Template (window) for subsequent pages (b..z)
}
* /
/ * *
* @ todo borders for each window
* @ param service
* @ param debug
* /
2023-12-24 09:11:40 +00:00
function init ( debug ) {
log ( LOG _DEBUG , '- PAGE::init(): type [' + SESSION _EXT + ']' ) ;
this . _ _window _ _ . layout = new Window ( 1 , 1 , FRAME _WIDTH , FRAME _HEIGHT + 1 , 'LAYOUT' , this , debug ) ;
this . _ _window _ _ . body = new Window ( 1 , 2 , FRAME _WIDTH , FRAME _HEIGHT , 'CONTENT' , this . _ _window _ _ . layout , debug ) ;
this . _ _window _ _ . header = new Window ( 1 , 1 , FRAME _WIDTH , 1 , 'HEADER' , this . _ _window _ _ . layout , debug ) ;
this . _ _window _ _ . provider = new Window ( 1 , 1 , FRAME _PROVIDER _LENGTH , 1 , 'PROVIDER' , this . _ _window _ _ . header , debug ) ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
2023-12-24 06:44:02 +00:00
require ( 'ansitex/load/session/ansitex.js' , 'SESSION_ANSITEX' ) ;
2022-12-09 06:19:33 +00:00
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . pagenum = new Window ( 57 , 1 , FRAME _PAGE _LENGTH , 1 , '#' , this . _ _window _ _ . header , debug ) ;
this . _ _window _ _ . cost = new Window ( 71 , 1 , FRAME _COST _LENGTH , 1 , '$' , this . _ _window _ _ . header , debug ) ;
2022-12-09 06:19:33 +00:00
break ;
case 'vtx' :
2023-12-24 06:44:02 +00:00
require ( 'ansitex/load/session/viewdata.js' , 'SESSION_VIEWDATA' ) ;
2022-12-09 06:19:33 +00:00
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . pagenum = new Window ( 24 , 1 , FRAME _PAGE _LENGTH , 1 , '#' , this . _ _window _ _ . header , debug ) ;
this . _ _window _ _ . cost = new Window ( 35 , 1 , FRAME _COST _LENGTH , 1 , '$' , this . _ _window _ _ . header , debug ) ;
2022-12-09 06:19:33 +00:00
break ;
default :
2023-12-24 06:44:02 +00:00
throw new Error ( 'INVALID Page Service: ' + SESSION _EXT ) ;
2022-12-09 06:19:33 +00:00
}
}
// @todo change this to Object.defineProperty() - see session.js
/ * *
* Determine if this frame is accessible to the current user
* /
Page . prototype . _ _defineGetter _ _ ( 'accessible' , function ( ) {
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '- Checking if user [' + user . number + '] can access frame: ' + this . name . toString ( ) ) ;
log ( LOG _DEBUG , '|* Frame Owner: ' + JSON . stringify ( this . pageowner ) + ', System Frame: ' + ( this . pageowner === SYSTEM _OWNER ) ) ;
log ( LOG _DEBUG , '|* Accessible: ' + JSON . stringify ( this . _ _properties _ _ . isAccessible ) ) ;
log ( LOG _DEBUG , '|* Public: ' + JSON . stringify ( this . _ _properties _ _ . isPublic ) ) ;
log ( LOG _DEBUG , '|* Member: ' + JSON . stringify ( this . isMember ) ) ;
2022-12-09 06:19:33 +00:00
// user.number 0 is unidentified user.
if ( user . number ) {
return (
( this . _ _properties _ _ . isAccessible && this . pageowner === SYSTEM _OWNER && ! this . _ _properties _ _ . isPublic ) ||
( this . _ _properties _ _ . isAccessible && this . _ _properties _ _ . isPublic ) ||
( this . _ _properties _ _ . isAccessible && ! this . _ _properties _ _ . isPublic && this . isMember ) ||
2023-12-21 23:25:32 +00:00
( pageEditor ( this . name . frame ) )
2022-12-09 06:19:33 +00:00
) ;
} else {
2023-12-24 06:44:02 +00:00
return this . _ _properties _ _ . isAccessible && ( this . pageowner === SYSTEM _OWNER ) && this . _ _properties _ _ . isPublic ;
2022-12-09 06:19:33 +00:00
}
} ) ;
2023-12-24 09:11:40 +00:00
Page . prototype . _ _defineGetter _ _ ( 'cost' , function ( ) {
return Number ( this . _ _properties _ _ . cost ) ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineSetter _ _ ( 'cost' , function ( int ) {
if ( typeof int !== 'number' )
throw new Error ( 'Cost must be a number' ) ;
2023-12-24 06:44:02 +00:00
this . _ _properties _ _ . cost = int ;
2023-12-24 09:11:40 +00:00
if ( ( '' + int ) . length > FRAME _COST _LENGTH - 1 - FRAME _ATTR _LENGTH )
throw new Error ( 'Cost too large' ) ;
2023-12-24 06:44:02 +00:00
// Populate the cost window
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . cost . _ _properties _ _ . content = rawtoattrs ( ESC + '[1;32m' + padright ( int , FRAME _COST _LENGTH - 1 - FRAME _ATTR _LENGTH , ' ' ) + FRAME _COSTUNIT ) . content ;
2022-12-09 06:19:33 +00:00
break ;
case 'vtx' :
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . cost . _ _properties _ _ . content = rawtoattrs ( VIEWDATA _BIN _GREEN + padright ( int , FRAME _COST _LENGTH - 1 - FRAME _ATTR _LENGTH , ' ' ) + FRAME _COSTUNIT ) . content ;
2022-12-09 06:19:33 +00:00
break ;
default :
2023-12-24 06:44:02 +00:00
throw new Error ( SESSION _EXT + ' type not implemented' ) ;
2022-12-09 06:19:33 +00:00
}
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'dimensions' , function ( ) {
2023-12-21 23:25:32 +00:00
return this . _ _properties _ _ . width + ' X ' + this . _ _properties _ _ . height ;
2022-12-09 06:19:33 +00:00
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'dynamic_fields' , function ( ) {
2023-12-24 06:44:02 +00:00
return this . _ _properties _ _ . dynamic _fields === undefined ? [ ] : this . _ _properties _ _ . dynamic _fields ;
2022-12-09 06:19:33 +00:00
} ) ;
2023-12-21 23:25:32 +00:00
Page . prototype . _ _defineSetter _ _ ( 'dynamic_fields' , function ( array ) {
this . _ _properties _ _ . dynamic _fields = array ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineGetter _ _ ( 'height' , function ( ) {
2023-12-21 23:25:32 +00:00
return Number ( this . _ _window _ _ . layout . height ) ;
2022-12-09 06:19:33 +00:00
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'input_fields' , function ( ) {
return this . _ _properties _ _ . input _fields ;
} ) ;
2023-12-21 23:25:32 +00:00
Page . prototype . _ _defineSetter _ _ ( 'input_fields' , function ( array ) {
this . _ _properties _ _ . input _fields = array ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineSetter _ _ ( 'isAccessible' , function ( bool ) {
2023-12-24 06:44:02 +00:00
if ( typeof bool !== 'boolean' )
2022-12-09 06:19:33 +00:00
throw new Error ( 'isAccessible must be a boolean' ) ;
2023-12-24 06:44:02 +00:00
this . _ _properties _ _ . isAccessible = bool ;
2022-12-09 06:19:33 +00:00
} ) ;
2023-12-27 11:24:20 +00:00
/ * *
* Check if the user is already a member of the CUG
* /
Page . prototype . _ _defineGetter _ _ ( 'isMember' , function ( ) {
// @todo Implement CUGs, this would be "=== SERVICE_PROVIDER" and user is a member of SERVICE_PROVIDER
return user . number && ( this . pageowner === SYSTEM _OWNER ) ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineSetter _ _ ( 'isPublic' , function ( bool ) {
2023-12-24 06:44:02 +00:00
if ( typeof bool !== 'boolean' )
2022-12-09 06:19:33 +00:00
throw new Error ( 'isPublic must be a boolean' ) ;
2023-12-24 06:44:02 +00:00
this . _ _properties _ _ . isPublic = bool ;
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'key' , function ( ) {
return this . _ _properties _ _ . key ;
} ) ;
Page . prototype . _ _defineSetter _ _ ( 'key' , function ( array ) {
if ( typeof array !== 'object' )
throw new Error ( 'key must be an array :' + typeof array ) ;
return this . _ _properties _ _ . key = array ;
2022-12-09 06:19:33 +00:00
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'name' , function ( ) {
return this . _ _properties _ _ . name ;
} ) ;
Page . prototype . _ _defineSetter _ _ ( 'name' , function ( object ) {
if ( ! ( object instanceof PageObject ) )
throw new Error ( 'Page must be PageObject' ) ;
this . _ _properties _ _ . name = object ;
2023-12-24 09:11:40 +00:00
if ( ( '' + this . _ _properties _ _ . name . frame ) . length > FRAME _PAGE _LENGTH - 1 - FRAME _ATTR _LENGTH )
throw new Error ( 'Pagenum too large' ) ;
2023-12-24 06:44:02 +00:00
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . pagenum . _ _properties _ _ . content = rawtoattrs ( ESC + '[1;37m' + this . _ _properties _ _ . name . toString ( ) ) . content ;
2022-12-09 06:19:33 +00:00
break ;
case 'vtx' :
2023-12-24 09:11:40 +00:00
this . _ _window _ _ . pagenum . _ _properties _ _ . content = rawtoattrs ( VIEWDATA _BIN _WHITE + this . _ _properties _ _ . name . toString ( ) ) . content ;
2022-12-09 06:19:33 +00:00
break ;
default :
2023-12-24 06:44:02 +00:00
throw new Error ( SESSION _EXT + ' type not implemented' ) ;
2022-12-09 06:19:33 +00:00
}
} ) ;
Page . prototype . _ _defineGetter _ _ ( 'pagenext' , function ( ) {
return this . _ _properties _ _ . name . next ;
} ) ;
/ * *
* Determine who the owner of a page is
* /
Page . prototype . _ _defineGetter _ _ ( 'pageowner' , function ( ) {
2023-12-21 23:25:32 +00:00
return pageOwner ( this . _ _properties _ _ . name . frame ) . prefix ;
2022-12-09 06:19:33 +00:00
} ) ;
Page . prototype . _ _defineSetter _ _ ( 'provider' , function ( ansi ) {
var provider ;
2023-12-24 06:44:02 +00:00
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
2023-12-24 09:11:40 +00:00
provider = rawtoattrs ( ansi + ESC + '[0m' ) . content ;
2022-12-09 06:19:33 +00:00
2023-12-24 09:11:40 +00:00
if ( provider [ 1 ] . filter ( function ( child ) { return child . ch ; } ) . length - 1 > FRAME _PROVIDER _LENGTH )
2022-12-09 06:19:33 +00:00
throw new Error ( 'Provider too large' ) ;
break ;
case 'vtx' :
2023-12-24 09:11:40 +00:00
provider = rawtoattrs ( ansi ) . content ;
2022-12-09 06:19:33 +00:00
2023-12-24 09:11:40 +00:00
if ( provider [ 1 ] . length - 1 > FRAME _PROVIDER _LENGTH )
2022-12-09 06:19:33 +00:00
throw new Error ( 'Provider too large' ) ;
break ;
default :
2023-12-24 06:44:02 +00:00
throw new Error ( SESSION _EXT + ' not implemented' ) ;
2022-12-09 06:19:33 +00:00
}
2023-12-21 23:25:32 +00:00
this . _ _window _ _ . provider . _ _properties _ _ . content = provider ;
2022-12-09 06:19:33 +00:00
} ) ;
2023-12-27 11:24:20 +00:00
Page . prototype . _ _defineGetter _ _ ( 'raw' , function ( ) {
return this . _ _properties _ _ . raw ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineSetter _ _ ( 'showHeader' , function ( bool ) {
if ( typeof bool !== 'boolean' )
throw new Error ( 'showHeader expected a true/false' ) ;
2023-12-21 23:25:32 +00:00
this . _ _window _ _ . header . visible = bool ;
2022-12-09 06:19:33 +00:00
} ) ;
2023-12-24 06:44:02 +00:00
Page . prototype . _ _defineGetter _ _ ( 'type' , function ( ) {
return this . _ _properties _ _ . type ;
} ) ;
Page . prototype . _ _defineSetter _ _ ( 'type' , function ( string ) {
if ( typeof string !== 'string' )
throw new Error ( 'type must be an string :' + typeof string ) ;
return this . _ _properties _ _ . type = string ;
} ) ;
2022-12-09 06:19:33 +00:00
Page . prototype . _ _defineGetter _ _ ( 'width' , function ( ) {
2023-12-21 23:25:32 +00:00
return Number ( this . _ _window _ _ . layout . width ) ;
2022-12-09 06:19:33 +00:00
} ) ;
/ * *
* Build the screen layout
*
* @ returns { * }
* /
this . build = function ( force ) {
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '* Building frame...' ) ;
2022-12-09 06:19:33 +00:00
if ( this . _ _compiled _ _ . build && ! force )
throw new Error ( 'Refusing to build without force.' ) ;
this . build _system _fields ( ) ;
2023-12-21 23:25:32 +00:00
this . _ _compiled _ _ . build = this . _ _window _ _ . layout . build ( 1 , 1 , false ) ;
2022-12-09 06:19:33 +00:00
// Add our dynamic values
var fields = this . dynamic _fields . filter ( function ( item ) { return item . value !== undefined ; } ) ;
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|* We have DF fields:' + fields . length ) ;
2022-12-09 06:19:33 +00:00
if ( fields . length )
insert _fields ( fields , this . _ _compiled _ _ . build ) ;
// Add our dynamic values
fields = this . input _fields . filter ( function ( item ) { return item . value !== undefined ; } ) ;
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|* We have INPUT fields:' + fields . length ) ;
2022-12-09 06:19:33 +00:00
if ( fields . length )
insert _fields ( fields , this . _ _compiled _ _ . build ) ;
// Insert our *_field data (if it is set)
function insert _fields ( fields , build ) {
for ( var i in fields ) {
// writeln('- adding:'+fields[i].name+', with value:'+fields[i].value);
var content = fields [ i ] . value . split ( '' ) ;
for ( x = fields [ i ] . x ; x < fields [ i ] . x + Math . abs ( fields [ i ] . length ) ; x ++ ) {
var index = x - fields [ i ] . x ;
if ( content [ index ] )
2024-01-01 06:03:25 +00:00
build [ fields [ i ] . y ] [ x ] . ch = fields [ i ] . type !== FIELD _PASSWORD ? content [ index ] : FIELD _PASSWORD _MASK ;
2022-12-09 06:19:33 +00:00
}
}
}
}
/ * *
2024-01-01 06:03:25 +00:00
* Build in any input _fields with values
2022-12-09 06:19:33 +00:00
* /
2024-01-01 06:03:25 +00:00
this . build _input _fields = function ( )
{
var that = this ;
2022-12-09 06:19:33 +00:00
2024-01-01 06:03:25 +00:00
var f = this . input _fields . filter ( function ( item ) { return item . value . length ; } ) ;
log ( LOG _DEBUG , '* INPUT_FIELDS WITH VALUES:' + f . length ) ;
if ( f . length ) {
f . forEach ( function ( field ) {
that . input _field ( field . name , field . value ) ;
} ) ;
this . _ _compiled _ _ . build = null ;
}
}
2022-12-09 06:19:33 +00:00
2024-01-01 06:03:25 +00:00
/ * *
* Build in our dynamic _fields that can be populated automatically
* /
this . build _system _fields = function ( context ) {
2022-12-09 06:19:33 +00:00
var that = this ;
2024-01-01 06:03:25 +00:00
var f = this . dynamic _fields . filter ( function ( item ) { return item . value === undefined ; } ) ;
if ( f . length ) {
f . forEach ( function ( field ) {
that . dynamic _field ( field . name , atcode ( field . name , field . length , field . pad , context ) ) ;
} ) ;
}
2022-12-09 06:19:33 +00:00
}
/ * *
* Return the compiled screen as an array of lines
*
* @ param last - the last attribute sent to the screen
* @ param color - whether to render the color attributes
* /
this . display = function ( last , color ) {
var debug = false ;
if ( ! this . _ _compiled _ _ . build )
this . build ( ) ;
// Our built display
var display = this . _ _compiled _ _ . build ;
// Default attribute when the screen is cleared
var new _screen ;
// Default attribute when a new line is started
var new _line ;
var result = [ ] ;
var attr ;
2023-12-24 09:11:40 +00:00
new _screen = BG _BLACK | LIGHTGRAY ;
2023-12-24 06:44:02 +00:00
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
break ;
case 'vtx' :
new _line = BG _BLACK | LIGHTGRAY ;
break ;
default :
throw new Error ( SESSION _EXT + ' dump processing not implemented' ) ;
}
if ( last === undefined )
last = new _screen ;
// Check all our dynamic fields have been placed
df = this . dynamic _fields . filter ( function ( item ) { return item . value === undefined ; } ) ;
// If our dynamic fields havent been filled in
if ( df . length > 0 )
2023-12-27 11:24:20 +00:00
throw new Error ( 'Dynamic fields [' + df . length + '] without values:' + ( df . map ( function ( item ) { return item . name ; } ) . join ( '|' ) ) ) ;
2022-12-09 06:19:33 +00:00
// Render the display
for ( y = 1 ; y <= this . height ; y ++ ) {
var line = '' ;
if ( new _line )
last = new _line ;
if ( debug )
writeln ( '============== [' + y + '] ===============' ) ;
for ( x = 1 ; x <= this . width ; x ++ ) {
if ( debug )
log ( LOG _DEBUG , '* CELL : y:' + y + ', x:' + x ) ;
// The current char value
var char = ( display [ y ] !== undefined && display [ y ] [ x ] !== undefined ) ? display [ y ] [ x ] : undefined ;
if ( debug )
2023-12-21 23:25:32 +00:00
log ( LOG _DEBUG , ' - CHAR : ' + ( char !== undefined ? char . ch : 'undefined' ) + ', ATTR:' + ( char !== undefined ? char . attr : 'undefined' ) + ', LAST:' + last ) ;
2022-12-09 06:19:33 +00:00
if ( debug ) {
writeln ( ) ;
writeln ( '-------- [' + x + '] ------' ) ;
2023-12-21 23:25:32 +00:00
writeln ( 'y:' + y + ',x:' + x + ', attr:' + ( char !== undefined ? char . attr : 'undefined' ) ) ;
2022-12-09 06:19:33 +00:00
}
if ( ( color === undefined ) || color ) {
// Only write a new attribute if it has changed (and not Videotex)
2023-12-24 06:44:02 +00:00
if ( ( SESSION _EXT === 'vtx' ) || ( last === undefined ) || ( last !== char . attr ) ) {
2022-12-09 06:19:33 +00:00
// The current attribute for this character
2023-12-24 06:44:02 +00:00
attr = ( char === undefined ) ? undefined : char . attribute ( last , SESSION _EXT , debug ) ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
switch ( SESSION _EXT ) {
2022-12-09 06:19:33 +00:00
case 'tex' :
// If the attribute is null, we'll write our default attribute
if ( attr === null )
2023-12-21 23:25:32 +00:00
line += this . attr
2022-12-09 06:19:33 +00:00
else
line += ( attr !== undefined ) ? attr : '' ;
break ;
case 'vtx' :
// If the attribute is null, we'll ignore it since we are drawing a character
if ( ( attr !== undefined ) && ( attr !== null ) ) {
if ( debug )
log ( LOG _DEBUG , ' = SEND ATTR :' + attr + ', attr length:' + attr . length + ', last:' + last ) ;
line += attr ;
}
break ;
default :
2023-12-24 06:44:02 +00:00
throw new Error ( 'service type:' + SESSION _EXT + ' hasnt been implemented.' ) ;
2022-12-09 06:19:33 +00:00
}
}
// For no-color output and ViewData, we'll render a character
} else {
2023-12-24 06:44:02 +00:00
if ( ( SESSION _EXT === 'vtx' ) && char . attr )
2022-12-09 06:19:33 +00:00
line += '^' ;
}
if ( char . ch !== undefined ) {
if ( debug )
2023-12-21 23:25:32 +00:00
log ( LOG _DEBUG , ' = SEND CHAR :' + char . ch + ', attr:' + char . attr + ', last:' + last ) ;
2022-12-09 06:19:33 +00:00
line += ( char . ch !== null ) ? char . ch : '' ;
} else {
if ( debug )
log ( LOG _DEBUG , ' = CHAR UNDEFINED' ) ;
line += ' ' ;
}
2023-12-21 23:25:32 +00:00
last = ( char . attr === undefined ) ? undefined : char . attr ;
2022-12-09 06:19:33 +00:00
}
result . push ( line ) ;
if ( debug && ( y > debug ) )
exit ( 1 ) ;
}
return result ;
}
/ * *
* Dump a page in an axis grid to view that it renders correctly
*
* @ param last - ( int ) The current cursor color
* @ param color - ( bool ) Optionally show color
* @ param debug - ( int ) Debugging mode starting at line
*
* @ note When drawing a Char :
*
* | CH | ATTR | RESULT |
* | -- -- -- -- -- -- | -- -- -- -- -- -- | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- |
* | undefined | undefined | no output ( cursor advances 1 ) | NOOP
* | null | undefined | invalid |
* | defined | undefined | invalid |
* | undefined | null | invalid |
* | null | null | invalid |
* | defined | null | render ch only ( cursor advances 1 ) | Viewdata
* | undefined | defined | render attr only ( no cursor move ) | ANSItex ( used to close the edge of a window )
* | null | defined | render attr only ( cursor advances 1 ) | Viewdata
* | defined | defined | render attr + ch ( cursor advances 1 ) | ANSItex
* | -- -- -- -- -- -- | -- -- -- -- -- -- | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- |
*
* + for ANSItex , attribute ( s ) dont advance the cursor , clear screen sets the default to BG _BLACK | LIGHTGRAY
* + for ViewData , an attribute does advance the cursor , and each attribute advances the cursor , also each new line starts with a default BG _BLACK | WHITE
* /
this . dump = function ( last , color , debug ) {
if ( ! this . _ _compiled _ _ . build )
this . build ( ) ;
// Our built display
var display = this . _ _compiled _ _ . build ;
color = ( color === undefined ) || ( color === '1' ) || ( color === true ) ;
2023-12-24 06:44:02 +00:00
writeln ( 'Dumping Page:' + this . name . toString ( ) ) ;
2022-12-09 06:19:33 +00:00
writeln ( '= Size :' + this . dimensions ) ;
writeln ( '- Last :' + last ) ;
writeln ( '- Color:' + color ) ;
writeln ( '- Debug:' + debug ) ;
if ( last === undefined )
last = new _screen ;
if ( debug ) {
writeln ( '==== content dump ====' ) ;
var yy = 1 ;
for ( var y in display ) {
write ( padright ( yy , 2 , 0 ) + ':' ) ;
var xx = 1 ;
for ( var x in display [ y ] ) {
if ( debug && ( y === debug ) ) {
writeln ( JSON . stringify ( display [ y ] [ x ] ) ) ;
writeln ( )
}
write ( '[' ) ;
2023-12-21 23:25:32 +00:00
if ( display [ y ] [ x ] . attr === undefined ) {
2022-12-09 06:19:33 +00:00
// NOOP
2023-12-21 23:25:32 +00:00
} else if ( display [ y ] [ x ] . attr === null ) {
2022-12-09 06:19:33 +00:00
// NOOP
} else {
try {
2023-12-21 23:25:32 +00:00
write ( ( last === display [ y ] [ x ] . attr ) ? '' : display [ y ] [ x ] . attr ) ;
2022-12-09 06:19:33 +00:00
} catch ( e ) {
writeln ( ) ;
writeln ( 'error:' + e ) ;
writeln ( ' y:' + y ) ;
writeln ( ' x:' + x ) ;
2023-12-21 23:25:32 +00:00
writeln ( JSON . stringify ( display [ y ] [ x ] . attr ) ) ;
2022-12-09 06:19:33 +00:00
exit ( 1 ) ;
}
}
write ( ':' ) ;
2023-12-21 23:25:32 +00:00
if ( display [ y ] [ x ] . ch === undefined ) {
2022-12-09 06:19:33 +00:00
// NOOP - No window filled a character at this location
2023-12-21 23:25:32 +00:00
write ( ( display [ y ] [ x ] . attr === undefined ) ? '--' : '' ) ;
} else if ( display [ y ] [ x ] . ch === null ) {
2022-12-09 06:19:33 +00:00
// NOOP
} else {
2023-12-21 23:25:32 +00:00
write ( '_' + display [ y ] [ x ] . ch ) ;
2022-12-09 06:19:33 +00:00
}
write ( ']' ) ;
2023-12-21 23:25:32 +00:00
last = display [ y ] [ x ] . attr ;
2022-12-09 06:19:33 +00:00
xx ++ ;
}
writeln ( '|' + padright ( xx - 1 , 2 , 0 ) ) ;
xx = 0 ;
yy ++ ;
}
// Detail dump when debug is a line number
if ( debug && ( y > debug ) ) {
writeln ( '==========================' ) ;
for ( var y in display ) {
writeln ( '------ [' + y + '] -------' ) ;
var xx = 1 ;
for ( var x in display [ y ] ) {
2023-12-21 23:25:32 +00:00
var attr = display [ y ] [ x ] . attr ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
writeln ( 'X:' + ( xx ++ ) + '|' + attr + ':' + display [ y ] [ x ] . ch + '|' + display [ y ] [ x ] . attribute ( last , SESSION _EXT , debug ) ) ;
2022-12-09 06:19:33 +00:00
// Only write a new attribute if it has changed
if ( ( this . last === undefined ) || ( this . last !== attr ) ) {
this . last = attr ;
}
}
}
}
writeln ( '==== END content dump ====' ) ;
}
// Dump Header
write ( '--:' ) ;
for ( x = 0 ; x < this . width ; x += 10 ) {
write ( '_' . repeat ( 4 ) + '.' + '_' . repeat ( 4 ) + '|' ) ;
}
writeln ( ) ;
var result = this . display ( last , color ) ;
// We draw line by line.
for ( var y = 1 ; y <= this . height ; y ++ ) {
// Line intro
if ( color )
write ( '\x1b[0m' ) ;
write ( padright ( y , 2 , 0 ) + ':' ) ;
writeln ( result [ y - 1 ] ) ;
}
// Dump Header
write ( '--:' ) ;
for ( x = 0 ; x < this . width ; x += 10 ) {
write ( '_' . repeat ( 4 ) + '.' + '_' . repeat ( 4 ) + '|' ) ;
}
writeln ( ) ;
if ( this . input _fields . length ) {
writeln ( '= Input Fields:' )
this . input _fields . forEach ( function ( x ) {
writeln ( ' - ' + x . name + ', type:' + x . type + ', length:' + x . length + ', value:' + x . value ) ;
} )
}
if ( this . dynamic _fields . length ) {
writeln ( '= Dynamic Fields:' )
this . dynamic _fields . forEach ( function ( x ) {
writeln ( ' - ' + x . name + ', length:' + Math . abs ( x . length ) + ', pad:' + x . pad + ', value:' + x . value ) ;
} )
}
// Reset our color
if ( color )
write ( '\x1b[0m' ) ;
}
/ * *
* Set the value for a dynamic field
*
* @ param field
* @ param value
* /
this . dynamic _field = function ( field , value ) {
var fields = this . dynamic _fields . filter ( function ( item ) { return item . name === field ; } ) ;
if ( fields . length !== 1 )
throw new Error ( 'Dynamic field: ' + field + ', doesnt exist?' ) ;
// Store our value
this . dynamic _fields [ this . dynamic _fields . indexOf ( fields [ 0 ] ) ] . value = value ;
}
2023-12-24 06:44:02 +00:00
/ * *
* Save the frame for later retrieval
* @ todo Inject back all input _fields and dynamic _fields
* @ todo this is not complete ?
* /
this . export = function ( ) {
var line ;
// If we have any input fields, we need to insert them back inside ESC .... ESC \ control codes
// @todo avoid the ending ESC \ with a control code.
this . input _fields . filter ( function ( child ) {
if ( child . y === y ) {
line . content = line . content . substring ( 0 , child . x - 1 )
+ 'FIELD:' + child . name
+ line . content . substring ( child . x + child . length , 80 )
+ 'END' ;
}
} )
// We draw line by line.
for ( var y = 1 ; y <= this . height ; y ++ ) {
// Line intro
write ( '\x1b[0m' ) ;
line = this . _ _window _ _ . layout . drawline ( 1 , this . width , y , false ) ;
write ( line . content ) ;
write ( '\x1b[0m' ) ;
writeln ( ) ;
}
}
2022-12-09 06:19:33 +00:00
/ * *
* Load a specific page
*
* @ param page
* /
2023-12-24 06:44:02 +00:00
this . get = function ( page ) {
2022-12-09 06:19:33 +00:00
if ( ! ( page instanceof PageObject ) )
throw new Error ( 'page must be a PageObject' ) ;
2024-01-01 06:03:25 +00:00
// Load a page from disk first if it exists
if ( FRAMES _MSG _FILES && this . import ( FRAMES _HOME + SESSION _EXT + '/' + page . toString ( ) ) )
2023-12-24 06:44:02 +00:00
return true ;
2024-01-01 06:03:25 +00:00
// Fall back to loading from msgbase
return FRAMES _MSG _BASE ? this . load ( page ) : false ;
2022-12-09 06:19:33 +00:00
}
/ * *
* Set the value for an input field
*
* @ param field
* @ param value
* /
this . input _field = function ( field , value ) {
var fields = this . input _fields . filter ( function ( item ) { return item . name === field ; } ) ;
if ( fields . length !== 1 )
throw new Error ( 'Input field: ' + field + ', doesnt exist?' ) ;
// Store our value
this . input _fields [ this . input _fields . indexOf ( fields [ 0 ] ) ] . value = value ;
}
/ * *
* Load a frame from a file
*
2023-12-21 23:25:32 +00:00
* @ param filename - Name of file to load page from ( SBBS default dir is CTRL , so relative to it )
2022-12-09 06:19:33 +00:00
* @ param width - Width to build window ( not required for ANS )
* @ param height - Height to build window ( not required for ANS )
* @ returns { boolean }
* @ todo Dont allow load ( ) to load a Viewdata frame for an ANSItex session and visa - versa .
* /
2023-12-21 23:25:32 +00:00
this . import = function ( filename , width , height ) {
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|- Importing frame: [' + filename + ']' ) ;
2022-12-09 06:19:33 +00:00
var f = new File ( filename ) ;
2023-12-21 23:25:32 +00:00
if ( ! f . exists || ! f . open ( 'rb' , true ) ) {
2024-01-01 06:03:25 +00:00
log ( LOG _ERROR , '|? File doesnt exist: [' + filename + ']' ) ;
2022-12-09 06:19:33 +00:00
return null ;
2023-12-21 23:25:32 +00:00
}
2022-12-09 06:19:33 +00:00
var contents = f . read ( ) ;
f . close ( ) ;
var valid _sauce = false ;
2024-01-01 06:03:25 +00:00
var ext = SESSION _EXT ;
2022-12-09 06:19:33 +00:00
if ( contents . substr ( - 128 , 7 ) === 'SAUCE00' ) {
2024-01-01 06:03:25 +00:00
ext = file _getext ( filename ) . substr ( 1 ) . toLowerCase ( ) ;
2022-12-09 06:19:33 +00:00
var sauceless _size = ascii ( contents . substr ( - 35 , 1 ) ) ;
sauceless _size <<= 8 ;
sauceless _size |= ascii ( contents . substr ( - 36 , 1 ) ) ;
sauceless _size <<= 8 ;
sauceless _size |= ascii ( contents . substr ( - 37 , 1 ) ) ;
sauceless _size <<= 8 ;
sauceless _size |= ascii ( contents . substr ( - 38 , 1 ) ) ;
var data _type = ascii ( contents . substr ( - 34 , 1 ) ) ;
var file _type = ascii ( contents . substr ( - 33 , 1 ) ) ;
var tinfo1 = ascii ( contents . substr ( - 31 , 1 ) ) ;
tinfo1 <<= 8 ;
tinfo1 |= ascii ( contents . substr ( - 32 , 1 ) ) ;
var tinfo2 = ascii ( contents . substr ( - 29 , 1 ) ) ;
tinfo2 <<= 8 ;
tinfo2 |= ascii ( contents . substr ( - 30 , 1 ) ) ;
switch ( data _type ) {
case 1 :
switch ( file _type ) {
// Plain ASCII
case 0 :
ext = 'TXT' ;
if ( tinfo1 )
width = tinfo1 ;
if ( tinfo2 )
height = tinfo2 ;
break ;
// ANSI
case 1 :
ext = 'ANS' ;
if ( tinfo1 )
width = tinfo1 ;
if ( tinfo2 )
height = tinfo2 ;
break ;
// Source
case 7 :
ext = 'TXT' ;
break ;
}
valid _sauce = true ;
break ;
case 5 :
ext = 'BIN' ;
width = file _type * 2 ;
height = ( sauceless _size / 2 ) / width ;
valid _sauce = true ;
break ;
}
if ( valid _sauce )
contents = contents . substr ( 0 , sauceless _size ) ;
}
2023-12-24 06:44:02 +00:00
return this . preload ( ( [ 'vtx' , 'tex' ] . indexOf ( ext ) !== - 1 ) ? JSON . parse ( contents ) : contents , ext , width , height ) ;
}
2024-01-01 06:03:25 +00:00
this . load = function ( page ) {
var headers ;
var mb = new MsgBase ( FRAMES _MSG _BASE ) ;
try {
if ( mb . open ( ) ) {
headers = mb . get _all _msg _headers ( false , false ) || [ ] ;
} else {
log ( LOG _ERROR , '! [' + FRAMES _MSG _BASE + '] cannot be opened [' + mb . error + ']' ) ;
return false ;
}
// @todo It appears if the message base doesnt exist, we dont error?
} catch ( e ) {
log ( LOG _ERROR , '! [' + FRAMES _MSG _BASE + '] cannot be opened [' + e . message + ']' ) ;
return false ;
}
var msg ;
// Find existing message with the page number and delete it if defined
for ( var x in headers ) {
if ( ( headers [ x ] . tags === page . toString ( ) ) && ( ! ( headers [ x ] . attr & MSG _DELETE ) ) && ( headers [ x ] . from === SESSION _EXT ) ) {
msg = headers [ x ] ;
break ;
}
}
if ( msg === undefined ) {
log ( LOG _DEBUG , '|- Frame not found: [' + page . toString ( ) + '] in [' + FRAMES _MSG _BASE + ']' ) ;
return false ;
} else {
log ( LOG _DEBUG , '|- Loading frame: [' + page . toString ( ) + '] from msgbase [' + msg . number + ']' ) ;
var contents = JSON . parse ( mb . get _msg _body ( false , msg . number , false , false , true , true ) ) ;
return this . preload ( contents , SESSION _EXT ) ;
}
return false ;
}
/ * *
* After page load routines
* /
this . loadcomplete = function ( ) {
var po = pageOwner ( this . name . frame ) ;
switch ( SESSION _EXT ) {
case 'tex' :
this . _ _window _ _ . pagenum . _ _properties _ _ . content = rawtoattrs ( ESC + '[1;37m' + this . name . toString ( ) ) . content ;
this . provider = base64 _decode ( po . logoans ) ;
break ;
case 'vtx' :
this . _ _window _ _ . pagenum . _ _properties _ _ . content = rawtoattrs ( VIEWDATA _BIN _WHITE + this . name . toString ( ) ) . content ;
this . provider = base64 _decode ( po . logovtx ) ;
break ;
default :
throw new Error ( SESSION _EXT + ' hasnt been implemented' ) ;
}
// Dont show header on un-authed login frames
if ( ! user . number )
this . showHeader = false ;
}
2023-12-24 06:44:02 +00:00
/ * *
* Process a loaded frame from either a file or message base
*
* @ param contents
* @ param ext
* @ param width
* @ param height
* @ returns { boolean | null }
* /
this . preload = function ( contents , ext , width , height ) {
2022-12-09 06:19:33 +00:00
switch ( ext ) {
2023-12-27 11:24:20 +00:00
// Messages
case 'txt' :
log ( LOG _DEBUG , 'Processing txt' ) ;
var page = rawtoattrs ( contents , this . width , this . _ _window _ _ . body . y , this . _ _window _ _ . body . x , debug ) ;
this . _ _window _ _ . body . _ _properties _ _ . content = page . content ;
this . _ _properties _ _ . raw = contents ;
2022-12-09 06:19:33 +00:00
// ANSI files
case 'ans' :
2023-12-24 09:11:40 +00:00
// ViewData files
case 'bin' :
log ( LOG _DEBUG , 'Processing ANSI/VIEWDATA file' ) ;
var page = rawtoattrs ( contents , this . width , this . _ _window _ _ . body . y , this . _ _window _ _ . body . x , debug ) ;
2022-12-09 06:19:33 +00:00
2023-12-21 23:25:32 +00:00
this . _ _window _ _ . body . _ _properties _ _ . content = page . content ;
this . dynamic _fields = page . dynamic _fields ;
2022-12-09 06:19:33 +00:00
// Our fields are sorted in x descending order
2023-12-21 23:25:32 +00:00
this . input _fields = page . input _fields . sort ( function ( a , b ) { return a . x < b . x ? 1 : - 1 ; } ) ;
2022-12-09 06:19:33 +00:00
2023-12-27 11:24:20 +00:00
this . _ _properties _ _ . raw = contents ;
2022-12-09 06:19:33 +00:00
break ;
// ANSItex files
case 'tex' :
case 'vtx' :
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|-- Processing FRAME file' ) ;
2022-12-09 06:19:33 +00:00
try {
2023-12-24 06:44:02 +00:00
for ( var index in contents ) {
if ( FRAME _SAVE _ATTRS . indexOf ( index ) === - 1 ) {
2023-12-27 11:24:20 +00:00
log ( LOG _ERROR , '|-! Unknown index [' + index + '] in input.' ) ;
2023-12-24 06:44:02 +00:00
continue ;
}
2022-12-09 06:19:33 +00:00
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|-* Processing [' + index + '] with value [' + JSON . stringify ( contents [ index ] ) + '].' ) ;
2023-12-24 06:44:02 +00:00
switch ( index ) {
2022-12-09 06:19:33 +00:00
case 'content' :
2023-12-27 11:24:20 +00:00
//if (ext === 'tex')
// var page = rawtoattrs(base64_decode(contents[index]).replace("\x0a\x0d\x0a\x0d","\x0a\x0d"),this.width,this.__window__.body.y,this.__window__.body.x);
//else if (ext === 'vtx')
2024-01-01 06:03:25 +00:00
var page = rawtoattrs ( base64 _decode ( contents [ index ] ) , this . width , this . _ _window _ _ . body . y , this . _ _window _ _ . body . x ) ;
2022-12-09 06:19:33 +00:00
2023-12-21 23:25:32 +00:00
this . _ _window _ _ . body . _ _properties _ _ . content = page . content ;
this . dynamic _fields = page . dynamic _fields ;
2023-12-24 06:44:02 +00:00
2022-12-09 06:19:33 +00:00
// Our fields are sorted in x descending order
if ( page . input _fields . length )
2023-12-21 23:25:32 +00:00
this . input _fields = page . input _fields . sort ( function ( a , b ) { return a . x < b . x ? 1 : - 1 ; } ) ;
2023-12-24 06:44:02 +00:00
2023-12-27 11:24:20 +00:00
this . _ _properties _ _ . raw = base64 _decode ( contents [ index ] ) ;
2022-12-09 06:19:33 +00:00
break ;
2023-12-24 06:44:02 +00:00
case 'cost' :
this . cost = contents [ index ] ;
break ;
2022-12-09 06:19:33 +00:00
case 'date' :
2023-12-27 11:24:20 +00:00
log ( LOG _INFO , '|-/ Frame date : ' + contents [ index ] ) ;
2023-12-24 06:44:02 +00:00
break ;
case 'dynamic_fields' :
this . dynamic _fields = contents [ index ] ;
2022-12-09 06:19:33 +00:00
break ;
case 'frame' :
2023-12-24 06:44:02 +00:00
this . name . frame = '' + contents [ index ] ;
break ;
case 'index' :
this . name . index = contents [ index ] ;
2022-12-09 06:19:33 +00:00
break ;
case 'input_fields' :
2023-12-24 06:44:02 +00:00
this . input _fields = contents [ index ] ;
2022-12-09 06:19:33 +00:00
break ;
2023-12-24 06:44:02 +00:00
case 'isAccessible' :
this . isAccessible = ( ( contents [ index ] === 1 ) || ( contents [ index ] === true ) ) ;
break ;
case 'isPublic' :
this . isPublic = ( ( contents [ index ] === 1 ) || ( contents [ index ] === true ) ) ;
break ;
case 'key' :
this . key = contents [ index ] ;
break ;
case 'type' :
this . type = contents [ index ] ;
2022-12-09 06:19:33 +00:00
break ;
case 'version' :
2023-12-27 11:24:20 +00:00
log ( LOG _INFO , '|-/ Frame version : ' + contents [ index ] ) ;
2023-12-24 06:44:02 +00:00
break ;
case 'window' :
for ( var y in contents [ index ] ) {
//log(LOG_DEBUG,' - Y: '+y+', '+JSON.stringify(contents[index][y]));
if ( contents [ index ] [ y ] === null )
continue ;
for ( var x in contents [ index ] [ y ] ) {
//log(LOG_DEBUG,' - X: '+x+', '+JSON.stringify(contents[index][y][x]));
if ( contents [ index ] [ y ] [ x ] === null )
continue ;
this . _ _window _ _ . body . _ _properties _ _ . content [ y ] [ x ] = new Char (
contents [ index ] [ y ] [ x ] . _ _properties _ _ . ch ,
contents [ index ] [ y ] [ x ] . _ _properties _ _ . attr
) ;
}
}
2023-12-24 09:11:40 +00:00
2022-12-09 06:19:33 +00:00
break ;
default :
2023-12-27 11:24:20 +00:00
log ( LOG _ERROR , '|-! Frame property not handled: ' + index + ', value:' + contents [ index ] ) ;
2022-12-09 06:19:33 +00:00
}
}
} catch ( error ) {
2023-12-27 11:24:20 +00:00
log ( LOG _ERROR , '|-! Frame error : ' + error ) ;
2022-12-09 06:19:33 +00:00
// Load our system error frame.
2023-12-24 06:44:02 +00:00
// @todo If our system error page errors, then we go into a loop
this . get ( new PageObject ( FRAME _SYSTEM _ERROR ) ) ;
2022-12-09 06:19:33 +00:00
return null ;
}
this . loadcomplete ( ) ;
2023-12-27 11:24:20 +00:00
log ( LOG _DEBUG , '|= Frame complete : ' + this . name . toString ( ) ) ;
2022-12-09 06:19:33 +00:00
break ;
default :
throw new Error ( 'Unsupported filetype:' + ext ) ;
}
// Successful load
return true ;
}
/ * *
2023-12-24 06:44:02 +00:00
* Save the frame to the message base
2022-12-09 06:19:33 +00:00
* /
this . save = function ( ) {
2023-12-24 06:44:02 +00:00
var mb = new MsgBase ( FRAMES _MSG _BASE ) ;
var headers ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
try {
if ( mb . open ( ) ) {
headers = mb . get _all _msg _headers ( false , false ) || [ ] ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
} else {
log ( LOG _ERROR , FRAMES _MSG _BASE + ' cannot be opened:' + mb . error ) ;
return ;
2022-12-09 06:19:33 +00:00
}
2023-12-24 06:44:02 +00:00
} catch ( e ) {
log ( LOG _ERROR , FRAMES _MSG _BASE + ' cannot be opened:' + e . message ) ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
return ;
}
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
// Build the save content
var content = { } ;
2022-12-09 06:19:33 +00:00
2023-12-24 06:44:02 +00:00
for ( var index in FRAME _SAVE _ATTRS ) {
switch ( FRAME _SAVE _ATTRS [ index ] ) {
case 'cost' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . cost ;
break ;
case 'dynamic_fields' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . dynamic _fields ;
break ;
case 'frame' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . name . frame ;
break ;
case 'index' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . name . index ;
break ;
case 'input_fields' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . input _fields ;
break ;
case 'isAccessible' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . _ _properties _ _ . isAccessible ;
break ;
case 'isPublic' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . _ _properties _ _ . isPublic ;
break ;
case 'key' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . key ;
break ;
case 'type' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . type ;
break ;
case 'version' :
content [ FRAME _SAVE _ATTRS [ index ] ] = 1 ;
break ;
case 'window' :
content [ FRAME _SAVE _ATTRS [ index ] ] = this . _ _window _ _ . body . _ _properties _ _ . content ;
break ;
default :
log ( LOG _ERROR , ' ! NOTE Index [' + FRAME _SAVE _ATTRS [ index ] + '] has been ignored.' ) ;
continue ;
}
log ( LOG _DEBUG , ' / Storing [' + FRAME _SAVE _ATTRS [ index ] + '] with value:' + content [ FRAME _SAVE _ATTRS [ index ] ] ) ;
}
// Find existing message with the page number and delete it if defined
var msg ;
for ( var x in headers ) {
if ( ( headers [ x ] . tags === this . name . toString ( ) ) && ( ! ( headers [ x ] . attr & MSG _DELETE ) ) ) {
msg = headers [ x ] ;
break ;
}
}
if ( msg === undefined ) {
log ( LOG _DEBUG , ' - Saving NEW frame: [' + this . name . toString ( ) + '] to [' + FRAMES _MSG _BASE + ']' ) ;
} else {
log ( LOG _DEBUG , ' - REPLACING frame: [' + this . name . toString ( ) + '] at [' + msg . number + ']' ) ;
if ( ! mb . remove _msg ( msg . number ) )
log ( LOG _ERROR , ' ! Error removing frame: [' + this . name . toString ( ) + '] to [' + msg . number + ']' ) ;
2022-12-09 06:19:33 +00:00
}
2023-12-24 06:44:02 +00:00
log ( LOG _DEBUG , '** Save frame with keys' + JSON . stringify ( Object . keys ( content ) ) ) ;
if ( ! mb . save _msg (
{
subject : this . name . toString ( ) ,
to : this . name . toString ( ) ,
from : SESSION _EXT ,
tags : this . name . toString ( ) ,
} ,
JSON . stringify ( content )
) )
log ( LOG _ERROR , ' ! Error saving frame: [' + this . name . toString ( ) + ']' ) ;
mb . close ( ) ;
2022-12-09 06:19:33 +00:00
}
2023-12-21 23:25:32 +00:00
2023-12-27 11:24:20 +00:00
this . scroll = function ( x , y ) {
this . _ _compiled _ _ . build = null ;
// @todo Check that we can scroll and if we are out of bounds.
this . _ _window _ _ . body . scroll ( x , y ) ;
}
2023-12-21 23:25:32 +00:00
init . apply ( this , arguments ) ;
2022-12-09 06:19:33 +00:00
}
function PageObject ( frame , index ) {
this . _ _properties _ _ = {
frame : '0' , // Frame number
index : 'a' , // Frame index
}
function init ( frame , index ) {
if ( typeof frame === 'object' ) {
2024-01-01 06:03:25 +00:00
this . _ _properties _ _ . frame = frame . frame . toString ( ) ;
2023-12-21 23:25:32 +00:00
this . index = frame . index ;
2022-12-09 06:19:33 +00:00
} else {
this . _ _properties _ _ . frame = frame ;
2023-12-21 23:25:32 +00:00
if ( index )
this . index = index ;
2022-12-09 06:19:33 +00:00
}
}
PageObject . prototype . _ _defineGetter _ _ ( 'frame' , function ( ) {
return this . _ _properties _ _ . frame ;
} ) ;
// @todo validate that string only has digits
PageObject . prototype . _ _defineSetter _ _ ( 'frame' , function ( string ) {
if ( typeof string !== 'string' )
throw new Error ( 'Page.number must be a string' ) ;
this . _ _properties _ _ . frame = string ;
} ) ;
PageObject . prototype . _ _defineGetter _ _ ( 'index' , function ( ) {
return this . _ _properties _ _ . index ;
} ) ;
PageObject . prototype . _ _defineSetter _ _ ( 'index' , function ( string ) {
if ( typeof string !== 'string' )
throw new Error ( 'Page.index must be a string' ) ;
if ( string . length !== 1 )
throw new Error ( 'Page.index can only be 1 char' ) ;
this . _ _properties _ _ . index = string ;
} ) ;
PageObject . prototype . _ _defineGetter _ _ ( 'next' , function ( ) {
var next = undefined ;
2023-12-21 23:25:32 +00:00
if ( this . index !== 'z' ) {
log ( LOG _DEBUG , 'page_next: Current page:' + this . frame + ', current index:' + this . index ) ;
2022-12-09 06:19:33 +00:00
2023-12-21 23:25:32 +00:00
next = new PageObject ( this . frame , String . fromCharCode ( this . index . charCodeAt ( 0 ) + 1 ) ) ;
2022-12-09 06:19:33 +00:00
}
return next ;
} ) ;
2023-12-24 06:44:02 +00:00
PageObject . prototype . toString = function ( ) {
return ( this . frame && this . index ) ? this . frame + this . index : null ;
}
init . apply ( this , arguments ) ;
2022-12-09 06:19:33 +00:00
}