Compare commits
No commits in common. "ansitex" and "master" have entirely different histories.
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
.idea
|
|
||||||
.editorconfig
|
|
||||||
*.debug
|
|
||||||
dev/
|
|
21
.gitlab-ci.yml
Normal file
21
.gitlab-ci.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
image: docker:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
variables:
|
||||||
|
VERSION: 3.17c-${ARCH}
|
||||||
|
CACHETAG: build-${ARCH}
|
||||||
|
DOCKER_HOST: tcp://docker:2375
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- docker info
|
||||||
|
- docker version
|
||||||
|
- echo "$CI_JOB_TOKEN" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin
|
||||||
|
- ls -alR .
|
||||||
|
|
||||||
|
include: .gitlab-docker-x86_64.yml
|
||||||
|
#include: .gitlab-docker-armv7l.yml
|
16
.gitlab-docker-armv7l.yml
Normal file
16
.gitlab-docker-armv7l.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
armv7l:build:
|
||||||
|
variables:
|
||||||
|
ARCH: armv7l
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
script:
|
||||||
|
- if [ -f init ]; then chmod 500 init; fi
|
||||||
|
- ([ -z "$REFRESH" ] && docker pull ${CI_REGISTRY_IMAGE}:${CACHETAG}) || echo "true"
|
||||||
|
- docker build --cache-from ${CI_REGISTRY_IMAGE}:${CACHETAG} -t ${CI_REGISTRY_IMAGE}:${VERSION} -t ${CI_REGISTRY_IMAGE}:${CACHETAG} .
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${VERSION}
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${CACHETAG}
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- armv7l
|
||||||
|
only:
|
||||||
|
- master
|
17
.gitlab-docker-x86_64.yml
Normal file
17
.gitlab-docker-x86_64.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
x86_64:build:
|
||||||
|
variables:
|
||||||
|
ARCH: x86_64
|
||||||
|
stage: build
|
||||||
|
image: docker:latest
|
||||||
|
script:
|
||||||
|
- if [ -f init ]; then chmod 500 init; fi
|
||||||
|
- ([ -z "$REFRESH" ] && docker pull ${CI_REGISTRY_IMAGE}:${CACHETAG}) || echo "true"
|
||||||
|
- docker build --cache-from ${CI_REGISTRY_IMAGE}:${CACHETAG} -t ${CI_REGISTRY_IMAGE}:${VERSION} -t ${CI_REGISTRY_IMAGE}:${CACHETAG} .
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${VERSION}
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${CACHETAG}
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- x86_64
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
66
Dockerfile
Normal file
66
Dockerfile
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# NAME leenooks/sbbs
|
||||||
|
# VERSION 3.17c
|
||||||
|
|
||||||
|
FROM debian:stretch-slim
|
||||||
|
|
||||||
|
# Base utilities
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -yqq curl procps less zip unzip arj unrar-free lhasa arc zoo logrotate libmozjs185-1.0 cron \
|
||||||
|
&& apt-get -y autoremove \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
# Add in Leenooks' apt repository
|
||||||
|
RUN curl -s http://apt.leenooks.net/setup.sh | sh
|
||||||
|
|
||||||
|
# Add ZeroTier
|
||||||
|
RUN echo "deb http://download.zerotier.com/debian/stretch stretch main" > /etc/apt/sources.list.d/zerotier.list
|
||||||
|
|
||||||
|
# Leenooks Utils
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -yqq --allow-unauthenticated makenl zerotier-one \
|
||||||
|
&& apt-get -y autoremove \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
# Other Utilities
|
||||||
|
RUN echo "deb http://ftp.au.debian.org/debian/ stretch contrib" > /etc/apt/sources.list.d/contrib.list
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -yqq dosemu \
|
||||||
|
&& apt-get -y autoremove \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
# Build SBBS
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -yqq build-essential libnspr4-dev libncurses5-dev libmozjs185-dev cvs wget pkgconf \
|
||||||
|
&& mkdir /opt/sbbs && cd /tmp \
|
||||||
|
&& wget http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/install/GNUmakefile \
|
||||||
|
&& make RELEASE=1 USE_DOSEMU=1 NO_X=1 JSINCLUDE=/usr/include/js JSLIB=mozjs185 SBBSDIR=/opt/sbbs install \
|
||||||
|
&& rm -rf /opt/sbbs/3rdp /opt/sbbs/src \
|
||||||
|
&& mv /opt/sbbs/ctrl /opt/sbbs/ctrl.orig \
|
||||||
|
&& mkdir /opt/sbbs/nodes.orig && mv /opt/sbbs/node[1-4] /opt/sbbs/nodes.orig \
|
||||||
|
&& ln -sf nodes/node1 /opt/sbbs/ \
|
||||||
|
&& ln -sf nodes/node2 /opt/sbbs/ \
|
||||||
|
&& ln -sf nodes/node3 /opt/sbbs/ \
|
||||||
|
&& ln -sf nodes/node4 /opt/sbbs/ \
|
||||||
|
&& find /opt/sbbs -name CVS -type d -exec rm -rf {} + \
|
||||||
|
&& apt-get -y purge build-essential libnspr4-dev libncurses5-dev libmozjs185-dev cvs wget pkgconf \
|
||||||
|
&& apt-get -y autoremove \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
WORKDIR /opt/sbbs
|
||||||
|
ENV SBBSCTRL=/opt/sbbs/ctrl
|
||||||
|
ENV SBBSEXEC=/opt/sbbs/exec
|
||||||
|
ENV PATH=$PATH:${SBBSEXEC}
|
||||||
|
|
||||||
|
COPY init /sbin/init
|
||||||
|
ENTRYPOINT [ "/sbin/init" ]
|
||||||
|
CMD ["sbbs"]
|
||||||
|
|
||||||
|
VOLUME [ "/var/lib/zerotier-one" ]
|
||||||
|
VOLUME [ "/opt/sbbs/data","/opt/sbbs/ctrl","/opt/sbbs/nodes","/opt/sbbs/fido" ]
|
||||||
|
|
||||||
|
# Set the default timezone for the container
|
||||||
|
RUN ln -sf /usr/share/zoneinfo/Australia/Melbourne /etc/localtime
|
@ -1,45 +0,0 @@
|
|||||||
; Videotex Options
|
|
||||||
|
|
||||||
; Our key used to sign frames we send to other systems
|
|
||||||
;gpg_key=0@videotex
|
|
||||||
|
|
||||||
[sqrl]
|
|
||||||
auth_url=https://sqrl.dege.au
|
|
||||||
auth_path=/sqrl/login
|
|
||||||
auth_post=/api/sqrl
|
|
||||||
|
|
||||||
; The prefix configurations need to be kept in sync with other nodes
|
|
||||||
; Frames with no specific owner are owned by this key
|
|
||||||
; [prefix:x] - applies to pages begining with x
|
|
||||||
; key= - the GPG key of the owner (for remote updates)
|
|
||||||
; user= - the local user that can edit these frames (via *04)
|
|
||||||
; logoans= - the service providers logo (for ANSI)
|
|
||||||
; logovtx= - the service providers logo (for VIEWDATA)
|
|
||||||
; code= - For Echomail, the echoarea tag
|
|
||||||
; last_page= - For Echomail, the last page tagged
|
|
||||||
|
|
||||||
[prefix]
|
|
||||||
key=0@videotex
|
|
||||||
logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
|
|
||||||
logovtx=AUECTgNTBEkHdGV4
|
|
||||||
user=1
|
|
||||||
|
|
||||||
; System frames are owned by this key
|
|
||||||
[prefix:9]
|
|
||||||
key=0@videotex
|
|
||||||
logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
|
|
||||||
logovtx=AUECTgNTBEkHdGV4
|
|
||||||
user=1
|
|
||||||
|
|
||||||
; ANSItex Help Pages
|
|
||||||
[prefix:516]
|
|
||||||
key=516@videotex
|
|
||||||
logoans=G1swbRtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleA==
|
|
||||||
logovtx=AUECTgNTBEkHdGV4
|
|
||||||
user=1
|
|
||||||
|
|
||||||
; Private Net
|
|
||||||
[prefix:10010]
|
|
||||||
user=1
|
|
||||||
logoans=UHJpdmF0ZU5ldA==
|
|
||||||
logovtx=UHJpdmF0ZU5ldA==
|
|
24
init
Executable file
24
init
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
NAME="Synchronet BBS"
|
||||||
|
|
||||||
|
export PATH=$PATH:${SBBSEXEC}
|
||||||
|
|
||||||
|
if [ ! -e "${SBBSCTRL}/sbbs.ini" ]; then
|
||||||
|
echo "* Installing SBBS ctrl files into ${SBBSCTRL}"
|
||||||
|
cp -a ${SBBSCTRL}.orig/* ${SBBSCTRL}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "${SBBSCTRL}/../nodes/node1" ]; then
|
||||||
|
echo "* Installing SBBS nodes files into ${SBBSCTRL}/../nodes"
|
||||||
|
cp -Ra ${SBBSCTRL}/../nodes.orig/* ${SBBSCTRL}/../nodes/
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x /usr/sbin/zerotier-one -a -n "${ENABLE_ZT}" ]; then
|
||||||
|
echo "* Starting ZeroTier"
|
||||||
|
mkdir /dev/net && mknod /dev/net/tun -m 666 c 10 200
|
||||||
|
/usr/sbin/zerotier-one -d
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
17
install.txt
17
install.txt
@ -1,17 +0,0 @@
|
|||||||
26 May 2020
|
|
||||||
Installation nodes.
|
|
||||||
|
|
||||||
1. For a new installation - make sure you have logged in first and created your sysop user.
|
|
||||||
- No to creating guest account
|
|
||||||
2. Setup files
|
|
||||||
- Replace answer.msg in text/ (or make a zero byte file)
|
|
||||||
- in mods/ ln -sf ansitex/main.js login.js
|
|
||||||
- in mods/ ln -sf ansitex/logon.js
|
|
||||||
- in mods/ ln -sf ansitex/main.js ansitex.js
|
|
||||||
- in mods/ baja ansitex/ansitex.src && mv ansitex/ansitex.bin .
|
|
||||||
3 - Create Shell: SCFG->Command Shells->Ansitex (optionally limit to FLAGS, but requires (requirement string) ANSI)
|
|
||||||
- logon.js will force users to ansitex shell - to make a ini config setting for this
|
|
||||||
- Optionally System -> New User Values -> Shells ->Ansitex
|
|
||||||
- Optionally Edit users and change their shell.
|
|
||||||
4 - Turn off sysop password required for sysop login
|
|
||||||
- SCFG -> Toggle Options -> Require Sys Pass During Login -> No
|
|
@ -1,7 +0,0 @@
|
|||||||
Key-Type: default
|
|
||||||
Key-Usage: encrypt,sign
|
|
||||||
Name-Real: Your Name
|
|
||||||
Name-Comment: Ansitex Page *???#
|
|
||||||
Name-Email: ???@videotex
|
|
||||||
Expire-Date: 0
|
|
||||||
%commit
|
|
@ -1,82 +0,0 @@
|
|||||||
/**
|
|
||||||
* This control renders echomail
|
|
||||||
*
|
|
||||||
* The system echomail prefix is *1, with echomail pages built from the following page number:
|
|
||||||
* zzzzEEpppp, where:
|
|
||||||
* + zzzz is the zone, zero padded, the zone identifies the message groups
|
|
||||||
* + EE is the echomail area, ie: the message areas
|
|
||||||
* + pppp is the message number, identified by the tag attached to the message header
|
|
||||||
*
|
|
||||||
* (Tags are added to the messages via an external process.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// All controls need a unique variable so that require() can know if the control has already been loaded
|
|
||||||
var CONTROL_ECHOMAIL = '1';
|
|
||||||
|
|
||||||
// Optional debug message so we can see that it is loaded
|
|
||||||
log(LOG_DEBUG,'+ Control ECHOMAIL loaded');
|
|
||||||
|
|
||||||
// A unique method name (same as the control name that is called as new method() on initialisation
|
|
||||||
function echomail(session,pagenum) {
|
|
||||||
log(LOG_DEBUG,' - Loading echomail page:'+pagenum);
|
|
||||||
|
|
||||||
// has this control completed
|
|
||||||
var complete = false;
|
|
||||||
var ready = false;
|
|
||||||
|
|
||||||
function init(session,pagenum) {
|
|
||||||
log(LOG_DEBUG,' - Echomail init('+pagenum+')');
|
|
||||||
|
|
||||||
ready = session.loadMessage(pagenum);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'getName', {
|
|
||||||
get: function () {
|
|
||||||
return 'ECHOMAIL';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'isComplete', {
|
|
||||||
get: function () {
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle the keyboard responses as we receive them.
|
|
||||||
this.handle = function(read) {
|
|
||||||
log(LOG_DEBUG,'Control ECHOMAIL handle() start. ('+read+')');
|
|
||||||
|
|
||||||
switch(read) {
|
|
||||||
case KEY_DOWN:
|
|
||||||
session.page.scroll(0,1);
|
|
||||||
read = '';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KEY_UP:
|
|
||||||
session.page.scroll(0,-1);
|
|
||||||
read = '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.render();
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ready() is called after a control has been initialised, to determine if the control will take the input
|
|
||||||
* or if it is unable to do so
|
|
||||||
*
|
|
||||||
* If ready() returns:
|
|
||||||
* + false, the main programming will return ERR_ROUTE to the user,
|
|
||||||
* + true, the main programming will continue to load the frame, and then pass input to on the next loop handle()
|
|
||||||
*
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
this.ready = function() {
|
|
||||||
log(LOG_DEBUG,'echomail:ready = '+JSON.stringify(ready));
|
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
init.apply(this,arguments);
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
load('ansiedit.js');
|
|
||||||
|
|
||||||
load('frame.js');
|
|
||||||
load('tree.js');
|
|
||||||
load('scrollbar.js');
|
|
||||||
load('event-timer.js');
|
|
||||||
load('graphic.js');
|
|
||||||
|
|
||||||
var CONTROL_FRAMEEDIT = '1';
|
|
||||||
|
|
||||||
function edit(session) {
|
|
||||||
log(LOG_DEBUG,'+ Control EDIT loaded');
|
|
||||||
var complete = false;
|
|
||||||
var inProperty = false;
|
|
||||||
|
|
||||||
function init(session) {
|
|
||||||
log(LOG_DEBUG,' - Edit init()');
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(this,'getName', {
|
|
||||||
get: function() {
|
|
||||||
return 'Frame Edit';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this,'isComplete', {
|
|
||||||
get: function() {
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log(LOG_DEBUG,' - Owner: '+JSON.stringify(fo.owner));
|
|
||||||
|
|
||||||
const frame = new Frame(1,1,console.screen_columns,console.screen_rows,BG_BLACK|LIGHTGRAY);
|
|
||||||
frame.gotoxy(1,1);
|
|
||||||
|
|
||||||
header = '\1n'+fo.pageownerlogo+' '.repeat(fo.settings.FRAME_HEADER-console.strlen(fo.pageownerlogo))+'\1n '+
|
|
||||||
'\1W\1H'+fo.page+' '.repeat(fo.settings.FRAME_PAGENUM-fo.page.length)+' '+
|
|
||||||
'\1G\1H'+' Edit';
|
|
||||||
frame.putmsg(header);
|
|
||||||
frame.open();
|
|
||||||
|
|
||||||
var editor = new ANSIEdit({
|
|
||||||
x: 1,
|
|
||||||
y: 2,
|
|
||||||
width: 80,
|
|
||||||
height: 23,
|
|
||||||
attr: WHITE,
|
|
||||||
//showPosition: true,
|
|
||||||
menuHeading: 'Frame Edit '+fo.page,
|
|
||||||
parentFrame: frame,
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.open();
|
|
||||||
editor.menu.addItem('Properties',properties);
|
|
||||||
editor.menu.addItem('Exit',onexit);
|
|
||||||
editor.menu.addItem('Save & Exit',saveexit);
|
|
||||||
|
|
||||||
var x = new Graphic;
|
|
||||||
x.ANSI = fo.parse(base64_decode(fo.content));
|
|
||||||
log(LOG_DEBUG,' - Fields: '+JSON.stringify(fo.frame_fields));
|
|
||||||
|
|
||||||
const bin = x.BIN;
|
|
||||||
var o = 0; // offset into 'bin'
|
|
||||||
for (var yy = 0; yy < 22; yy++) {
|
|
||||||
for (var xx = 0; xx < 80; xx++) {
|
|
||||||
editor.putChar({
|
|
||||||
x : xx,
|
|
||||||
y : yy,
|
|
||||||
ch : bin.substr(o, 1),
|
|
||||||
attr : bin.substr(o + 1, 1).charCodeAt(0) || BG_BLACK
|
|
||||||
});
|
|
||||||
o = o + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.cycle();
|
|
||||||
frame.cycle();
|
|
||||||
|
|
||||||
this.handle=function(read) {
|
|
||||||
if (! js.terminated) {
|
|
||||||
switch (ascii(read)) {
|
|
||||||
case 26:
|
|
||||||
if (inProperty) {
|
|
||||||
propFrame.close();
|
|
||||||
frame.top();
|
|
||||||
frame.cycle();
|
|
||||||
inProperty = false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
|
|
||||||
case 27:
|
|
||||||
if (inProperty) {
|
|
||||||
log(LOG_DEBUG, ' + FrameEdit properties(): ESC');
|
|
||||||
propFrame.close();
|
|
||||||
frame.top();
|
|
||||||
frame.cycle();
|
|
||||||
inProperty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (inProperty) {
|
|
||||||
log(LOG_DEBUG, ' + FrameEdit properties(): read');
|
|
||||||
propFrame.putmsg(read);
|
|
||||||
propFrame.cycle();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
editor.getcmd(read);
|
|
||||||
editor.cycle();
|
|
||||||
frame.cycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! complete)
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.close();
|
|
||||||
frame.close();
|
|
||||||
fo.render();
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function properties() {
|
|
||||||
inProperty = true;
|
|
||||||
log(LOG_DEBUG, '+ FrameEdit properties()');
|
|
||||||
frame.bottom();
|
|
||||||
propFrame = new Frame(1,2,40,14,BG_BLUE|WHITE,frame);
|
|
||||||
propFrame.gotoxy(1,1);
|
|
||||||
propFrame.putmsg('Properties!');
|
|
||||||
propFrame.open();
|
|
||||||
propFrame.cycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
fo.content = base64_encode(editor.exportAnsi().join(''));
|
|
||||||
fo.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onexit() {
|
|
||||||
complete = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveexit() {
|
|
||||||
save();
|
|
||||||
onexit();
|
|
||||||
}
|
|
||||||
|
|
||||||
init.apply(this,arguments);
|
|
||||||
}
|
|
@ -1,212 +0,0 @@
|
|||||||
/**
|
|
||||||
* This handles user registration.
|
|
||||||
*
|
|
||||||
* The form must have the following fields:
|
|
||||||
* + USER The user's user id to login
|
|
||||||
* + EMAIL The users's email address - to receive tokens
|
|
||||||
* + FULLNAME The user's full name
|
|
||||||
* + PASS The users's preferred password
|
|
||||||
* + CITY The user's city
|
|
||||||
* + COUNTRY The user's country - 3 letter ISO code
|
|
||||||
* + PCODE THe user's postal code
|
|
||||||
*/
|
|
||||||
|
|
||||||
var CONTROL_REGISTER = '1';
|
|
||||||
var EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
||||||
var cValChars='ACDEFHJKLMNPQRTUVWXY23456789!@$%&';
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'+ Control REGISTER loaded');
|
|
||||||
|
|
||||||
function register(session) {
|
|
||||||
var code = '';
|
|
||||||
var complete = false;
|
|
||||||
var processed = false;
|
|
||||||
var ready = false;
|
|
||||||
|
|
||||||
function init(session) {
|
|
||||||
log(LOG_DEBUG,' - Register init()');
|
|
||||||
ready = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called before processing for a field
|
|
||||||
Object.defineProperty(this, 'getName', {
|
|
||||||
get: function () {
|
|
||||||
return 'Control-Registration';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'isComplete', {
|
|
||||||
get: function () {
|
|
||||||
return complete && processed;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.handle = function(read) {
|
|
||||||
// Dont allow existing users to re-register
|
|
||||||
if (user.number) {
|
|
||||||
session.baselineSend('ALREADY_MEMBER',false);
|
|
||||||
return (read === '*') ? read : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Control REGISTER handle() start. ('+read+')');
|
|
||||||
if (cf === undefined) {
|
|
||||||
log(LOG_DEBUG,' - CF not defined, returning');
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'- Field '+cf.name+'('+JSON.stringify(cf)+')');
|
|
||||||
|
|
||||||
if ((cf.name === 'TOKEN') && (read === '#' || read === "\r")) {
|
|
||||||
if (cf.value === code) {
|
|
||||||
complete = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
session.baselineSend('INVALID_CODE',false);
|
|
||||||
session.cursorOn(cf.c+cf.value.length,cf.r);
|
|
||||||
session.attr(cf.attribute);
|
|
||||||
read = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'- Field Value ['+cf.value+'] ('+code+')');
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.prefield = function() {
|
|
||||||
log(LOG_DEBUG,'- prefield: Field '+cf.name+'('+JSON.stringify(cf)+')');
|
|
||||||
|
|
||||||
// Make sure we got an email
|
|
||||||
if (cf.name === 'TOKEN') {
|
|
||||||
if (! code.length) {
|
|
||||||
log(LOG_DEBUG,' - BASELINE '+cf.name+'('+JSON.stringify(cf)+')');
|
|
||||||
session.baselineSend('TOKEN_EMAIL',false);
|
|
||||||
|
|
||||||
var email = session.fieldValue('EMAIL');
|
|
||||||
var uid = session.fieldValue('USER');
|
|
||||||
var name = session.fieldValue('FULLNAME');
|
|
||||||
|
|
||||||
log(LOG_DEBUG,' - VALIDATE EMAIL TO ('+JSON.stringify(system.matchuserdata(U_NETMAIL,email))+')');
|
|
||||||
|
|
||||||
// Validate Email hasnt been used
|
|
||||||
// Validate USER_ID hasnt been used
|
|
||||||
if ((email.indexOf('@') === -1) || ! EMAIL_REGEX.test(email) || (system.matchuserdata(U_NETMAIL,email) !== 0)) {
|
|
||||||
session.baselineSend('INVALID_EMAIL',false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! system.check_name(uid)) {
|
|
||||||
log(LOG_DEBUG,' - Cannot use user_id: ('+uid+')');
|
|
||||||
session.baselineSend('INVALID_UID',false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgbase = new MsgBase('mail');
|
|
||||||
for (var i=0;i<6;i++) {
|
|
||||||
code += cValChars.substr(parseInt(Math.random()*cValChars.length),1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hdrs = new Object();
|
|
||||||
hdrs.to = name;
|
|
||||||
hdrs.to_net_type = netaddr_type(email);
|
|
||||||
|
|
||||||
if (hdrs.to_net_type !== NET_NONE) {
|
|
||||||
hdrs.to_net_addr = email;
|
|
||||||
} else {
|
|
||||||
session.baselineSend('CANNOT_SEND_TOKEN',false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdrs.from = system.name;
|
|
||||||
hdrs.from_net_addr = 'sysop@'+system.inet_addr;
|
|
||||||
hdrs.from_net_type = NET_INTERNET;
|
|
||||||
hdrs.subject = 'Registration TOKEN for '+system.name;
|
|
||||||
|
|
||||||
if (msgbase.open !== undefined && msgbase.open() === false) {
|
|
||||||
console.print("\r\n\1n\1h\1rERROR: \1y" + msgbase.last_error + "\1n \r\n");
|
|
||||||
console.pause();
|
|
||||||
msgbase.close();
|
|
||||||
bbs.hangup();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg="CODE: " + code + "\n\n";
|
|
||||||
msg += 'Please use the above code to validate your login to '+system.name+'.';
|
|
||||||
|
|
||||||
if (! msgbase.save_msg(hdrs,msg)) {
|
|
||||||
console.print("\r\n\1n\1h\1rERROR: \1y" + msgbase.last_error + "\1n \r\n");
|
|
||||||
console.pause();
|
|
||||||
msgbase.close();
|
|
||||||
bbs.hangup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
msgbase.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
session.baselineSend('TOKEN_SENT',false);
|
|
||||||
log(LOG_DEBUG,'SENT EMAIL TOKEN ('+code+') ['+JSON.stringify(hdrs)+']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.process = function() {
|
|
||||||
log(LOG_DEBUG,'Creating user: ['+session.fieldValue('EMAIL')+'] ['+session.fieldValue('USER')+']');
|
|
||||||
|
|
||||||
try {
|
|
||||||
var newuser = system.new_user(session.fieldValue('USER'));
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
session.baselineSend('USER_EXISTS',false);
|
|
||||||
log(LOG_ERROR,"New user couldn't be created (user created while signing up)");
|
|
||||||
log(LOG_ERROR,JSON.stringify(e));
|
|
||||||
processed = true;
|
|
||||||
return this.isComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof newuser === 'number') {
|
|
||||||
session.baselineSend('USER_CREATE_ERROR',false);
|
|
||||||
log(LOG_ERROR,"New user couldn't be created (error code "+newuser+")");
|
|
||||||
processed = true;
|
|
||||||
return this.isComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
newuser.security.password = '';
|
|
||||||
if (bbs.login(newuser.alias,null)) {
|
|
||||||
user.number = newuser.number;
|
|
||||||
user.security.password = session.fieldValue('PASS');
|
|
||||||
user.name = session.fieldValue('FULLNAME');
|
|
||||||
user.handle = session.fieldValue('USER');
|
|
||||||
user.location = session.fieldValue('CITY')+', '+session.fieldValue('COUNTRY');
|
|
||||||
user.zipcode = session.fieldValue('PCODE');
|
|
||||||
user.netmail = session.fieldValue('EMAIL');
|
|
||||||
user.comment = 'ANSItex registered user';
|
|
||||||
bbs.user_sync();
|
|
||||||
bbs.logon();
|
|
||||||
|
|
||||||
log(LOG_INFO,"Created user record #"+user.number+": "+user.alias);
|
|
||||||
|
|
||||||
action = ACTION_EXIT;
|
|
||||||
processed = true;
|
|
||||||
return this.isComplete;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
session.baselineSend('LOGIN_ERROR',false);
|
|
||||||
log(LOG_INFO,"bbs.login() failed");
|
|
||||||
user.comment = 'Initial login failed!';
|
|
||||||
newuser.settings |= USER_DELETED;
|
|
||||||
delete newuser;
|
|
||||||
processed = true;
|
|
||||||
|
|
||||||
return this.isComplete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo Doesnt appear to be used
|
|
||||||
this.ready = function() {
|
|
||||||
log(LOG_DEBUG,'register:ready = '+JSON.stringify(ready));
|
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
init.apply(this,arguments);
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
/**
|
|
||||||
* This handles user registration.
|
|
||||||
*
|
|
||||||
* The form must have the following fields:
|
|
||||||
* + UID The user's user id to login
|
|
||||||
* + EMAIL The users's email address - to receive tokens
|
|
||||||
* + FULLNAME The user's full name
|
|
||||||
* + PASS The users's preferred password
|
|
||||||
* + CITY The user's city
|
|
||||||
* + COUNTRY The user's country - 3 letter ISO code
|
|
||||||
* + PCODE THe user's postal code
|
|
||||||
*/
|
|
||||||
|
|
||||||
var CONTROL_SQRL = '1';
|
|
||||||
|
|
||||||
require('http.js','HTTPRequest');
|
|
||||||
load('ansitex/load/qrcode-make.js');
|
|
||||||
load('frame.js');
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'+ Control SQRL-LOGIN loaded');
|
|
||||||
|
|
||||||
// @todo This should move to handle - since we have to press 2 twice to get out.
|
|
||||||
function sqrllogin() {
|
|
||||||
var complete = false;
|
|
||||||
var cancel = false;
|
|
||||||
|
|
||||||
var sqrl = loadOptions('sqrl');
|
|
||||||
log(LOG_DEBUG,'OPTIONS: '+JSON.stringify(sqrl));
|
|
||||||
var http = new HTTPRequest();
|
|
||||||
http.SetupGet(sqrl.auth_path,undefined,sqrl.auth_url);
|
|
||||||
http.request_headers.push('Accept: application/json');
|
|
||||||
|
|
||||||
try {
|
|
||||||
http.SendRequest();
|
|
||||||
http.ReadResponse();
|
|
||||||
log(LOG_INFO,'SQRL: '+JSON.stringify(http.body));
|
|
||||||
|
|
||||||
var data = http.body
|
|
||||||
.split('')
|
|
||||||
.map(function(x) {return x.charCodeAt(0)});
|
|
||||||
var qr = qrcodegen.QrCode.encodeBinary(data,qrcodegen.QrCode.Ecc.LOW);
|
|
||||||
|
|
||||||
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.baselineSend('CANCEL_MSG',false);
|
|
||||||
|
|
||||||
// Loop and see if the user has logged in
|
|
||||||
var nut = http.body.substr(http.body.indexOf('nut='),68);
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
log(LOG_INFO,'SQRL Error: '+err+' '+JSON.stringify(sqrl));
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called before processing for a field
|
|
||||||
Object.defineProperty(this, 'getName', {
|
|
||||||
get: function () {
|
|
||||||
return 'SQRL-LOGIN';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'isComplete', {
|
|
||||||
get: function () {
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Nothing to do here
|
|
||||||
this.handle=function(read) {
|
|
||||||
log(LOG_DEBUG,'Control SQRL-LOGIN handle() start. ('+read+')');
|
|
||||||
|
|
||||||
if (read === '2') {
|
|
||||||
log(LOG_INFO,'SQRL: Cancelled with 2');
|
|
||||||
cancel = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_DEBUG,'SQRL read ['+read+']');
|
|
||||||
|
|
||||||
try {
|
|
||||||
http = new HTTPRequest();
|
|
||||||
http.SetupGet(sqrl.auth_post+'?'+nut,undefined,sqrl.auth_url);
|
|
||||||
http.request_headers.push('Accept: application/json');
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Checking NUT in ['+nut+']');
|
|
||||||
http.SendRequest();
|
|
||||||
http.ReadResponse();
|
|
||||||
|
|
||||||
switch (http.response_code) {
|
|
||||||
case 404:
|
|
||||||
log(LOG_DEBUG,'- NUT not Authorised yet.');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 200:
|
|
||||||
var result = JSON.parse(http.body);
|
|
||||||
|
|
||||||
if (result.isReady) {
|
|
||||||
log(LOG_INFO,'NUT: '+result.msg);
|
|
||||||
log(LOG_INFO,'NEXT: '+result.nextPage);
|
|
||||||
if (result.msg === 'SQRL authenticated') {
|
|
||||||
log(LOG_DEBUG,'Getting Authenticated User ['+result.nextPage+']');
|
|
||||||
http = new HTTPRequest();
|
|
||||||
http.SetupGet(result.nextPage,undefined,'');
|
|
||||||
http.request_headers.push('Accept: application/json');
|
|
||||||
|
|
||||||
http.SendRequest();
|
|
||||||
http.ReadResponse();
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Getting Authenticated User Response ['+http.response_code+']');
|
|
||||||
if (http.response_code === 200) {
|
|
||||||
var sqrluser = http.body.substr(0,40);
|
|
||||||
var username = 'S'+sqrluser.substr(0,24)
|
|
||||||
log(LOG_DEBUG,'Getting Authenticated sqrluser ['+JSON.stringify(sqrluser)+']');
|
|
||||||
|
|
||||||
// Look through our user base for an existing user
|
|
||||||
var uid = system.matchuser(username);
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Getting Authenticated UID ['+JSON.stringify(uid)+']');
|
|
||||||
if (! uid) {
|
|
||||||
log(LOG_DEBUG,'New User ['+username+'] with pass ('+sqrluser+')');
|
|
||||||
var user = system.new_user(username);
|
|
||||||
log(LOG_DEBUG,'New User ['+JSON.stringify(user.number)+']');
|
|
||||||
user.name = username;
|
|
||||||
user.security.password = sqrluser;
|
|
||||||
user.handle = username.substr(0,8);
|
|
||||||
user.location = 'Earth';
|
|
||||||
user.zipcode = '000';
|
|
||||||
user.netmail = username+'@'+system.inet_addr;
|
|
||||||
|
|
||||||
user.comment = 'ANSItex registered user - with SQRL';
|
|
||||||
bbs.user_sync();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
user = new User(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Getting Authenticated USER ['+JSON.stringify(user.number)+']');
|
|
||||||
|
|
||||||
// Existing user, we'll exit here
|
|
||||||
if (bbs.login(user.name,null,user.security.password)) {
|
|
||||||
log(LOG_DEBUG,' - User:'+JSON.stringify(user.number));
|
|
||||||
bbs.logon();
|
|
||||||
log(LOG_DEBUG,' - SEND TO EXIT:');
|
|
||||||
|
|
||||||
complete = true;
|
|
||||||
action = ACTION_EXIT;
|
|
||||||
if (typeof subframe === 'object')
|
|
||||||
subframe.close();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'- Login Failed? ');
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'- Unhandled User Details: '+http.response_code);
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'- Unhandled isReady msg: '+result.msg);
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
complete = true;
|
|
||||||
if (typeof subframe === 'object')
|
|
||||||
subframe.close();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'- Unhandled isReady: '+result.isReady);
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
log(LOG_ERROR,'- Unhandled response code: '+http.response_code);
|
|
||||||
|
|
||||||
// We are done
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
log(LOG_INFO,'SQRL Error: '+err+' '+JSON.stringify(sqrl));
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancel) {
|
|
||||||
log(LOG_INFO,'SQRL: Processing CANCEL ['+read+'].');
|
|
||||||
complete = true;
|
|
||||||
if (typeof subframe === 'object')
|
|
||||||
subframe.close();
|
|
||||||
|
|
||||||
action = ACTION_GOTO;
|
|
||||||
next_page = FRAME_LOGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this;
|
|
149
load/defs.js
149
load/defs.js
@ -1,149 +0,0 @@
|
|||||||
/**
|
|
||||||
* ANSItex definitions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Our home on disk */
|
|
||||||
const ANSITEX_HOME = system.mods_dir+'ansitex';
|
|
||||||
/* Frames location */
|
|
||||||
const FRAMES_HOME = ANSITEX_HOME+'/text/';
|
|
||||||
/* Load frames from msgbase */
|
|
||||||
const FRAMES_MSG_BASE = 'vtx_data';
|
|
||||||
/* Load frames from files */
|
|
||||||
const FRAMES_MSG_FILES = true;
|
|
||||||
|
|
||||||
/* Unit of cost */
|
|
||||||
const FRAME_COSTUNIT = 'c';
|
|
||||||
|
|
||||||
/** ACTIONS **/
|
|
||||||
|
|
||||||
/* Exit the script */
|
|
||||||
const ACTION_EXIT = 99;
|
|
||||||
/* Reload the current frame */
|
|
||||||
const ACTION_RELOAD = 1;
|
|
||||||
/* Goto a specific frame */
|
|
||||||
const ACTION_GOTO = 2;
|
|
||||||
/* Goto previous frame */
|
|
||||||
const ACTION_BACKUP = 3;
|
|
||||||
/* Goto next frame */
|
|
||||||
const ACTION_NEXT = 4;
|
|
||||||
/* Terminate the session */
|
|
||||||
const ACTION_TERMINATE = 5;
|
|
||||||
/* Submit form contents */
|
|
||||||
const ACTION_SUBMITRF = 6;
|
|
||||||
/* Star command entry */
|
|
||||||
const ACTION_STAR = 7;
|
|
||||||
/* Edit a frame */
|
|
||||||
const ACTION_EDIT = 8;
|
|
||||||
|
|
||||||
/** MODES **/
|
|
||||||
|
|
||||||
/* Typing * command on baseline */
|
|
||||||
const MODE_BL = 1;
|
|
||||||
/* Field Input */
|
|
||||||
const MODE_FIELD = 2;
|
|
||||||
/* Asking if form should be submitted */
|
|
||||||
const MODE_SUBMITRF = 3;
|
|
||||||
/* Response frame not sent */
|
|
||||||
const MODE_RFNOTSENT = 4;
|
|
||||||
/* Response frame sent */
|
|
||||||
const MODE_RFSENT = 5;
|
|
||||||
/* Response frame error */
|
|
||||||
const MODE_RFERROR = 6;
|
|
||||||
|
|
||||||
/** FRAME TYPES **/
|
|
||||||
|
|
||||||
/* Information Frame, requires no response after viewed */
|
|
||||||
const FRAME_TYPE_INFO = 'i';
|
|
||||||
/* Terminate Frame, contents displayed and then carrier dropped */
|
|
||||||
const FRAME_TYPE_TERMINATE = 't';
|
|
||||||
/**
|
|
||||||
* Frame the calls an External Method
|
|
||||||
* Contents indicate the method to be called with arguments
|
|
||||||
*/
|
|
||||||
const FRAME_TYPE_EXTERNAL = 'x';
|
|
||||||
/**
|
|
||||||
* Frame calls a door
|
|
||||||
* Contents indicate the name of the door
|
|
||||||
*/
|
|
||||||
const FRAME_TYPE_DOOR = 'X';
|
|
||||||
/**
|
|
||||||
* Frame renders a BBS from sbbslist
|
|
||||||
* Contents can be:
|
|
||||||
* + preview(bbs) - to show the BBS preview display
|
|
||||||
* + telnet(bbs) - to telgate to the BBS
|
|
||||||
*/
|
|
||||||
const FRAME_TYPE_BBS = 'b';
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
const FRAME_TYPE_RESPONSE = 'r';
|
|
||||||
/* Login frame, enables the user to authenticate to the system, or to a CUG */
|
|
||||||
const FRAME_TYPE_LOGIN = 'l';
|
|
||||||
/* Mail template frames - mail templates will have the user's stats for the area passed to render() */
|
|
||||||
const FRAME_TYPE_MAIL_TEMPLATE = 'm';
|
|
||||||
/* Frame is a message */
|
|
||||||
const FRAME_TYPE_MESSAGE = 'M';
|
|
||||||
|
|
||||||
/* Disable *# going backwards for the following frames */
|
|
||||||
const FRAMES_NO_HISTORY = ['980a','98b','981a','982a','983a','998a'];
|
|
||||||
|
|
||||||
/* Frames prefixed with this are owned by the system */
|
|
||||||
const SYSTEM_OWNER = 9;
|
|
||||||
// @todo can we get this from the message base?
|
|
||||||
const SYSTEM_ZONE = 516;
|
|
||||||
|
|
||||||
/* Time to wait for a key press */
|
|
||||||
const INACTIVE_TIMEOUT = 100000;
|
|
||||||
/* Idle time for un-authenticated users */
|
|
||||||
const INACTIVE_NOLOGIN = 30000;
|
|
||||||
/* Idle time for authenticated users */
|
|
||||||
const INACTIVE_LOGIN = 5*60000;
|
|
||||||
|
|
||||||
/* Home Frame */
|
|
||||||
const FRAME_HOME = {frame: '1',index: 'a'};
|
|
||||||
/* Login Frame */
|
|
||||||
const FRAME_LOGIN = {frame: '98',index: 'a'};
|
|
||||||
/* Registration Frame */
|
|
||||||
const FRAME_REGISTER = {frame: '981',index: 'a'};
|
|
||||||
/* SQRL Login */
|
|
||||||
const FRAME_SQRL = {frame: '982',index: 'a'};
|
|
||||||
/* Login Failed */
|
|
||||||
const FRAME_LOGIN_FAILED = {frame: '983',index: 'a'};
|
|
||||||
/* Home page after authentication */
|
|
||||||
const FRAME_HOME_AUTH = {frame: '98',index: 'b'};
|
|
||||||
/* Home page for initial connection */
|
|
||||||
const FRAME_HOME_CONNECT = {frame: '980',index: 'a'};
|
|
||||||
const FRAME_SYSTEM_ERROR = {frame: '998',index: 'a'};
|
|
||||||
|
|
||||||
/* Attributes saved/loaded from files */
|
|
||||||
const FRAME_SAVE_ATTRS = [
|
|
||||||
'content', // raw source content of a frame (as stored in vtx/tex files)
|
|
||||||
'cost', // integer, frame cost
|
|
||||||
'dynamic_fields', // array of fields
|
|
||||||
'frame', // Page ID,
|
|
||||||
'index', // Page index,
|
|
||||||
'input_fields', // array of fields
|
|
||||||
'isAccessible', // boolean
|
|
||||||
'isPublic', // boolean
|
|
||||||
'key', // array, representing our key actions
|
|
||||||
'type', // frame type
|
|
||||||
'version', // frame version (1)
|
|
||||||
'window' // processed frame data
|
|
||||||
];
|
|
||||||
|
|
||||||
/* The page that has our echomail area reading template */
|
|
||||||
const MAIL_TEMPLATE_FRAME = {frame: '199',index: 'a'};
|
|
||||||
|
|
||||||
/* The page that has our echomail area summary template */
|
|
||||||
const MAIL_TEMPLATE_AREA_SUMMARY = {frame: '198',index: 'a'};
|
|
||||||
|
|
||||||
// The maximum size of embedded dynamic fields in frames
|
|
||||||
const DYNAMIC_FIELD_SIZE_MAX = 50;
|
|
||||||
|
|
||||||
/** ESCAPE CODES **/
|
|
||||||
const ESC = '\x1b';
|
|
||||||
|
|
||||||
const FIELD_PASSWORD_MASK = '*';
|
|
||||||
const FIELD_TEXT = 't';
|
|
||||||
const FIELD_PASSWORD = 'p';
|
|
533
load/funcs.js
533
load/funcs.js
@ -1,533 +0,0 @@
|
|||||||
require('ansitex/load/defs.js','ANSITEX_HOME'); // ANSITEX definitions
|
|
||||||
|
|
||||||
// Array of page owners
|
|
||||||
pageowners = [];
|
|
||||||
|
|
||||||
// String repeat.
|
|
||||||
if (!String.prototype.repeat) {
|
|
||||||
String.prototype.repeat = function(count) {
|
|
||||||
'use strict';
|
|
||||||
if (this === null) {
|
|
||||||
throw new TypeError('can\'t convert ' + this + ' to object');
|
|
||||||
}
|
|
||||||
var str = '' + this;
|
|
||||||
count = +count;
|
|
||||||
if (count !== count) {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
if (count < 0) {
|
|
||||||
throw new RangeError('repeat count must be non-negative: '+count);
|
|
||||||
}
|
|
||||||
if (count === Infinity) {
|
|
||||||
throw new RangeError('repeat count must be less than infinity');
|
|
||||||
}
|
|
||||||
count = Math.floor(count);
|
|
||||||
if (str.length === 0 || count === 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// Ensuring count is a 31-bit integer allows us to heavily optimize the
|
|
||||||
// main part. But anyway, most current (August 2014) browsers can't handle
|
|
||||||
// strings 1 << 28 chars or longer, so:
|
|
||||||
if (str.length * count >= 1 << 28) {
|
|
||||||
throw new RangeError('repeat count must not overflow maximum string size');
|
|
||||||
}
|
|
||||||
var rpt = '';
|
|
||||||
for (;;) {
|
|
||||||
if ((count & 1) === 1) {
|
|
||||||
rpt += str;
|
|
||||||
}
|
|
||||||
count >>>= 1;
|
|
||||||
if (count === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
str += str;
|
|
||||||
}
|
|
||||||
return rpt;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert ANSI into BIN for loading into a Frame
|
|
||||||
*
|
|
||||||
* @param ansi
|
|
||||||
*/
|
|
||||||
function ans2bin(ansi,frame) {
|
|
||||||
var x = new Graphic;
|
|
||||||
x.ANSI = ansi;
|
|
||||||
|
|
||||||
var o = 0; // offset into 'bin'
|
|
||||||
for (var yy = 0; yy < 22; yy++) {
|
|
||||||
for (var xx = 0; xx < 80; xx++) {
|
|
||||||
frame.setData(
|
|
||||||
xx,
|
|
||||||
yy,
|
|
||||||
x.BIN.substr(o,1),
|
|
||||||
x.BIN.substr(o+1,1).charCodeAt(0) || BG_BLACK
|
|
||||||
);
|
|
||||||
|
|
||||||
o = o+2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
pad = pad ? pad : ' ';
|
|
||||||
var result = '';
|
|
||||||
var args = [];
|
|
||||||
|
|
||||||
if (field.search(/:/)) {
|
|
||||||
args = field.split(':');
|
|
||||||
field = args.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
//log(LOG_DEBUG,'Field:'+field,'Args:'+JSON.stringify(args));
|
|
||||||
|
|
||||||
switch(field) {
|
|
||||||
// Get the ECHOAREA FTN AREA_TAG
|
|
||||||
case 'msg_area_areatag':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = context.msgbase.cfg.area_tag;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Get the ECHOAREA Description
|
|
||||||
case 'msg_area_desc':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = context.msgbase.cfg.description;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Oldest message in msgarea
|
|
||||||
// Our oldest message, is the first message with a tag from the headers
|
|
||||||
case 'msg_area_msgoldest_date':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.list_tagged[0];
|
|
||||||
|
|
||||||
result = x ? x.date : '';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'msg_area_msgoldest_page':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.list_tagged[0];
|
|
||||||
|
|
||||||
return x ? context.getMessagePage(x.number) : null;
|
|
||||||
|
|
||||||
// Newest message in msgarea
|
|
||||||
// Our newest message, is the last message with a tag from the headers
|
|
||||||
case 'msg_area_msgnewest_date':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.list_tagged[context.list_tagged.length-1];
|
|
||||||
|
|
||||||
result = x ? x.date : '';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'msg_area_msgnewest_page':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.list_tagged[context.list_tagged.length-1];
|
|
||||||
|
|
||||||
return x ? context.getMessagePage(x.number) : null;
|
|
||||||
|
|
||||||
// First unread message
|
|
||||||
case 'msg_area_msgunread_date':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.newMsgs();
|
|
||||||
|
|
||||||
result = x.length ? x.shift().date : '';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'msg_area_msgunread_page':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.newMsgs();
|
|
||||||
return x.length ? context.getMessagePage(x.shift().number) : null;
|
|
||||||
|
|
||||||
// First unread message to me
|
|
||||||
case 'msg_area_msgotome_date':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.newMsgsToMe();
|
|
||||||
|
|
||||||
result = x.length > 1 ? x[1].date : '';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'msg_area_msgtome_page':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = context.newMsgsToMe();
|
|
||||||
return x.length > 1 ? context.getMessagePage(x[1].number) : null;
|
|
||||||
|
|
||||||
// Count of unread messages
|
|
||||||
case 'msg_area_new':
|
|
||||||
if (args.length === 1) {
|
|
||||||
context = new MsgArea();
|
|
||||||
context.code = args[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ''+context.newMsgs().length;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Count of unread messages to me
|
|
||||||
case 'msg_area_newtome':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ''+(context.newMsgsToMe().length > 1 ? context.newMsgsToMe().length-1 : 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Is this message area in my new scan list
|
|
||||||
case 'msg_area_newscan':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (context.getUserStats().scan_ptr & SCAN_CFG_TOYOU) ? 'YES' : 'NO';
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Is this message area in my new scan list
|
|
||||||
case 'msg_area_pending':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ''+context.list_untagged.length;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Get the ECHOAREA Total Number of Messages
|
|
||||||
case 'msg_area_total':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ''+context.msgbase.total_msgs;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Get the ECHOAREA Group Name
|
|
||||||
case 'msg_grp_name':
|
|
||||||
if (typeof context !== 'object') {
|
|
||||||
log(LOG_ERROR,'Unable to render ['+field+'], no context provided');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = context.zone_name;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'nodeid':
|
|
||||||
result = getNodeID();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
result = (typeof bbs === 'undefined') ? '*'.repeat(Math.abs(length)) : bbs.atcode(field+(args.length ? ':'+args : ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((result === null) || (typeof result === 'undefined'))
|
|
||||||
result = '';
|
|
||||||
|
|
||||||
length = length ? length : result.length;
|
|
||||||
|
|
||||||
//log(LOG_DEBUG,' - result length ['+result.length+'] desired ('+length+')');
|
|
||||||
if (result.length < Math.abs(length))
|
|
||||||
result = (length < 0) ? padright(result,Math.abs(length),pad) : padleft(result,length,pad);
|
|
||||||
else if (result.length > Math.abs(length))
|
|
||||||
result = result.substr(0,Math.abs(length));
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'* ATCODE ['+field+'] ('+length+'|"'+pad+'") returns ['+result+']');
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a message base by code
|
|
||||||
*
|
|
||||||
* @param code
|
|
||||||
* @returns {number | string|boolean}
|
|
||||||
* @deprecated Can this move to the msgbases.js?
|
|
||||||
*/
|
|
||||||
function findMsgBase(code) {
|
|
||||||
if (! code)
|
|
||||||
code = "vtx_data";
|
|
||||||
|
|
||||||
for (var s in msg_area.sub) {
|
|
||||||
var sub = msg_area.sub[s];
|
|
||||||
writeln('sub:'+sub.code);
|
|
||||||
|
|
||||||
if (sub.code.substr(-code.length).toLowerCase() === code)
|
|
||||||
return sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an argument from argv, or an error if it doesnt exit
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @param error
|
|
||||||
* @param abort
|
|
||||||
*/
|
|
||||||
function getArg(key,error,abort) {
|
|
||||||
index = argv.indexOf(key);
|
|
||||||
|
|
||||||
if ((index !== -1) && (! (argv[index+1] === undefined || argv[index+1].match(/^-/)))) {
|
|
||||||
return argv[index+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (abort) {
|
|
||||||
log(LOG_ERROR,error);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current node number
|
|
||||||
*
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function getNodeID() {
|
|
||||||
var regex = new RegExp('^'+SYSTEM_ZONE+':');
|
|
||||||
var matches = [];
|
|
||||||
|
|
||||||
for each (var addr in system.fido_addr_list)
|
|
||||||
{
|
|
||||||
if (regex.test(addr)) {
|
|
||||||
addr = addr.replace(regex,'');
|
|
||||||
matches = addr.split('/',2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
// Load the owner configuration into memory
|
|
||||||
if (! pageowners.length) {
|
|
||||||
var f = new File(ANSITEX_HOME+'/ctrl/videotex.ini');
|
|
||||||
|
|
||||||
if (f.open('r')) {
|
|
||||||
var logoans = f.iniGetValue('prefix','logoans');
|
|
||||||
var logovtx = f.iniGetValue('prefix','logovtx');
|
|
||||||
var users = f.iniGetValue('prefix','user');
|
|
||||||
//log(LOG_DEBUG,'+ pageOwner: users='+JSON.stringify(users));
|
|
||||||
pageowners.push({prefix: 0,logoans: logoans,logovtx: logovtx,user:users});
|
|
||||||
|
|
||||||
f.iniGetSections('prefix:').forEach(function (prefix) {
|
|
||||||
var p = parseInt(prefix.substr(7));
|
|
||||||
var logoans = f.iniGetValue(prefix,'logoans','');
|
|
||||||
var logovtx = f.iniGetValue(prefix,'logovtx','');
|
|
||||||
var users = f.iniGetValue(prefix,'user','');
|
|
||||||
//log(LOG_DEBUG,'+ pageOwner: users='+JSON.stringify(users));
|
|
||||||
pageowners.push({prefix: p,logoans: logoans,logovtx: logovtx,user:users});
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_DEBUG,'getPageOwners: Couldnt open videotex.ini? :'+JSON.stringify(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
// Sort the pageowners ascending
|
|
||||||
pageowners.sort(function(a,b) { return (a.prefix < b.prefix) ? 1 : ((b.prefix < a.prefix) ? -1 : 0); });
|
|
||||||
|
|
||||||
//log(LOG_DEBUG,'+ pageOwner: pageowners='+JSON.stringify(pageowners));
|
|
||||||
}
|
|
||||||
|
|
||||||
return pageowners;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadOptions(option) {
|
|
||||||
var f = new File(ANSITEX_HOME+'/ctrl/videotex.ini');
|
|
||||||
|
|
||||||
if (! f.open('r')) {
|
|
||||||
log(LOG_DEBUG,'loadOptions: Couldnt open videotex.ini? :'+JSON.stringify(f));
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
val = f.iniGetObject(option);
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
function msgBaseImport(msgbase,page,text) {
|
|
||||||
var msgbase = new MsgBase(findMsgBase(msgbase).code);
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Sending ['+page+'] to message base ['+msgbase.cfg.code+']');
|
|
||||||
|
|
||||||
var hdr = { to:'All', from:'Videotex', subject:'Frame: '+page };
|
|
||||||
|
|
||||||
var body = '';
|
|
||||||
body += text+"\r\n";
|
|
||||||
body += "--- " + js.exec_file + " " + '1.0' + "\r\n";
|
|
||||||
|
|
||||||
return msgbase.save_msg(hdr, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right Pad a string with char c
|
|
||||||
function padright(n,width,c) {
|
|
||||||
c = c || '0';
|
|
||||||
n = n + '';
|
|
||||||
return n.length >= width ? n : new Array(width - n.length + 1).join(c) + n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left Pad a string with char c
|
|
||||||
function padleft(n,width,c) {
|
|
||||||
c = c || '0';
|
|
||||||
n = n + '';
|
|
||||||
return n.length >= width ? n : n+new Array(width - n.length + 1).join(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the frame as a string
|
|
||||||
*/
|
|
||||||
function pageStr(page) {
|
|
||||||
if (page.frame==null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (! page.index)
|
|
||||||
page.index = 'a';
|
|
||||||
|
|
||||||
return page.frame.toString()+page.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read our videotex.ini configuration and determine who owns a page.
|
|
||||||
* If there is no prefix for the page, it is owned by the system '0'
|
|
||||||
*
|
|
||||||
* @param page
|
|
||||||
* @returns {undefined}
|
|
||||||
*/
|
|
||||||
function pageOwner(page) {
|
|
||||||
var BreakException = {};
|
|
||||||
var o = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
getPageOwners().forEach(function(owner) {
|
|
||||||
var p = owner.prefix.toString();
|
|
||||||
o = owner;
|
|
||||||
|
|
||||||
var re = new RegExp('^' + p, 'g');
|
|
||||||
if (page.toString().match(re)) {
|
|
||||||
//log(LOG_DEBUG,'= pageOwner: p='+p+',o: '+o);
|
|
||||||
throw BreakException;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
if (e !== BreakException) throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
//log(LOG_DEBUG,'+ pageOwner: page='+page+', owner: '+JSON.stringify(o));
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can the user edit the frame
|
|
||||||
*
|
|
||||||
* @param page
|
|
||||||
* @param user
|
|
||||||
*/
|
|
||||||
function pageEditor(page) {
|
|
||||||
//log(LOG_DEBUG,'+ pageEditor: page='+page+', user #'+user.number);
|
|
||||||
|
|
||||||
var BreakException = {};
|
|
||||||
var pageditor = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
getPageOwners().forEach(function(owner) {
|
|
||||||
var p = owner.prefix.toString();
|
|
||||||
//log(LOG_DEBUG,' - pageEditor: '+JSON.stringify(owner));
|
|
||||||
frameusers = owner.user ? owner.user.toString().split(',') : [1];
|
|
||||||
|
|
||||||
log(LOG_DEBUG,' - pageEditor: p='+p+'('+p.length+') user ['+JSON.stringify(frameusers)+'] - :'+frameusers.indexOf(user.number.toString()));
|
|
||||||
|
|
||||||
var re = new RegExp('^' + p, 'g');
|
|
||||||
if (page.toString().match(re) && (frameusers.indexOf(user.number.toString()) !== -1)) {
|
|
||||||
pageditor = true;
|
|
||||||
throw BreakException;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
if (e !== BreakException) throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'+ pageEditor: page='+page+', editor: '+JSON.stringify(pageditor));
|
|
||||||
return pageditor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns a list of zones used by this system.
|
|
||||||
*/
|
|
||||||
function zones() {
|
|
||||||
var z = [];
|
|
||||||
var ftn = /([0-9]+):([0-9]+)\/([0-9]+)(\.([0-9]+))?/;
|
|
||||||
|
|
||||||
for(var g=0;g<system.fido_addr_list.length;g++) {
|
|
||||||
z.push(parseInt(system.fido_addr_list[g].match(ftn)[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return z.sort(function(a,b) {
|
|
||||||
return (a>b);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this;
|
|
428
load/msgbases.js
428
load/msgbases.js
@ -1,428 +0,0 @@
|
|||||||
const PAGE_LENGTH = 4; // The size of our page tag as stored in the msgbase for echomail/netmail
|
|
||||||
const PAGE_LAST_KEY = 'last_page'; // Last page which has the latest message
|
|
||||||
const MAX_PAGE_NUM = 9999; // Maximum page number. @todo Can this be changed to '9'.repeat(PAGE_LENGTH)?
|
|
||||||
|
|
||||||
// Our message bases
|
|
||||||
function MsgAreas() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
this.areas = [];
|
|
||||||
this.areas_excluded = [];
|
|
||||||
var zone_id;
|
|
||||||
var zone_name;
|
|
||||||
var ma;
|
|
||||||
|
|
||||||
for(var g in msg_area.grp_list) {
|
|
||||||
if (msg_area.grp_list[g].name.indexOf(':') !== -1) {
|
|
||||||
zone_id = msg_area.grp_list[g].name.split(':')[0];
|
|
||||||
zone_name = msg_area.grp_list[g].name.split(':')[1];
|
|
||||||
|
|
||||||
for (var a in msg_area.grp_list[g].sub_list) {
|
|
||||||
if (msg_area.grp_list[g].sub_list[a].name.indexOf(':') !== -1) {
|
|
||||||
ma = new MsgArea();
|
|
||||||
ma.zone_id = zone_id;
|
|
||||||
ma.zone_name = zone_name;
|
|
||||||
ma.area_id = msg_area.grp_list[g].sub_list[a].name.split(':')[0];
|
|
||||||
ma.area_name = msg_area.grp_list[g].sub_list[a].name.split(':')[1];
|
|
||||||
ma.code = msg_area.grp_list[g].sub_list[a].code;
|
|
||||||
|
|
||||||
this.areas.push(ma);
|
|
||||||
} else {
|
|
||||||
this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
zone_name = msg_area.grp_list[g].name;
|
|
||||||
|
|
||||||
for (var a in msg_area.grp_list[g].sub_list) {
|
|
||||||
this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(this,'list',{
|
|
||||||
get: function() {
|
|
||||||
writeln('Areas that we are NOT managing mail:'+this.areas_excluded.length);
|
|
||||||
writeln('Areas that we ARE managing mail:'+this.areas.length);
|
|
||||||
|
|
||||||
for(var x in this.areas_excluded) {
|
|
||||||
writeln(x+':'+((this.areas_excluded[x].area_name === undefined)
|
|
||||||
? this.areas_excluded[x]
|
|
||||||
: JSON.stringify(this.areas_excluded[x])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function MsgArea() {
|
|
||||||
this.zone_id = undefined;
|
|
||||||
this.zone_name = undefined;
|
|
||||||
this.area_id = undefined;
|
|
||||||
this.area_name = undefined;
|
|
||||||
this.msgbase = undefined;
|
|
||||||
this.headers = undefined;
|
|
||||||
this.tagged_list = undefined;
|
|
||||||
this.untagged_list = undefined;
|
|
||||||
this.grp_number = undefined;
|
|
||||||
this.subnum = undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a MsgArea once we are given the code
|
|
||||||
*/
|
|
||||||
Object.defineProperty(this,'code',{
|
|
||||||
set: function(code) {
|
|
||||||
this.msgbase = new MsgBase(code);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.msgbase.open()) {
|
|
||||||
headers = this.msgbase.get_all_msg_headers(false,false) || [];
|
|
||||||
|
|
||||||
// Just take the last MAX_MESSAGES
|
|
||||||
this.headers = Object.keys(headers).slice(-(MAX_PAGE_NUM+1)).map(function(key) { return headers[key]; });
|
|
||||||
headers = undefined;
|
|
||||||
|
|
||||||
this.msgbase.close();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,code+' cannot be opened:'+this.msgbase.error);
|
|
||||||
this.headers = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
log(LOG_ERROR,code+' cannot be opened:'+e.message);
|
|
||||||
this.headers = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get Area's full name
|
|
||||||
Object.defineProperty(this,'full_name',{
|
|
||||||
get: function() {
|
|
||||||
return this.zone_name+':'+this.area_name;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Total tagged messages
|
|
||||||
Object.defineProperty(this,'list_tagged',{
|
|
||||||
get: function() {
|
|
||||||
if (this.tagged_list === undefined) {
|
|
||||||
this.tagged_list = [];
|
|
||||||
|
|
||||||
if (! this.headers)
|
|
||||||
return this.tagged_list;
|
|
||||||
|
|
||||||
for(var x in this.headers) {
|
|
||||||
if (this.headers[x].tags && (this.headers[x].tags.length === PAGE_LENGTH)) {
|
|
||||||
this.tagged_list.push(this.headers[x]);
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.tagged_list;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// List untagged messages
|
|
||||||
Object.defineProperty(this,'list_untagged',{
|
|
||||||
get: function() {
|
|
||||||
if (this.untagged_list === undefined) {
|
|
||||||
this.untagged_list = [];
|
|
||||||
|
|
||||||
if (! this.headers)
|
|
||||||
return this.untagged_list;
|
|
||||||
|
|
||||||
for(var x in this.headers) {
|
|
||||||
if ((! this.headers[x].tags) || (this.headers[x].tags.length !== PAGE_LENGTH)) {
|
|
||||||
this.untagged_list.push(this.headers[x]);
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.untagged_list;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get Next page number
|
|
||||||
Object.defineProperty(this,'page_next',{
|
|
||||||
get: function() {
|
|
||||||
var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini'));
|
|
||||||
if (! f.open('r')) {
|
|
||||||
writeln('Unable to open ini file');
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var page = f.iniGetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY)
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
return page ? page : '0'.repeat(PAGE_LENGTH);
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function(page) {
|
|
||||||
var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini'));
|
|
||||||
if (! f.open('r+')) {
|
|
||||||
writeln('Unable to open ini file');
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
f.iniSetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY,(''+page).padStart(4,'0'));
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Our page prefix for this msg area
|
|
||||||
Object.defineProperty(this,'page_prefix',{
|
|
||||||
get: function() {
|
|
||||||
return ''+this.zone_id+this.area_id;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unread messages [1..]
|
|
||||||
* Array key 0 returns the last read message
|
|
||||||
*
|
|
||||||
* @returns {*[]}
|
|
||||||
*/
|
|
||||||
MsgArea.prototype.newMsgs = function() {
|
|
||||||
var msgs = [];
|
|
||||||
var stats = this.getUserStats();
|
|
||||||
//log(LOG_DEBUG,'Users last_read pointer: '+JSON.stringify(stats.last_read));
|
|
||||||
|
|
||||||
for(var x in this.list_tagged) {
|
|
||||||
// Advance past our last scan_ptr
|
|
||||||
if (this.list_tagged[x].number <= stats.last_read)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
msgs.push(this.list_tagged[x]);
|
|
||||||
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New Messages for the logged in user
|
|
||||||
*/
|
|
||||||
MsgArea.prototype.newMsgsToMe = function() {
|
|
||||||
var msgs = [];
|
|
||||||
var stats = this.getUserStats();
|
|
||||||
var last = null;
|
|
||||||
//log(LOG_DEBUG,'Users scan_ptr pointer: '+JSON.stringify(stats.scan_ptr));
|
|
||||||
|
|
||||||
for(var x in this.list_tagged) {
|
|
||||||
// Advance past our last scan_ptr
|
|
||||||
if (this.list_tagged[x].number <= stats.scan_ptr) {
|
|
||||||
if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias))
|
|
||||||
last = x;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add our previous to me message
|
|
||||||
if (msgs.length === 0)
|
|
||||||
msgs.push(last !== null ? this.list_tagged[last] : []);
|
|
||||||
|
|
||||||
if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias))
|
|
||||||
msgs.push(this.list_tagged[x]);
|
|
||||||
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a specific message with a tag
|
|
||||||
*/
|
|
||||||
MsgArea.prototype.getMessage = function(page) {
|
|
||||||
var msg = undefined;
|
|
||||||
|
|
||||||
for(var x in this.list_tagged) {
|
|
||||||
if (this.list_tagged[x].tags === page) {
|
|
||||||
msg = this.list_tagged[x];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! msg)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
if (! this.msgbase.open()) {
|
|
||||||
writeln(code+' cannot be opened:'+this.msgbase.error);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.grp_number = this.msgbase.cfg.grp_number;
|
|
||||||
var cfg = this.msgbase.cfg;
|
|
||||||
msg.subnum = msg_area.grp_list[cfg.grp_number].sub_list.filter(function(x) { return x.number === cfg.number; }).pop().index;
|
|
||||||
msg.content = this.msgbase.get_msg_body(false,msg.number,false,false,true,true);
|
|
||||||
this.msgbase.close();
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a message page by pointer
|
|
||||||
*
|
|
||||||
* @param number
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
MsgArea.prototype.getMessagePage = function(number) {
|
|
||||||
log(LOG_DEBUG,'Get Message Page with number ['+number+']');
|
|
||||||
|
|
||||||
var r;
|
|
||||||
|
|
||||||
for (var x in this.list_tagged) {
|
|
||||||
if (this.list_tagged[x].number === number) {
|
|
||||||
r = this.list_tagged[x];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! r || r.tags === undefined)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return '1'+this.zone_id+this.area_id+r.tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgArea.prototype.getUserStats = function() {
|
|
||||||
return this.msgbase.cfg ? msg_area.grp_list[this.msgbase.cfg.grp_number].sub_list[msg_area.sub[this.msgbase.cfg.code].index] : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgArea.prototype.MessageNext = function(page) {
|
|
||||||
var x = null;
|
|
||||||
|
|
||||||
if (! page)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
var msgid = page.substr(7,4);
|
|
||||||
|
|
||||||
for(x in this.list_tagged) {
|
|
||||||
if (this.list_tagged[x].tags === msgid) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
|
|
||||||
//log(LOG_DEBUG,'- Next Message is:'+JSON.stringify(this.list_tagged[(parseInt(x)+1)])+', msgid:'+msgid+', page:'+page+', x:'+x);
|
|
||||||
|
|
||||||
/*
|
|
||||||
= Our next message is either
|
|
||||||
+ x+1 if x < this.list_tagged.length
|
|
||||||
+ x=0 if x == this.list_tagged.length (-1)
|
|
||||||
+ null if this.list_tagged.length == null; (thus no messages)
|
|
||||||
*/
|
|
||||||
|
|
||||||
return x === null ? null : this.list_tagged[(parseInt(x) === this.list_tagged.length-1) ? 0 : (parseInt(x)+1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgArea.prototype.MessagePrev = function(page) {
|
|
||||||
var prev = null;
|
|
||||||
var x = null;
|
|
||||||
|
|
||||||
if (! page)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
var msgid = page.substr(7,4);
|
|
||||||
|
|
||||||
for(x in this.list_tagged) {
|
|
||||||
if (this.list_tagged[x].tags === msgid) {
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
prev = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
write(); // @todo This is needed for this to work?
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
= Our previous message is either
|
|
||||||
+ prev if a tag was found, unless
|
|
||||||
+ prev is null, in which case it is this.list_tagged.length -1
|
|
||||||
+ null if x is still null (thus no messages)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// If prev is still null, then our last message must be the last one, unless x is null then there are no messages
|
|
||||||
return x === null ? null : this.list_tagged[(prev === null) ? this.list_tagged.length-1 : parseInt(prev)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tag messages with a frame number
|
|
||||||
* @note: May need to run jsexec with -m 32MB to overcome memory issues
|
|
||||||
*
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
MsgArea.prototype.tag_msgs = function() {
|
|
||||||
var msgs = this.list_untagged;
|
|
||||||
|
|
||||||
writeln("We have "+msgs.length+" messages to tag.");
|
|
||||||
|
|
||||||
// See if we need to tag something
|
|
||||||
if (! msgs.length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (! this.msgbase.open()) {
|
|
||||||
writeln(code+' cannot be opened:'+this.msgbase.error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var page_next = this.page_next;
|
|
||||||
|
|
||||||
for(var x in msgs) {
|
|
||||||
msgs[x].tags = (''+(page_next)).padStart(4,'0');
|
|
||||||
|
|
||||||
if(! this.msgbase.put_msg_header(msgs[x].number,msgs[x])) {
|
|
||||||
writeln('ERROR:'+this.msgbase.error);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
page_next++;
|
|
||||||
if (page_next > MAX_PAGE_NUM)
|
|
||||||
page_next = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.msgbase.close();
|
|
||||||
this.page_next = page_next;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgArea.prototype.page = function(msgid) {
|
|
||||||
return '1'+this.page_prefix+msgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgAreas.prototype.getArea = function(area) {
|
|
||||||
log(LOG_DEBUG,'- AREA:'+JSON.stringify(area));
|
|
||||||
if (area === undefined)
|
|
||||||
return undefined;
|
|
||||||
|
|
||||||
var zone = (''+area).substr(1,4);
|
|
||||||
var echo = (''+area).substr(5,2);
|
|
||||||
log(LOG_DEBUG,' - zone:'+zone);
|
|
||||||
log(LOG_DEBUG,' - echo:'+echo);
|
|
||||||
|
|
||||||
return this.areas.filter(function(x) {
|
|
||||||
return x.zone_id === zone && x.area_id === echo;
|
|
||||||
})[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgAreas.prototype.getMessage = function(page) {
|
|
||||||
var area = this.getArea(page);
|
|
||||||
log(LOG_DEBUG,' - msg:'+JSON.stringify(page.substr(7,4)));
|
|
||||||
|
|
||||||
return area ? area.getMessage(page.substr(7,4)) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgAreas.prototype.getUserStats = function(page) {
|
|
||||||
var area = this.getArea(page);
|
|
||||||
|
|
||||||
return area ? msg_area.grp_list[area.msgbase.cfg.grp_number].sub_list[msg_area.sub[area.msgbase.cfg.code].index] : undefined;
|
|
||||||
}
|
|
1386
load/page.js
1386
load/page.js
File diff suppressed because it is too large
Load Diff
@ -1,995 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
/*
|
|
||||||
* Module "qrcodegen", public members:
|
|
||||||
* - Class QrCode:
|
|
||||||
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
|
|
||||||
* - Function encodeBinary(list<byte> data, QrCode.Ecc ecl) -> QrCode
|
|
||||||
* - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl,
|
|
||||||
* int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode
|
|
||||||
* - Constants int MIN_VERSION, MAX_VERSION
|
|
||||||
* - Constructor QrCode(int version, QrCode.Ecc ecl, list<byte> dataCodewords, int mask)
|
|
||||||
* - Fields int version, size, mask
|
|
||||||
* - Field QrCode.Ecc errorCorrectionLevel
|
|
||||||
* - Method getModule(int x, int y) -> bool
|
|
||||||
* - Method drawCanvas(int scale, int border, HTMLCanvasElement canvas) -> void
|
|
||||||
* - Method toSvgString(int border) -> str
|
|
||||||
* - Enum Ecc:
|
|
||||||
* - Constants LOW, MEDIUM, QUARTILE, HIGH
|
|
||||||
* - Field int ordinal
|
|
||||||
* - Class QrSegment:
|
|
||||||
* - Function makeBytes(list<byte> data) -> QrSegment
|
|
||||||
* - Function makeNumeric(str data) -> QrSegment
|
|
||||||
* - Function makeAlphanumeric(str data) -> QrSegment
|
|
||||||
* - Function makeSegments(str text) -> list<QrSegment>
|
|
||||||
* - Function makeEci(int assignVal) -> QrSegment
|
|
||||||
* - Constructor QrSegment(QrSegment.Mode mode, int numChars, list<int> bitData)
|
|
||||||
* - Field QrSegment.Mode mode
|
|
||||||
* - Field int numChars
|
|
||||||
* - Method getData() -> list<int>
|
|
||||||
* - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX
|
|
||||||
* - Enum Mode:
|
|
||||||
* - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI
|
|
||||||
*/
|
|
||||||
var qrcodegen = new function() {
|
|
||||||
this.QrCode = function(version, errCorLvl, dataCodewords, mask) {
|
|
||||||
|
|
||||||
/*---- Constructor (low level) ----*/
|
|
||||||
|
|
||||||
// Check scalar arguments
|
|
||||||
if (version < MIN_VERSION || version > MAX_VERSION)
|
|
||||||
throw 'Version value out of range';
|
|
||||||
if (mask < -1 || mask > 7)
|
|
||||||
throw 'Mask value out of range';
|
|
||||||
if (!(errCorLvl instanceof Ecc))
|
|
||||||
throw 'QrCode.Ecc expected';
|
|
||||||
var size = version * 4 + 17;
|
|
||||||
|
|
||||||
// Initialize both grids to be size*size arrays of Boolean false
|
|
||||||
var row = [];
|
|
||||||
for (var i = 0; i < size; i++)
|
|
||||||
row.push(false);
|
|
||||||
var modules = []; // Initially all white
|
|
||||||
var isFunction = [];
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
modules .push(row.slice());
|
|
||||||
isFunction.push(row.slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute ECC, draw modules
|
|
||||||
drawFunctionPatterns();
|
|
||||||
var allCodewords = addEccAndInterleave(dataCodewords);
|
|
||||||
drawCodewords(allCodewords);
|
|
||||||
|
|
||||||
// Do masking
|
|
||||||
if (mask === -1) { // Automatically choose best mask
|
|
||||||
var minPenalty = Infinity;
|
|
||||||
for (var i = 0; i < 8; i++) {
|
|
||||||
applyMask(i);
|
|
||||||
drawFormatBits(i);
|
|
||||||
var penalty = getPenaltyScore();
|
|
||||||
if (penalty < minPenalty) {
|
|
||||||
mask = i;
|
|
||||||
minPenalty = penalty;
|
|
||||||
}
|
|
||||||
applyMask(i); // Undoes the mask due to XOR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mask < 0 || mask > 7)
|
|
||||||
throw 'Assertion error';
|
|
||||||
applyMask(mask); // Apply the final choice of mask
|
|
||||||
drawFormatBits(mask); // Overwrite old format bits
|
|
||||||
|
|
||||||
isFunction = null;
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Read-only instance properties ----*/
|
|
||||||
|
|
||||||
// The version number of this QR Code, which is between 1 and 40 (inclusive).
|
|
||||||
// This determines the size of this barcode.
|
|
||||||
Object.defineProperty(this, 'version', {value:version});
|
|
||||||
|
|
||||||
// The width and height of this QR Code, measured in modules, between
|
|
||||||
// 21 and 177 (inclusive). This is equal to version * 4 + 17.
|
|
||||||
Object.defineProperty(this, 'size', {value:size});
|
|
||||||
|
|
||||||
// The error correction level used in this QR Code.
|
|
||||||
Object.defineProperty(this, 'errorCorrectionLevel', {value:errCorLvl});
|
|
||||||
|
|
||||||
// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
|
|
||||||
// Even if a QR Code is created with automatic masking requested (mask = -1),
|
|
||||||
// the resulting object still has a mask value between 0 and 7.
|
|
||||||
Object.defineProperty(this, 'mask', {value:mask});
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Accessor methods ----*/
|
|
||||||
|
|
||||||
// Returns the color of the module (pixel) at the given coordinates, which is false
|
|
||||||
// for white or true for black. The top left corner has the coordinates (x=0, y=0).
|
|
||||||
// If the given coordinates are out of bounds, then false (white) is returned.
|
|
||||||
this.getModule = function(x, y) {
|
|
||||||
return 0 <= x && x < size && 0 <= y && y < size && modules[y][x];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Public instance methods ----*/
|
|
||||||
|
|
||||||
// Draws this QR Code, with the given module scale and border modules, onto the given HTML
|
|
||||||
// canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale.
|
|
||||||
// The drawn image is be purely black and white, and fully opaque.
|
|
||||||
// The scale must be a positive integer and the border must be a non-negative integer.
|
|
||||||
this.drawCanvas = function(scale, border, canvas) {
|
|
||||||
if (scale <= 0 || border < 0)
|
|
||||||
throw 'Value out of range';
|
|
||||||
var width = (size + border * 2) * scale;
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = width;
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
for (var y = -border; y < size + border; y++) {
|
|
||||||
for (var x = -border; x < size + border; x++) {
|
|
||||||
ctx.fillStyle = this.getModule(x, y) ? '#000000' : '#FFFFFF';
|
|
||||||
ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns a string of SVG code for an image depicting this QR Code, with the given number
|
|
||||||
// of border modules. The string always uses Unix newlines (\n), regardless of the platform.
|
|
||||||
this.toSvgString = function(border) {
|
|
||||||
if (border < 0)
|
|
||||||
throw 'Border must be non-negative';
|
|
||||||
var parts = [];
|
|
||||||
for (var y = 0; y < size; y++) {
|
|
||||||
for (var x = 0; x < size; x++) {
|
|
||||||
if (this.getModule(x, y))
|
|
||||||
parts.push('M' + (x + border) + ',' + (y + border) + 'h1v1h-1z');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
||||||
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
|
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ' +
|
|
||||||
(size + border * 2) + ' ' + (size + border * 2) + '" stroke="none">\n' +
|
|
||||||
'\t<rect width="100%" height="100%" fill="#FFFFFF"/>\n' +
|
|
||||||
'\t<path d="' + parts.join(" ") + '" fill="#000000"/>\n' +
|
|
||||||
'</svg>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Private helper methods for constructor: Drawing function modules ----*/
|
|
||||||
|
|
||||||
// Reads this object's version field, and draws and marks all function modules.
|
|
||||||
function drawFunctionPatterns() {
|
|
||||||
// Draw horizontal and vertical timing patterns
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
setFunctionModule(6, i, i % 2 === 0);
|
|
||||||
setFunctionModule(i, 6, i % 2 === 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
|
|
||||||
drawFinderPattern(3, 3);
|
|
||||||
drawFinderPattern(size - 4, 3);
|
|
||||||
drawFinderPattern(3, size - 4);
|
|
||||||
|
|
||||||
// Draw numerous alignment patterns
|
|
||||||
var alignPatPos = getAlignmentPatternPositions();
|
|
||||||
var numAlign = alignPatPos.length;
|
|
||||||
for (var i = 0; i < numAlign; i++) {
|
|
||||||
for (var j = 0; j < numAlign; j++) {
|
|
||||||
// Don't draw on the three finder corners
|
|
||||||
if (!(i === 0 && j === 0 || i === 0 && j === numAlign - 1 || i === numAlign - 1 && j === 0))
|
|
||||||
drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw configuration data
|
|
||||||
drawFormatBits(0); // Dummy mask value; overwritten later in the constructor
|
|
||||||
drawVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draws two copies of the format bits (with its own error correction code)
|
|
||||||
// based on the given mask and this object's error correction level field.
|
|
||||||
function drawFormatBits(mask) {
|
|
||||||
// Calculate error correction code and pack bits
|
|
||||||
var data = errCorLvl.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3
|
|
||||||
var rem = data;
|
|
||||||
for (var i = 0; i < 10; i++)
|
|
||||||
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
|
|
||||||
var bits = (data << 10 | rem) ^ 0x5412; // uint15
|
|
||||||
if (bits >>> 15 !== 0)
|
|
||||||
throw 'Assertion error';
|
|
||||||
|
|
||||||
// Draw first copy
|
|
||||||
for (var i = 0; i <= 5; i++)
|
|
||||||
setFunctionModule(8, i, getBit(bits, i));
|
|
||||||
setFunctionModule(8, 7, getBit(bits, 6));
|
|
||||||
setFunctionModule(8, 8, getBit(bits, 7));
|
|
||||||
setFunctionModule(7, 8, getBit(bits, 8));
|
|
||||||
for (var i = 9; i < 15; i++)
|
|
||||||
setFunctionModule(14 - i, 8, getBit(bits, i));
|
|
||||||
|
|
||||||
// Draw second copy
|
|
||||||
for (var i = 0; i < 8; i++)
|
|
||||||
setFunctionModule(size - 1 - i, 8, getBit(bits, i));
|
|
||||||
for (var i = 8; i < 15; i++)
|
|
||||||
setFunctionModule(8, size - 15 + i, getBit(bits, i));
|
|
||||||
setFunctionModule(8, size - 8, true); // Always black
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draws two copies of the version bits (with its own error correction code),
|
|
||||||
// based on this object's version field, iff 7 <= version <= 40.
|
|
||||||
function drawVersion() {
|
|
||||||
if (version < 7)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Calculate error correction code and pack bits
|
|
||||||
var rem = version; // version is uint6, in the range [7, 40]
|
|
||||||
for (var i = 0; i < 12; i++)
|
|
||||||
rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);
|
|
||||||
var bits = version << 12 | rem; // uint18
|
|
||||||
if (bits >>> 18 !== 0)
|
|
||||||
throw 'Assertion error';
|
|
||||||
|
|
||||||
// Draw two copies
|
|
||||||
for (var i = 0; i < 18; i++) {
|
|
||||||
var bit = getBit(bits, i);
|
|
||||||
var a = size - 11 + i % 3;
|
|
||||||
var b = Math.floor(i / 3);
|
|
||||||
setFunctionModule(a, b, bit);
|
|
||||||
setFunctionModule(b, a, bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draws a 9*9 finder pattern including the border separator,
|
|
||||||
// with the center module at (x, y). Modules can be out of bounds.
|
|
||||||
function drawFinderPattern(x, y) {
|
|
||||||
for (var dy = -4; dy <= 4; dy++) {
|
|
||||||
for (var dx = -4; dx <= 4; dx++) {
|
|
||||||
var dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm
|
|
||||||
var xx = x + dx, yy = y + dy;
|
|
||||||
if (0 <= xx && xx < size && 0 <= yy && yy < size)
|
|
||||||
setFunctionModule(xx, yy, dist !== 2 && dist !== 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draws a 5*5 alignment pattern, with the center module
|
|
||||||
// at (x, y). All modules must be in bounds.
|
|
||||||
function drawAlignmentPattern(x, y) {
|
|
||||||
for (var dy = -2; dy <= 2; dy++) {
|
|
||||||
for (var dx = -2; dx <= 2; dx++)
|
|
||||||
setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sets the color of a module and marks it as a function module.
|
|
||||||
// Only used by the constructor. Coordinates must be in bounds.
|
|
||||||
function setFunctionModule(x, y, isBlack) {
|
|
||||||
modules[y][x] = isBlack;
|
|
||||||
isFunction[y][x] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Private helper methods for constructor: Codewords and masking ----*/
|
|
||||||
|
|
||||||
// Returns a new byte string representing the given data with the appropriate error correction
|
|
||||||
// codewords appended to it, based on this object's version and error correction level.
|
|
||||||
function addEccAndInterleave(data) {
|
|
||||||
if (data.length !== QrCode.getNumDataCodewords(version, errCorLvl))
|
|
||||||
throw 'Invalid argument';
|
|
||||||
|
|
||||||
// Calculate parameter numbers
|
|
||||||
var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version];
|
|
||||||
var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK [errCorLvl.ordinal][version];
|
|
||||||
var rawCodewords = Math.floor(QrCode.getNumRawDataModules(version) / 8);
|
|
||||||
var numShortBlocks = numBlocks - rawCodewords % numBlocks;
|
|
||||||
var shortBlockLen = Math.floor(rawCodewords / numBlocks);
|
|
||||||
|
|
||||||
// Split data into blocks and append ECC to each block
|
|
||||||
var blocks = [];
|
|
||||||
var rs = new ReedSolomonGenerator(blockEccLen);
|
|
||||||
for (var i = 0, k = 0; i < numBlocks; i++) {
|
|
||||||
var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));
|
|
||||||
k += dat.length;
|
|
||||||
var ecc = rs.getRemainder(dat);
|
|
||||||
if (i < numShortBlocks)
|
|
||||||
dat.push(0);
|
|
||||||
blocks.push(dat.concat(ecc));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interleave (not concatenate) the bytes from every block into a single sequence
|
|
||||||
var result = [];
|
|
||||||
for (var i = 0; i < blocks[0].length; i++) {
|
|
||||||
for (var j = 0; j < blocks.length; j++) {
|
|
||||||
// Skip the padding byte in short blocks
|
|
||||||
if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks)
|
|
||||||
result.push(blocks[j][i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result.length !== rawCodewords)
|
|
||||||
throw 'Assertion error';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
|
|
||||||
// data area of this QR Code. Function modules need to be marked off before this is called.
|
|
||||||
function drawCodewords(data) {
|
|
||||||
if (data.length !== Math.floor(QrCode.getNumRawDataModules(version) / 8))
|
|
||||||
throw 'Invalid argument';
|
|
||||||
var i = 0; // Bit index into the data
|
|
||||||
// Do the funny zigzag scan
|
|
||||||
for (var right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
|
|
||||||
if (right === 6)
|
|
||||||
right = 5;
|
|
||||||
for (var vert = 0; vert < size; vert++) { // Vertical counter
|
|
||||||
for (var j = 0; j < 2; j++) {
|
|
||||||
var x = right - j; // Actual x coordinate
|
|
||||||
var upward = ((right + 1) & 2) === 0;
|
|
||||||
var y = upward ? size - 1 - vert : vert; // Actual y coordinate
|
|
||||||
if (!isFunction[y][x] && i < data.length * 8) {
|
|
||||||
modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
// If this QR Code has any remainder bits (0 to 7), they were assigned as
|
|
||||||
// 0/false/white by the constructor and are left unchanged by this method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i !== data.length * 8)
|
|
||||||
throw 'Assertion error';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// XORs the codeword modules in this QR Code with the given mask pattern.
|
|
||||||
// The function modules must be marked and the codeword bits must be drawn
|
|
||||||
// before masking. Due to the arithmetic of XOR, calling applyMask() with
|
|
||||||
// the same mask value a second time will undo the mask. A final well-formed
|
|
||||||
// QR Code needs exactly one (not zero, two, etc.) mask applied.
|
|
||||||
function applyMask(mask) {
|
|
||||||
if (mask < 0 || mask > 7)
|
|
||||||
throw 'Mask value out of range';
|
|
||||||
for (var y = 0; y < size; y++) {
|
|
||||||
for (var x = 0; x < size; x++) {
|
|
||||||
var invert;
|
|
||||||
switch (mask) {
|
|
||||||
case 0: invert = (x + y) % 2 === 0; break;
|
|
||||||
case 1: invert = y % 2 === 0; break;
|
|
||||||
case 2: invert = x % 3 === 0; break;
|
|
||||||
case 3: invert = (x + y) % 3 === 0; break;
|
|
||||||
case 4: invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0; break;
|
|
||||||
case 5: invert = x * y % 2 + x * y % 3 === 0; break;
|
|
||||||
case 6: invert = (x * y % 2 + x * y % 3) % 2 === 0; break;
|
|
||||||
case 7: invert = ((x + y) % 2 + x * y % 3) % 2 === 0; break;
|
|
||||||
default: throw 'Assertion error';
|
|
||||||
}
|
|
||||||
if (!isFunction[y][x] && invert)
|
|
||||||
modules[y][x] = !modules[y][x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates and returns the penalty score based on state of this QR Code's current modules.
|
|
||||||
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
|
|
||||||
function getPenaltyScore() {
|
|
||||||
var result = 0;
|
|
||||||
|
|
||||||
// Adjacent modules in row having same color, and finder-like patterns
|
|
||||||
for (var y = 0; y < size; y++) {
|
|
||||||
var runHistory = [0,0,0,0,0,0,0];
|
|
||||||
var color = false;
|
|
||||||
var runX = 0;
|
|
||||||
for (var x = 0; x < size; x++) {
|
|
||||||
if (modules[y][x] === color) {
|
|
||||||
runX++;
|
|
||||||
if (runX === 5)
|
|
||||||
result += QrCode.PENALTY_N1;
|
|
||||||
else if (runX > 5)
|
|
||||||
result++;
|
|
||||||
} else {
|
|
||||||
QrCode.addRunToHistory(runX, runHistory);
|
|
||||||
if (!color && QrCode.hasFinderLikePattern(runHistory))
|
|
||||||
result += QrCode.PENALTY_N3;
|
|
||||||
color = modules[y][x];
|
|
||||||
runX = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QrCode.addRunToHistory(runX, runHistory);
|
|
||||||
if (color)
|
|
||||||
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
|
|
||||||
if (QrCode.hasFinderLikePattern(runHistory))
|
|
||||||
result += QrCode.PENALTY_N3;
|
|
||||||
}
|
|
||||||
// Adjacent modules in column having same color, and finder-like patterns
|
|
||||||
for (var x = 0; x < size; x++) {
|
|
||||||
var runHistory = [0,0,0,0,0,0,0];
|
|
||||||
var color = false;
|
|
||||||
var runY = 0;
|
|
||||||
for (var y = 0; y < size; y++) {
|
|
||||||
if (modules[y][x] === color) {
|
|
||||||
runY++;
|
|
||||||
if (runY === 5)
|
|
||||||
result += QrCode.PENALTY_N1;
|
|
||||||
else if (runY > 5)
|
|
||||||
result++;
|
|
||||||
} else {
|
|
||||||
QrCode.addRunToHistory(runY, runHistory);
|
|
||||||
if (!color && QrCode.hasFinderLikePattern(runHistory))
|
|
||||||
result += QrCode.PENALTY_N3;
|
|
||||||
color = modules[y][x];
|
|
||||||
runY = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QrCode.addRunToHistory(runY, runHistory);
|
|
||||||
if (color)
|
|
||||||
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
|
|
||||||
if (QrCode.hasFinderLikePattern(runHistory))
|
|
||||||
result += QrCode.PENALTY_N3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2*2 blocks of modules having same color
|
|
||||||
for (var y = 0; y < size - 1; y++) {
|
|
||||||
for (var x = 0; x < size - 1; x++) {
|
|
||||||
var color = modules[y][x];
|
|
||||||
if (color === modules[y][x + 1] && color === modules[y + 1][x] && color === modules[y + 1][x + 1])
|
|
||||||
result += QrCode.PENALTY_N2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Balance of black and white modules
|
|
||||||
var black = 0;
|
|
||||||
modules.forEach(function(row) {
|
|
||||||
row.forEach(function(color) {
|
|
||||||
if (color)
|
|
||||||
black++;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var total = size * size; // Note that size is odd, so black/total !== 1/2
|
|
||||||
// Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
|
|
||||||
var k = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1;
|
|
||||||
result += k * QrCode.PENALTY_N4;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns an ascending list of positions of alignment patterns for this version number.
|
|
||||||
// Each position is in the range [0,177), and are used on both the x and y axes.
|
|
||||||
// This could be implemented as lookup table of 40 variable-length lists of integers.
|
|
||||||
function getAlignmentPatternPositions() {
|
|
||||||
if (version === 1)
|
|
||||||
return [];
|
|
||||||
else {
|
|
||||||
var numAlign = Math.floor(version / 7) + 2;
|
|
||||||
var step = (version === 32) ? 26 :
|
|
||||||
Math.ceil((size - 13) / (numAlign*2 - 2)) * 2;
|
|
||||||
var result = [6];
|
|
||||||
for (var pos = size - 7; result.length < numAlign; pos -= step)
|
|
||||||
result.splice(1, 0, pos);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns true iff the i'th bit of x is set to 1.
|
|
||||||
function getBit(x, i) {
|
|
||||||
return ((x >>> i) & 1) !== 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Static factory functions (high level) for QrCode ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a QR Code representing the given Unicode text string at the given error correction level.
|
|
||||||
* As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer
|
|
||||||
* Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible
|
|
||||||
* QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the
|
|
||||||
* ecl argument if it can be done without increasing the version.
|
|
||||||
*/
|
|
||||||
this.QrCode.encodeText = function(text, ecl) {
|
|
||||||
var segs = qrcodegen.QrSegment.makeSegments(text);
|
|
||||||
return this.encodeSegments(segs, ecl);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a QR Code representing the given binary data at the given error correction level.
|
|
||||||
* This function always encodes using the binary segment mode, not any text mode. The maximum number of
|
|
||||||
* bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
|
|
||||||
* The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
|
|
||||||
*/
|
|
||||||
this.QrCode.encodeBinary = function(data, ecl) {
|
|
||||||
var seg = qrcodegen.QrSegment.makeBytes(data);
|
|
||||||
return this.encodeSegments([seg], ecl);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Static factory functions (mid level) for QrCode ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a QR Code representing the given segments with the given encoding parameters.
|
|
||||||
* The smallest possible QR Code version within the given range is automatically
|
|
||||||
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
|
|
||||||
* may be higher than the ecl argument if it can be done without increasing the
|
|
||||||
* version. The mask number is either between 0 to 7 (inclusive) to force that
|
|
||||||
* mask, or -1 to automatically choose an appropriate mask (which may be slow).
|
|
||||||
* This function allows the user to create a custom sequence of segments that switches
|
|
||||||
* between modes (such as alphanumeric and byte) to encode text in less space.
|
|
||||||
* This is a mid-level API; the high-level API is encodeText() and encodeBinary().
|
|
||||||
*/
|
|
||||||
this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) {
|
|
||||||
if (minVersion === undefined) minVersion = MIN_VERSION;
|
|
||||||
if (maxVersion === undefined) maxVersion = MAX_VERSION;
|
|
||||||
if (mask === undefined) mask = -1;
|
|
||||||
if (boostEcl === undefined) boostEcl = true;
|
|
||||||
if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7)
|
|
||||||
throw 'Invalid value';
|
|
||||||
|
|
||||||
// Find the minimal version number to use
|
|
||||||
var version, dataUsedBits;
|
|
||||||
for (version = minVersion; ; version++) {
|
|
||||||
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
|
||||||
dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version);
|
|
||||||
if (dataUsedBits <= dataCapacityBits)
|
|
||||||
break; // This version number is found to be suitable
|
|
||||||
if (version >= maxVersion) // All versions in the range could not fit the given data
|
|
||||||
throw 'Data too long';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase the error correction level while the data still fits in the current version number
|
|
||||||
[this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { // From low to high
|
|
||||||
if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)
|
|
||||||
ecl = newEcl;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Concatenate all segments to create the data bit string
|
|
||||||
var bb = new BitBuffer();
|
|
||||||
segs.forEach(function(seg) {
|
|
||||||
bb.appendBits(seg.mode.modeBits, 4);
|
|
||||||
bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version));
|
|
||||||
seg.getData().forEach(function(bit) {
|
|
||||||
bb.push(bit);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (bb.length !== dataUsedBits)
|
|
||||||
throw 'Assertion error';
|
|
||||||
|
|
||||||
// Add terminator and pad up to a byte if applicable
|
|
||||||
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
|
|
||||||
if (bb.length > dataCapacityBits)
|
|
||||||
throw 'Assertion error';
|
|
||||||
bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length));
|
|
||||||
bb.appendBits(0, (8 - bb.length % 8) % 8);
|
|
||||||
if (bb.length % 8 !== 0)
|
|
||||||
throw 'Assertion error';
|
|
||||||
|
|
||||||
// Pad with alternating bytes until data capacity is reached
|
|
||||||
for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
|
|
||||||
bb.appendBits(padByte, 8);
|
|
||||||
|
|
||||||
// Pack bits into bytes in big endian
|
|
||||||
var dataCodewords = [];
|
|
||||||
while (dataCodewords.length * 8 < bb.length)
|
|
||||||
dataCodewords.push(0);
|
|
||||||
bb.forEach(function(bit, i) {
|
|
||||||
dataCodewords[i >>> 3] |= bit << (7 - (i & 7));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the QR Code object
|
|
||||||
return new this(version, ecl, dataCodewords, mask);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Private static helper functions for QrCode ----*/
|
|
||||||
|
|
||||||
var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'.
|
|
||||||
|
|
||||||
|
|
||||||
// Returns the number of data bits that can be stored in a QR Code of the given version number, after
|
|
||||||
// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
|
|
||||||
// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
|
|
||||||
QrCode.getNumRawDataModules = function(ver) {
|
|
||||||
if (ver < MIN_VERSION || ver > MAX_VERSION)
|
|
||||||
throw 'Version number out of range';
|
|
||||||
var result = (16 * ver + 128) * ver + 64;
|
|
||||||
if (ver >= 2) {
|
|
||||||
var numAlign = Math.floor(ver / 7) + 2;
|
|
||||||
result -= (25 * numAlign - 10) * numAlign - 55;
|
|
||||||
if (ver >= 7)
|
|
||||||
result -= 36;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
|
|
||||||
// QR Code of the given version number and error correction level, with remainder bits discarded.
|
|
||||||
// This stateless pure function could be implemented as a (40*4)-cell lookup table.
|
|
||||||
QrCode.getNumDataCodewords = function(ver, ecl) {
|
|
||||||
return Math.floor(QrCode.getNumRawDataModules(ver) / 8) -
|
|
||||||
QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] *
|
|
||||||
QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Inserts the given value to the front of the given array, which shifts over the
|
|
||||||
// existing values and deletes the last value. A helper function for getPenaltyScore().
|
|
||||||
QrCode.addRunToHistory = function(run, history) {
|
|
||||||
history.pop();
|
|
||||||
history.unshift(run);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
|
|
||||||
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
|
|
||||||
// Must only be called immediately after a run of white modules has ended.
|
|
||||||
QrCode.hasFinderLikePattern = function(runHistory) {
|
|
||||||
var n = runHistory[1];
|
|
||||||
return n > 0 && runHistory[2] === n && runHistory[4] === n && runHistory[5] === n
|
|
||||||
&& runHistory[3] === n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Constants and tables for QrCode ----*/
|
|
||||||
|
|
||||||
var MIN_VERSION = 1; // The minimum version number supported in the QR Code Model 2 standard
|
|
||||||
var MAX_VERSION = 40; // The maximum version number supported in the QR Code Model 2 standard
|
|
||||||
Object.defineProperty(this.QrCode, 'MIN_VERSION', {value:MIN_VERSION});
|
|
||||||
Object.defineProperty(this.QrCode, 'MAX_VERSION', {value:MAX_VERSION});
|
|
||||||
|
|
||||||
// For use in getPenaltyScore(), when evaluating which mask is best.
|
|
||||||
QrCode.PENALTY_N1 = 3;
|
|
||||||
QrCode.PENALTY_N2 = 3;
|
|
||||||
QrCode.PENALTY_N3 = 40;
|
|
||||||
QrCode.PENALTY_N4 = 10;
|
|
||||||
|
|
||||||
QrCode.ECC_CODEWORDS_PER_BLOCK = [
|
|
||||||
// Version: (note that index 0 is for padding, and is set to an illegal value)
|
|
||||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
|
||||||
[null, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low
|
|
||||||
[null, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium
|
|
||||||
[null, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile
|
|
||||||
[null, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High
|
|
||||||
];
|
|
||||||
|
|
||||||
QrCode.NUM_ERROR_CORRECTION_BLOCKS = [
|
|
||||||
// Version: (note that index 0 is for padding, and is set to an illegal value)
|
|
||||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
|
||||||
[null, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low
|
|
||||||
[null, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium
|
|
||||||
[null, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile
|
|
||||||
[null, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Public helper enumeration ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The error correction level in a QR Code symbol. Immutable.
|
|
||||||
*/
|
|
||||||
this.QrCode.Ecc = {
|
|
||||||
LOW : new Ecc(0, 1), // The QR Code can tolerate about 7% erroneous codewords
|
|
||||||
MEDIUM : new Ecc(1, 0), // The QR Code can tolerate about 15% erroneous codewords
|
|
||||||
QUARTILE: new Ecc(2, 3), // The QR Code can tolerate about 25% erroneous codewords
|
|
||||||
HIGH : new Ecc(3, 2), // The QR Code can tolerate about 30% erroneous codewords
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Private constructor.
|
|
||||||
function Ecc(ord, fb) {
|
|
||||||
// (Public) In the range 0 to 3 (unsigned 2-bit integer)
|
|
||||||
Object.defineProperty(this, 'ordinal', {value:ord});
|
|
||||||
|
|
||||||
// (Package-private) In the range 0 to 3 (unsigned 2-bit integer)
|
|
||||||
Object.defineProperty(this, 'formatBits', {value:fb});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Data segment class ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A segment of character/binary/control data in a QR Code symbol.
|
|
||||||
* Instances of this class are immutable.
|
|
||||||
* The mid-level way to create a segment is to take the payload data
|
|
||||||
* and call a static factory function such as QrSegment.makeNumeric().
|
|
||||||
* The low-level way to create a segment is to custom-make the bit buffer
|
|
||||||
* and call the QrSegment() constructor with appropriate values.
|
|
||||||
* This segment class imposes no length restrictions, but QR Codes have restrictions.
|
|
||||||
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
|
|
||||||
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
|
|
||||||
* This constructor creates a QR Code segment with the given attributes and data.
|
|
||||||
* The character count (numChars) must agree with the mode and the bit buffer length,
|
|
||||||
* but the constraint isn't checked. The given bit buffer is cloned and stored.
|
|
||||||
*/
|
|
||||||
this.QrSegment = function(mode, numChars, bitData) {
|
|
||||||
/*---- Constructor (low level) ----*/
|
|
||||||
if (numChars < 0 || !(mode instanceof Mode))
|
|
||||||
throw 'Invalid argument';
|
|
||||||
|
|
||||||
// The data bits of this segment. Accessed through getData().
|
|
||||||
bitData = bitData.slice(); // Make defensive copy
|
|
||||||
|
|
||||||
// The mode indicator of this segment.
|
|
||||||
Object.defineProperty(this, 'mode', {value:mode});
|
|
||||||
|
|
||||||
// The length of this segment's unencoded data. Measured in characters for
|
|
||||||
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
|
|
||||||
// Always zero or positive. Not the same as the data's bit length.
|
|
||||||
Object.defineProperty(this, 'numChars', {value:numChars});
|
|
||||||
|
|
||||||
// Returns a new copy of the data bits of this segment.
|
|
||||||
this.getData = function() {
|
|
||||||
return bitData.slice(); // Make defensive copy
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Static factory functions (mid level) for QrSegment ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a segment representing the given binary data encoded in
|
|
||||||
* byte mode. All input byte arrays are acceptable. Any text string
|
|
||||||
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
|
|
||||||
*/
|
|
||||||
this.QrSegment.makeBytes = function(data) {
|
|
||||||
var bb = new BitBuffer();
|
|
||||||
data.forEach(function(b) {
|
|
||||||
bb.appendBits(b, 8);
|
|
||||||
});
|
|
||||||
return new this(this.Mode.BYTE, data.length, bb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
|
|
||||||
*/
|
|
||||||
this.QrSegment.makeNumeric = function(digits) {
|
|
||||||
if (!this.NUMERIC_REGEX.test(digits))
|
|
||||||
throw 'String contains non-numeric characters';
|
|
||||||
var bb = new BitBuffer();
|
|
||||||
for (var i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration
|
|
||||||
var n = Math.min(digits.length - i, 3);
|
|
||||||
bb.appendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1);
|
|
||||||
i += n;
|
|
||||||
}
|
|
||||||
return new this(this.Mode.NUMERIC, digits.length, bb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a segment representing the given text string encoded in alphanumeric mode.
|
|
||||||
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
|
|
||||||
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
|
||||||
*/
|
|
||||||
this.QrSegment.makeAlphanumeric = function(text) {
|
|
||||||
if (!this.ALPHANUMERIC_REGEX.test(text))
|
|
||||||
throw 'String contains unencodable characters in alphanumeric mode';
|
|
||||||
var bb = new BitBuffer();
|
|
||||||
var i;
|
|
||||||
for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2
|
|
||||||
var temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;
|
|
||||||
temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
|
|
||||||
bb.appendBits(temp, 11);
|
|
||||||
}
|
|
||||||
if (i < text.length) // 1 character remaining
|
|
||||||
bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6);
|
|
||||||
return new this(this.Mode.ALPHANUMERIC, text.length, bb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a new mutable list of zero or more segments to represent the given Unicode text string.
|
|
||||||
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
|
|
||||||
*/
|
|
||||||
this.QrSegment.makeSegments = function(text) {
|
|
||||||
// Select the most efficient segment encoding automatically
|
|
||||||
if (text === '')
|
|
||||||
return [];
|
|
||||||
else if (this.NUMERIC_REGEX.test(text))
|
|
||||||
return [this.makeNumeric(text)];
|
|
||||||
else if (this.ALPHANUMERIC_REGEX.test(text))
|
|
||||||
return [this.makeAlphanumeric(text)];
|
|
||||||
else
|
|
||||||
return [this.makeBytes(toUtf8ByteArray(text))];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a segment representing an Extended Channel Interpretation
|
|
||||||
* (ECI) designator with the given assignment value.
|
|
||||||
*/
|
|
||||||
this.QrSegment.makeEci = function(assignVal) {
|
|
||||||
var bb = new BitBuffer();
|
|
||||||
if (assignVal < 0)
|
|
||||||
throw 'ECI assignment value out of range';
|
|
||||||
else if (assignVal < (1 << 7))
|
|
||||||
bb.appendBits(assignVal, 8);
|
|
||||||
else if (assignVal < (1 << 14)) {
|
|
||||||
bb.appendBits(2, 2);
|
|
||||||
bb.appendBits(assignVal, 14);
|
|
||||||
} else if (assignVal < 1000000) {
|
|
||||||
bb.appendBits(6, 3);
|
|
||||||
bb.appendBits(assignVal, 21);
|
|
||||||
} else
|
|
||||||
throw 'ECI assignment value out of range';
|
|
||||||
return new this(this.Mode.ECI, 0, bb);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// (Package-private) Calculates and returns the number of bits needed to encode the given segments at the
|
|
||||||
// given version. The result is infinity if a segment has too many characters to fit its length field.
|
|
||||||
this.QrSegment.getTotalBits = function(segs, version) {
|
|
||||||
var result = 0;
|
|
||||||
for (var i = 0; i < segs.length; i++) {
|
|
||||||
var seg = segs[i];
|
|
||||||
var ccbits = seg.mode.numCharCountBits(version);
|
|
||||||
if (seg.numChars >= (1 << ccbits))
|
|
||||||
return Infinity; // The segment's length doesn't fit the field's bit width
|
|
||||||
result += 4 + ccbits + seg.getData().length;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Constants for QrSegment ----*/
|
|
||||||
|
|
||||||
var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'.
|
|
||||||
|
|
||||||
// (Public) Describes precisely all strings that are encodable in numeric mode.
|
|
||||||
// To test whether a string s is encodable: var ok = NUMERIC_REGEX.test(s);
|
|
||||||
// A string is encodable iff each character is in the range 0 to 9.
|
|
||||||
this.QrSegment.NUMERIC_REGEX = /^[0-9]*$/;
|
|
||||||
|
|
||||||
// (Public) Describes precisely all strings that are encodable in alphanumeric mode.
|
|
||||||
// To test whether a string s is encodable: var ok = ALPHANUMERIC_REGEX.test(s);
|
|
||||||
// A string is encodable iff each character is in the following set: 0 to 9, A to Z
|
|
||||||
// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
|
||||||
this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/;
|
|
||||||
|
|
||||||
// (Private) The set of all legal characters in alphanumeric mode,
|
|
||||||
// where each character value maps to the index in the string.
|
|
||||||
QrSegment.ALPHANUMERIC_CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Public helper enumeration ----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Describes how a segment's data bits are interpreted. Immutable.
|
|
||||||
*/
|
|
||||||
this.QrSegment.Mode = { // Constants
|
|
||||||
NUMERIC : new Mode(0x1, [10, 12, 14]),
|
|
||||||
ALPHANUMERIC: new Mode(0x2, [ 9, 11, 13]),
|
|
||||||
BYTE : new Mode(0x4, [ 8, 16, 16]),
|
|
||||||
KANJI : new Mode(0x8, [ 8, 10, 12]),
|
|
||||||
ECI : new Mode(0x7, [ 0, 0, 0]),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Private constructor.
|
|
||||||
function Mode(mode, ccbits) {
|
|
||||||
// (Package-private) The mode indicator bits, which is a uint4 value (range 0 to 15).
|
|
||||||
Object.defineProperty(this, 'modeBits', {value:mode});
|
|
||||||
|
|
||||||
// (Package-private) Returns the bit width of the character count field for a segment in
|
|
||||||
// this mode in a QR Code at the given version number. The result is in the range [0, 16].
|
|
||||||
this.numCharCountBits = function(ver) {
|
|
||||||
return ccbits[Math.floor((ver + 7) / 17)];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*---- Private helper functions and classes ----*/
|
|
||||||
|
|
||||||
// Returns a new array of bytes representing the given string encoded in UTF-8.
|
|
||||||
function toUtf8ByteArray(str) {
|
|
||||||
str = encodeURI(str);
|
|
||||||
var result = [];
|
|
||||||
for (var i = 0; i < str.length; i++) {
|
|
||||||
if (str.charAt(i) !== '%')
|
|
||||||
result.push(str.charCodeAt(i));
|
|
||||||
else {
|
|
||||||
result.push(parseInt(str.substring(i + 1, i + 3), 16));
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A private helper class that computes the Reed-Solomon error correction codewords for a sequence of
|
|
||||||
* data codewords at a given degree. Objects are immutable, and the state only depends on the degree.
|
|
||||||
* This class exists because each data block in a QR Code shares the same the divisor polynomial.
|
|
||||||
* This constructor creates a Reed-Solomon ECC generator for the given degree. This could be implemented
|
|
||||||
* as a lookup table over all possible parameter values, instead of as an algorithm.
|
|
||||||
*/
|
|
||||||
function ReedSolomonGenerator(degree) {
|
|
||||||
if (degree < 1 || degree > 255)
|
|
||||||
throw 'Degree out of range';
|
|
||||||
|
|
||||||
// Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which
|
|
||||||
// is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
|
|
||||||
var coefficients = [];
|
|
||||||
|
|
||||||
// Start with the monomial x^0
|
|
||||||
for (var i = 0; i < degree - 1; i++)
|
|
||||||
coefficients.push(0);
|
|
||||||
coefficients.push(1);
|
|
||||||
|
|
||||||
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
|
|
||||||
// drop the highest term, and store the rest of the coefficients in order of descending powers.
|
|
||||||
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
|
|
||||||
var root = 1;
|
|
||||||
for (var i = 0; i < degree; i++) {
|
|
||||||
// Multiply the current product by (x - r^i)
|
|
||||||
for (var j = 0; j < coefficients.length; j++) {
|
|
||||||
coefficients[j] = ReedSolomonGenerator.multiply(coefficients[j], root);
|
|
||||||
if (j + 1 < coefficients.length)
|
|
||||||
coefficients[j] ^= coefficients[j + 1];
|
|
||||||
}
|
|
||||||
root = ReedSolomonGenerator.multiply(root, 0x02);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computes and returns the Reed-Solomon error correction codewords for the given
|
|
||||||
// sequence of data codewords. The returned object is always a new byte array.
|
|
||||||
// This method does not alter this object's state (because it is immutable).
|
|
||||||
this.getRemainder = function(data) {
|
|
||||||
// Compute the remainder by performing polynomial division
|
|
||||||
var result = coefficients.map(function() { return 0; });
|
|
||||||
data.forEach(function(b) {
|
|
||||||
var factor = b ^ result.shift();
|
|
||||||
result.push(0);
|
|
||||||
coefficients.forEach(function(coef, i) {
|
|
||||||
result[i] ^= ReedSolomonGenerator.multiply(coef, factor);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This static function returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and
|
|
||||||
// result are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.
|
|
||||||
ReedSolomonGenerator.multiply = function(x, y) {
|
|
||||||
if (x >>> 8 !== 0 || y >>> 8 !== 0)
|
|
||||||
throw 'Byte out of range';
|
|
||||||
// Russian peasant multiplication
|
|
||||||
var z = 0;
|
|
||||||
for (var i = 7; i >= 0; i--) {
|
|
||||||
z = (z << 1) ^ ((z >>> 7) * 0x11D);
|
|
||||||
z ^= ((y >>> i) & 1) * x;
|
|
||||||
}
|
|
||||||
if (z >>> 8 !== 0)
|
|
||||||
throw 'Assertion error';
|
|
||||||
return z;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A private helper class that represents an appendable sequence of bits (0s and 1s).
|
|
||||||
* Mainly used by QrSegment. This constructor creates an empty bit buffer (length 0).
|
|
||||||
*/
|
|
||||||
function BitBuffer() {
|
|
||||||
Array.call(this);
|
|
||||||
|
|
||||||
// Appends the given number of low-order bits of the given value
|
|
||||||
// to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len.
|
|
||||||
this.appendBits = function(val, len) {
|
|
||||||
if (len < 0 || len > 31 || val >>> len !== 0)
|
|
||||||
throw 'Value out of range';
|
|
||||||
for (var i = len - 1; i >= 0; i--) // Append bit by bit
|
|
||||||
this.push((val >>> i) & 1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
BitBuffer.prototype = Object.create(Array.prototype);
|
|
||||||
BitBuffer.prototype.constructor = BitBuffer;
|
|
||||||
|
|
||||||
};
|
|
187
load/session.js
187
load/session.js
@ -1,187 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
this.previous = undefined;
|
|
||||||
|
|
||||||
/* Frame type settings */
|
|
||||||
this.settings = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable pulling out submitted value by its name
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @returns {null|string|*}
|
|
||||||
*/
|
|
||||||
this.fieldValue = function(key) {
|
|
||||||
for each (var k in this.page.input_fields) {
|
|
||||||
if (k.name === key)
|
|
||||||
return k.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get = function(page) {
|
|
||||||
if (!(page instanceof PageObject))
|
|
||||||
throw new Error('page must be a PageObject');
|
|
||||||
|
|
||||||
this.baselineSend('LOADING');
|
|
||||||
// Just in case our new page doesnt exist
|
|
||||||
this.previous = this.page;
|
|
||||||
|
|
||||||
this.page = new Page();
|
|
||||||
var result = this.page.get(page);
|
|
||||||
|
|
||||||
// If our new page doesnt exist, reset our page back to the current
|
|
||||||
if (! result)
|
|
||||||
this.page = this.previous;
|
|
||||||
|
|
||||||
this.previous = undefined;
|
|
||||||
this.baselineClear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a message frame
|
|
||||||
*
|
|
||||||
* @param pagenum
|
|
||||||
*/
|
|
||||||
this.loadMessage = function(pagenum,ext) {
|
|
||||||
log(LOG_ERROR,'Loading Message :['+pagenum+']');
|
|
||||||
|
|
||||||
// Load our message
|
|
||||||
var ma = new MsgAreas()
|
|
||||||
var area = ma.getArea(pagenum);
|
|
||||||
var msg = ma.getMessage(pagenum);
|
|
||||||
var msg_header;
|
|
||||||
|
|
||||||
if (! msg)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// @todo Search 1zzzzEE..., 1zzzz...
|
|
||||||
if (! this.get(new PageObject(MAIL_TEMPLATE_FRAME),ext)) {
|
|
||||||
this.page = new Page();
|
|
||||||
|
|
||||||
log(LOG_ERROR,'Echomail template missing :['+JSON.stringify(MAIL_TEMPLATE_FRAME)+'] ?');
|
|
||||||
|
|
||||||
msg_header = 'TO: '+msg.to.substr(0,72)+"\r\n";
|
|
||||||
msg_header += 'FROM: '+msg.from.substr(0,72)+"\r\n";
|
|
||||||
msg_header += 'DATE: '+msg.date.substr(0,72)+"\r\n";
|
|
||||||
msg_header += 'SUBJECT: '+msg.subject.substr(0,72)+"\r\n";
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// @todo change this to use atcode()
|
|
||||||
msg_header = this.page.raw.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 'FROM': return msg.from.substr(0,length);
|
|
||||||
case 'SUBJECT': return msg.subject.substr(0,length);
|
|
||||||
case 'TO': return msg.to.substr(0,length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.page.name = new PageObject(pagenum,'a');
|
|
||||||
this.page.owner = 1;
|
|
||||||
// @todo Keys should map to next/previous/send, etc as indicated in the template frame.
|
|
||||||
this.page.key = [this.page.name.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.page.type = FRAME_TYPE_MESSAGE;
|
|
||||||
// @todo Take the cost from the template
|
|
||||||
this.page.cost = 5;
|
|
||||||
// @todo Take the key values from the template
|
|
||||||
this.page.__properties__.isAccessible = true;
|
|
||||||
this.page.__properties__.isPublic = true;
|
|
||||||
|
|
||||||
this.page.preload(msg_header+msg.content,'txt');
|
|
||||||
|
|
||||||
// Update the user's pointers
|
|
||||||
var stats = ma.getUserStats(this.page.name.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.page.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.page.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.page.name.frame);
|
|
||||||
if (x)
|
|
||||||
this.page.key[4] = area.page(x.tags);
|
|
||||||
|
|
||||||
// Next Message
|
|
||||||
x = area.MessageNext(this.page.name.frame);
|
|
||||||
if (x)
|
|
||||||
this.page.key[6] = area.page(x.tags);
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'Built frame: ['+this.page.name.frame+']['+this.page.name.index+'] ('+this.page.name.toString()+')');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the page
|
|
||||||
this.render = function() {
|
|
||||||
this.gotoxy(0,0);
|
|
||||||
//console.clear(null,false);
|
|
||||||
write(so.page.display().join(''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a system message for a index
|
|
||||||
*
|
|
||||||
* @param index
|
|
||||||
* @returns {string|*}
|
|
||||||
* @see SessionProtocol()
|
|
||||||
*/
|
|
||||||
Session.prototype.getMessage = function(index) {
|
|
||||||
eval('var msg = this.settings.'+index);
|
|
||||||
return msg;
|
|
||||||
}
|
|
@ -1,596 +0,0 @@
|
|||||||
const SESSION_ANSITEX = (1<<1);
|
|
||||||
var SESSION_EXT = 'tex';
|
|
||||||
|
|
||||||
var FRAME_WIDTH = 80;
|
|
||||||
var FRAME_HEIGHT = 22;
|
|
||||||
var FRAME_PROVIDER_LENGTH = 55;
|
|
||||||
var FRAME_PAGE_LENGTH = 13;
|
|
||||||
var FRAME_COST_LENGTH = 10;
|
|
||||||
var FRAME_ATTR_LENGTH = 0; // Space that an attribute takes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 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;
|
|
||||||
|
|
||||||
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 };
|
|
||||||
var line;
|
|
||||||
var x;
|
|
||||||
|
|
||||||
while (lines.length > 0) {
|
|
||||||
x = 0;
|
|
||||||
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 SessionProtocol() {
|
|
||||||
Session.apply(this,arguments);
|
|
||||||
|
|
||||||
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 **';
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionProtocol.prototype = Session.prototype;
|
|
||||||
SessionProtocol.prototype.constructor = SessionProtocol;
|
|
@ -1,650 +0,0 @@
|
|||||||
const SESSION_VIEWDATA = (1<<2);
|
|
||||||
var SESSION_EXT = 'vtx';
|
|
||||||
|
|
||||||
var FRAME_WIDTH = 40;
|
|
||||||
var FRAME_HEIGHT = 22;
|
|
||||||
var FRAME_PROVIDER_LENGTH = 23;
|
|
||||||
var FRAME_PAGE_LENGTH = 11;
|
|
||||||
var FRAME_COST_LENGTH = 6;
|
|
||||||
var FRAME_ATTR_LENGTH = 1; // 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 **';
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
1066
load/windows.js
1066
load/windows.js
File diff suppressed because it is too large
Load Diff
20
logon.js
20
logon.js
@ -1,20 +0,0 @@
|
|||||||
require("sbbsdefs.js", 'SS_RLOGIN');
|
|
||||||
//require("nodedefs.js", 'NODE_QUIET');
|
|
||||||
|
|
||||||
// To force all users to the ansitex shell
|
|
||||||
// @todo Make a ini config setting for this
|
|
||||||
user.command_shell = 'ansitex';
|
|
||||||
|
|
||||||
// Disable System Info and ?
|
|
||||||
system.settings |= (SYS_NOSYSINFO | SYS_NONODELIST);
|
|
||||||
|
|
||||||
// @note: Unable to suppress "Logging on to <BBS> as <USER>..."
|
|
||||||
|
|
||||||
// Need to suppress Search for new messages & files
|
|
||||||
user.settings &= ~(USER_ASK_SSCAN | USER_ASK_NSCAN | USER_ANFSCAN | USER_PAUSE | USER_NO_EXASCII | USER_COLDKEYS);
|
|
||||||
|
|
||||||
// Enable ANSI and some other settings
|
|
||||||
user.settings |= (USER_AUTOTERM | USER_ANSI | USER_COLOR);
|
|
||||||
|
|
||||||
// Disable Chatting
|
|
||||||
user.chat_settings |= (CHAT_NOPAGE | CHAT_NOACT);
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
find tex vtx -maxdepth 1 -type f | awk -F. '{print $1}'|awk -F/ '{print $2}'|sort|uniq| while read i; do
|
|
||||||
[ "$i" == "tex/:" -o "$i" == "vtx/:" ] && continue
|
|
||||||
|
|
||||||
echo "======= ${i} ======="
|
|
||||||
jsexec -n ansitex/tools/frames_check $i
|
|
||||||
done
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":198,"index":"a","owner":1,"cost":0,"content":"G1swbRtbNzZDG1sxbS4bWzBtDQogG1szM20bWG1zZ19ncnBfbmFtZTs0MBtcG1szMEMbWzM3bRtbMW3awr/Cv7+zG1swbQ0KIBtbMzZtG1htc2dfYXJlYV9hcmVhdGFnOzQwG1wbWzMwQxtbMzdtsyCzwrSzsw0KIBtbMTszNm0bWG1zZ19hcmVhX2Rlc2M7NDAbXBtbMzBDG1sxOzM3bVN1bW1hcnkbWzBtDQobWzE7MzBtxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMSzxMTExMTExMTExMTExMTExMTExMTExMTExMTExBtbMG0NCiAbWERBVEVUSU1FOzI0G1wbWzI1QxtbMTszMG2zG1swbSAbWzFtTmV3IE1lc3NhZ2VzIHRvIFlvdRtbMG06IBtbMTszMW0bWG1zZ19hcmVhX25ld3RvbWU7LTUbXBtbMG0NChtbNTBDG1sxOzMwbbMbWzBtICAgICAbWzFtVW5yZWFkIE1lc3NhZ2VzG1swbTogG1sxOzMxbRtYbXNnX2FyZWFfbmV3Oy01G1wbWzBtDQogG1sxOzMybTEbWzM3bSBGaXJzdCB0byB5b3UgICAbWzM0bRtYbXNnX2FyZWFfbXNnb3RvbWVfZGF0ZTszMRtcG1swbSAbWzE7MzBtsxtbMG0gICAgICAbWzFtVG90YWwgTWVzc2FnZXMbWzBtOiAbWzE7MzFtG1htc2dfYXJlYV90b3RhbDstNRtcG1swbQ0KIBtbMTszMm0yG1swbSAbWzFtRmlyc3QgdW5yZWFkICAgG1sxOzM0bRtYbXNnX2FyZWFfbXNndW5yZWFkX2RhdGU7MzEbXBtbMG0gG1sxOzMwbbMbWzBtDQobWzUwQxtbMTszMG2zG1swbSAgICBQZW5kaW5nIE1lc3NhZ2VzOiAbWzMxbRtYbXNnX2FyZWFfcGVuZGluZzstNRtcG1szN20NCiAbWzE7MzJtMyAbWzM3bU9sZGVzdCBNZXNzYWdlIBtbMzRtG1htc2dfYXJlYV9tc2dvbGRlc3RfZGF0ZTszMRtcG1szN20gG1sxOzMwbbMbWzBtDQogG1sxOzMybTQgG1szN21MYXRlc3QgTWVzc2FnZSAbWzM0bRtYbXNnX2FyZWFfbXNnbmV3ZXN0X2RhdGU7MzEbXBtbMzdtIBtbMTszMG2zG1swbSAgICAgICAgICAbWzMybTkbWzBtIE5ldyBTY2FuOiAgIBtbMTszMm0bWG1zZ19hcmVhX25ld3NjYW47LTMbXBtbMG0NChtbNTBDG1sxOzMwbcEbWzBtDQogG1szMm01G1sxbSAbWzBtV3JpdGUgTmV3IE1lc3NhZ2UgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzQ0bdXNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NzbgbWzBtDQogG1szMm02G1sxbSAbWzBtU2VhcmNoIGZvciBNZXNzYWdlICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzQ0bbMbWzMzbSCvIE1lc3NhZ2UgUmVhZGluZyBOYXZpZ2F0aW9uIK4gG1sxOzM3bbMbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7NDRtsyAbWzMybRgbWzM2bSBTY3JvbGwgVVAgG1swOzQ0bSAgG1sxbbMbWzA7NDRtIBtbMTszMm0ZG1szNm0gU2Nyb2xsIERPV04bWzA7NDRtIBtbMTszM20gG1szN22zG1swbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzQ0bbMgG1szMm0xG1szNm0gUHJldiBUbyBNZSAgG1sxOzMzbbMbWzA7NDRtIBtbMTszMm0yG1szNm0gTmV4dCBUbyBNZSAgG1szM20gG1szN22zG1swbQ0KIBtbMzJtOBtbMzdtIEFyZWEgU3VtbWFyeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTs0NG2zG1szM20gG1szMm00IBtbMzZtUHJldmlvdXMgICAgG1sxOzMzbbMbWzA7NDRtIBtbMzJtMxtbMTszNm0gG1swOzM2OzQ0bVByZXYgVGhyZWFkG1szN20gG1sxOzMzbSAbWzM3bbMbWzBtDQogG1sxOzMybTAbWzA7MzJtIBtbMTszN21BcmVhIERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7NDRtsxtbMzNtIBtbMzJtNhtbMzZtIE5leHQbWzFtIBtbMDs0NG0gICAgICAgG1sxbbMbWzA7NDRtIBtbMzJtNxtbMzdtIBtbMzZtTmV4dCBUaHJlYWQbWzM3bSAbWzE7MzNtIBtbMzdtsxtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTs0NG2zG1swOzQ0bSAbWzMybTUbWzM3bSAbWzM2bVdyaXRlIE5ldxtbMzdtICAgG1sxbbMbWzA7NDRtIBtbMzJtOBtbMzdtIBtbMzZtUmVwbHkbWzM3bSAgICAgICAbWzE7MzNtIBtbMzdtsxtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTs0NG2zG1swOzQ0bSAbWzMybSMbWzM3bSAbWzM2bU1zZyBBdHRycyAgG1szN20gG1sxbbMbWzA7NDRtIBtbMTszMm0wG1szN20gG1sxOzM2bVJldHVybiBoZXJlG1szN20gG1sxOzMzbSAbWzM3bbMbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7NDRt1M3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NvhtbMG0NCg==","isPublic":0,"isAccessible":1,"type":"m","key":[null,null,null,null,null,null,null,null,null,null],"date":"2022-04-29T00:00:00.000Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":199,"index":"a","owner":1,"cost":0,"content":"DQogG1sxOzM2bURhdGU6IBtbMzdtQERBVEU6NjBADQogICAbWzE7MzZtVG86IBtbMzdtQFRPOjYwQA0KIBtbMTszNm1Gcm9tOiAbWzM3bUBGUk9NOjYwQA0KIBtbMTszNm1TdWJqOiAbWzM3bUBTVUJKRUNUOjYwQA0KIBtbMTszMG3ExMTExMQbWzBtxMTExMTEG1sxOzMwbcTExMTExBtbMG3ExMTExMQbWzE7MzBtxMTExMTEG1swbcTExMTExBtbMTszMG3ExMTExMQbWzBtxMTExMTEG1sxOzMwbcTExMTExBtbMG3ExMTExMQbWzE7MzBtxMTExMTEG1swbQ0K","isPublic":0,"isAccessible":1,"type":"m","key":[null,null,null,null,null,null,null,null,null,null],"date":"2022-04-29T00:00:00.000Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":1,"index":"a","owner":0,"cost":0,"content":"G1swbSAgICAgIBtbMW0uG1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1szMW3axBtbMW3cG1swOzMybdrEG1sxbdwbWzA7MzRt2sQbWzFt3BtbMDszNm0gG1sxOzMzbd8bWzBtIBtbMzA7NDdt2sLc2sTc2iDcIBtbMzc7NDBtDQogG1sxbdrCv8K/v9q/INrCv8K/2r+/2htbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMW2zG1swOzMxbd/bG1sxOzMybbMbWzA7MzZtIBtbMzJt2xtbMTszNG0uXBtbMDszNG3cG1szM23eG1sxbdsbWzBtIBtbMzA7NDdtILMgw1/c2t/cIBtbMzc7NDBtDQogsyCzwrSzs7MgsyCzw9mzs7OzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtxMTExMTExMTExMTEG1swOzMwOzQ3bdzc3Nzc3Nzc3NwbWzM3OzQwbQ0KIBtbMTszMG3AINnBwdnZ2SDAINnB2dnZwdkbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG1ub2RlOhtbMG0gG1sxOzMybRtYbm9kZWlkOy0xMRtcG1swbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtZGF0ZTogG1szMm0bWERBVEU6JVktJWItJWQ7LTExG1wbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG10aW1lOiAbWzMybRtYVElNRTstMTEbXBtbMG0NChtbMTszMG3ExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMSzxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExBtbMG0gG1sxOzMybTEbWzM3bSBGaWRvIE1lc3NhZ2UgTmV0d29ya3MbWzBtICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtICAgIBtbMTszMG1Vc2VmdWwgUGFnZXMbWzBtDQogG1szMm0yG1sxOzM3bSAbWzBtQkJTIERvb3IgR2FtZXMbWzFtIBtbMDszMW1bG1sxbUNvbWluZyBTb29uG1swOzMxbV0bWzM3bSAgICAgICAgG1sxOzMwbbMbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtsxtbMG0gG1sxOzMybSoxMDAxMiMbWzBtIBtbMW1Eb3ZlbmV0IE1lc3NhZ2UgTmV0d29yaxtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG2zG1swbSAbWzE7MzJtKjEwMDIxIxtbMG0gG1sxbUZTWG5ldBtbMG0gG1sxbU1lc3NhZ2UgTmV0d29yaxtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG2zG1swbSAbWzE7MzJtKjExMzM3IxtbMG0gG1sxbVRRV25ldBtbMG0gG1sxbU1lc3NhZ2UgTmV0d29yaxtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG2zG1swbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtIBtbMTszMm0qNTE2IxtbMG0gICAbWzE7MzFtQRtbMzJtThtbMzRtUxtbMzNtSRtbMDszMDs0N210ZXgbWzM3OzQwbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtIBtbMTszMm0qNTE2MiMgIBtbMzdtTmF2aWdhdGluZxtbMzNtIBtbMzFtQRtbMzJtThtbMzRtUxtbMzNtSRtbMDszMDs0N210ZXgbWzM3OzQwbQ0KIBtbMTszMm05G1szN20gQWJvdXQgG1szMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleBtbMzc7NDBtICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtsxtbMG0gG1sxOzMybSo5NSMgICAgG1szN21IZWxwG1swbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtIBtbMTszMm0qOTkjG1szN20gICAgTG9nIE9mZhtbMG0NChtbMW0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzMwbbMbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtsxtbMG0NCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzU7MzJtKjAjG1swOzFtIHRvIGdldCBiYWNrIGhlcmUgYW55dGltZRtbMG0NCg==","isPublic":1,"isAccessible":1,"type":"i","key":[null,11,2,null,null,null,null,null,null,516],"date":"2022-05-13T00:00:00.000Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":"980","index":"a","owner":9,"cost":0,"content":"G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMzFt2sQbWzFt3BtbMDszMm3axBtbMW3cG1swOzM0bdrEG1sxbdwbWzA7MzZtIBtbMTszM23fG1swbSAbWzMwOzQ3bdrC3NrE3Nog3CAbWzM3OzQwbQ0KIBtbMW3aIL/Cv7Pav9q/2sK/wr8bWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzFtsxtbMDszMW3f2xtbMTszMm2zG1swOzM2bSAbWzMybdsbWzE7MzRtLlwbWzA7MzRt3BtbMzNt3htbMW3bG1swbSAbWzMwOzQ3bSCzIMNf3Nrf3CAbWzM3OzQwbQ0KILOzs8PZs7Mgs7OzILPD2SAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbcTExMTExMTExMTExBtbMDszMDs0N23c3Nzc3Nzc3NzcG1szNzs0MG0NCiAbWzE7MzBtwMHZwdnAwNnA2cAg2cHZG1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtbm9kZTobWzBtIBtbMTszMm0bWG5vZGVpZDstMTEbXBtbMzRtDQobWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtZGF0ZTobWzA7MzJtIBtbMW0bWERBVEU6JVktJWItJWQ7LTExG1wbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG10aW1lOiAbWzMybRtYVElNRTstMTEbXBtbMG0NCg0KIBtbMW1XZWxjb21lLCB5b3UgaGF2ZSBjb25uZWN0ZWQgdG8gG1szMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleBtbMTszNzs0MG0gYSBCQlMgdGhhdCBpcyBiYXNlZCBvbiB0aGUbWzBtDQogG1sxbTE5ODAncyBWaWRlb3RleCBzZXJ2aWNlLCBidXQgdXNpbmcgQU5TSS4bWzBtDQoNCiAbWzFtRGVwZW5kaW5nIG9uIHdoaWNoIGNvdW50cnkgeW91IGxpdmUgaW4gdGhlIFZpZGVvdGV4IHNlcnZpY2Ugd2FzIGNhbGxlZBtbMG0NCiAbWzFtVmlhdGVsIChBVSksIFByZXN0ZWwgKFVLKSwgTWluaXRleCAoRlIpLCBUZWxpZG9uIChDQSksIEliZXJ0ZXggKFNQKSwgZXRjG1swbQ0KG1sxbQ0KG1swbSAbWzFtSWYgeW91IGdvdCBoZXJlIGJ5IG1pc3Rha2UsIHlvdSBzaG91bGQgZGlzY29ubmVjdCBub3csIG90aGVyd2lzZSwgeW91IGNhbhtbMG0NChtbMW0gcHJlc3MbWzBtIBtbMTs1OzMybTAbWzA7MW0gdG8gZ2V0IHRvIHRoZSBsb2dpbiBzY3JlZW4uG1swbQ0KG1sxbQ0KG1swbQ0K","isPublic":1,"isAccessible":1,"type":"i","key":[0,982,null,null,null,null,null,null,null,null],"date":"2020-07-05T12:57:03.790Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":981,"index":"a","owner":9,"cost":0,"content":"G1swbSAgICAgICAbWzFt+htbMG0gICAgICAgICAgG1sxbfobWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzMxbdrEG1sxbdwbWzA7MzJt2sQbWzFt3BtbMDszNG3axBtbMW3cG1swOzM2bSAbWzE7MzNt3xtbMG0gG1szMDs0N23awtzaxNzaINwgG1szNzs0MG0NCiAbWzFtwr/Cv8K/v9q/wy3Cv8K/wy2/2r/avxtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMxbbMbWzA7MzFt39sbWzE7MzJtsxtbMDszNm0gG1szMm3bG1sxOzM0bS5cG1swOzM0bdwbWzMzbd4bWzFt2xtbMG0gG1szMDs0N20gsyDDX9za39wgG1szNzs0MG0NCiCzIMPZs7OzwL+zILMgwrSzILOzs7OzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG3ExMTExMTExMTExMQbWzA7MzA7NDdt3Nzc3Nzc3Nzc3BtbMzc7NDBtDQogG1sxOzMwbdkbWzM2bSAbWzMwbcHZwbTZwNnA2dkbWzM2bSAbWzMwbcHBwNnZwNnZ2RtbMG0NCiAgICAgG1sxOzMwbcTZG1swbQ0KG1sxbSBQbGVhc2UgY29tcGxldGUgdGhlIHVzZXIgcmVnaXN0cmF0aW9uOhtbMG0NCg0KIBtbMTszMG2zG1swbSAgICAgICAbWzE7MzFtRW1haWw6G1swbSAbWzFtG19FTUFJTDs2MHQ7+RtcG1swbSAgG1sxOzMwbbMbWzBtDQogG1sxOzMwbbMbWzBtICAgICAbWzE7MzFtVXNlciBJRDobWzBtIBtbMW0bX1VTRVI7MjV0O/kbXBtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtDQogG1sxOzMwbbMbWzBtICAgIBtbMTszMW1QYXNzd29yZDobWzBtIBtbMW0bX1BBU1M7NDBwO/kbXBtbMG0gICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtDQogG1sxOzMwbbMbWzBtICAgG1sxOzMxbUZ1bGwgTmFtZTobWzBtIBtbMW0bX0ZVTExOQU1FOzI1dDv5G1wbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG2zG1swbQ0KIBtbMTszMG2zG1swbSAgICAgICAbWzE7MzFtVG9rZW46G1swbSAbWzFtG19UT0tFTjs2dDv5G1wbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtsxtbMG0NCg0KIBtbMW1UbyBjbGVhciB0aGUgY3VycmVudCBmaWVsZCwgdXNlIBtbMzJtKiobWzM3bS4bWzBtDQoNCiAbWzFtUmVnaXN0ZXJpbmcgYW5kIHVzaW5nIHRoaXMgc3lzdGVtLCB5b3UgYWdyZWUgdG8gYWJpZGUgYnkgdGhlIHN5c3RlbSBydWxlcy4bWzBtDQogG1sxbVlvdSBjYW4gdmlldyB0aG9zZSBydWxlcyBvbiBwYWdlIBtbMzJtKjk4OCMbWzM3bS4bWzBtDQo=","isPublic":1,"isAccessible":1,"type":"r","key":[980,"register",null,null,null,null,null,null,null,null],"date":"2020-07-09T11:42:40.643Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":"981","index":"b","owner":9,"cost":0,"content":"G1swbSAbWzFt2iC/wr+z2r/av9rCv8K/G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1szMW3axBtbMW3cG1swOzMybdrEG1sxbdwbWzA7MzRt2sQbWzFt3BtbMDszNm0gG1sxOzMzbd8bWzBtIBtbMzA7NDdt2sLc2sTc2iDcIBtbMzc7NDBtDQogs7Ozw9mzsyCzs7Mgs8PZICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzFtsxtbMDszMW3f2xtbMTszMm2zG1swOzM2bSAbWzMybdsbWzE7MzRtLlwbWzA7MzRt3BtbMzNt3htbMW3bG1swbSAbWzMwOzQ3bSCzIMNf3Nrf3CAbWzM3OzQwbQ0KIBtbMTszMG3AwdnB2cDA2cDZwCDZwdkbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtxMTExMTExMTExMTEG1swOzMwOzQ3bdzc3Nzc3Nzc3NwbWzM3OzQwbQ0KDQogG1sxbVdlbGNvbWUgdG8gG1szMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleBtbMTszNzs0MG0gYSBidWxsZXRpbiBib2FyZCBzeXN0ZW0gYmFzZWQgb24gYSBWaWRlb3RleCBidXQgd2l0aCBBTlNJIRtbMG0NCg0KG1sxbSBZb3VyIHVzYWdlIGhlcmUgaXMgZ292ZXJuZWQgYnkgc29tZSBydWxlcywgd2hpY2ggeW91IGNhbiBzZWUgb24gcGFnZSAbWzMybSo5ODgjG1szN20uG1swbQ0KDQobWzFtIFRvIGdldCB0byB0aGUgaG9tZSBwYWdlIGF0IGFueXRpbWUsIHVzZSAbWzMybSowIxtbMzdtLhtbMG0NCg0KG1sxbSBJZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIFZpZGVvdGV4LCB0aGVuIHlvdSBtaWdodCBsaWtlIHRvIHZpc2l0IHRoZSBoZWxwG1swbQ0KG1sxbSBwYWdlcyBhdCAbWzMybSo1MTYjLhtbMG0NCg0KG1sxbSBOYXZpZ2F0aW5nIGFyb3VuZCB0aGlzIEJCUyBpcyBkb25lIGJ5IHVzaW5nIHlvdXIga2V5Ym9hcmQgbnVtYmVyIGtleXMuIFRvIGdvG1swbQ0KG1sxbSB0byBhIHBhZ2UgZGlyZWN0bHksIHByZXNzIBtbMzJtKhtbMzdtIGZvbGxvd2VkIGJ5IHRoZSBwYWdlIG51bWJlciwgYW5kIHRoZW4gG1szMm0jG1szN20gdG8bWzBtDQobWzFtIHRlbGVwb3J0IHRoZXJlLhtbMG0NCg0KG1sxbSBTb21lIHBhZ2VzIHByb3ZpZGUgbmF2aWdhdGlvbiBvbiB0aGUgcGFnZSwgdXNpbmcgdGhlIGtleXMgG1szMm0wG1szN20tG1szMm05G1szN20uIE9uIHBhZ2UbWzBtDQobWzFtIG5hdmlnYXRpb24gaXMgbm9ybWFsbHkgb2J2aW91cywgYnV0IGlmIHlvdSB0cnkgaXQgYW5kIHRoZXJlIGlzbnQgYW55dGhpbmcgeW91G1swbQ0KG1sxbSB3aWxsIGdldCBhIG5vdGlmaWN0aW9uIG9uIHRoZSBzdGF0dXMgbGluZSBiZWxvdy4gTW9yZSBpbmZvcm1hdGlvbiBvbiAbWzMybSo1MTYjLhtbMG0NCg0KG1sxbSBQcmVzcyAbWzMybTAbWzM3bSB0byBnZXQgdGhlIGhvbWUgcGFnZS4bWzBtDQo=","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"date":"2023-12-30T12:57:03.790Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":983,"index":"a","owner":9,"cost":0,"content":"G1swbRtbMzFt2sQbWzFt3BtbMDszMm3axBtbMW3cG1swOzM0bdrEG1sxbdwbWzA7MzZtIBtbMTszM23fG1swbSAbWzMwOzQ3bdrC3NrE3Nog3CAbWzM3OzQwbQ0KG1sxOzMxbbMbWzA7MzFt39sbWzE7MzJtsxtbMDszNm0gG1szMm3bG1sxOzM0bS5cG1swOzM0bdwbWzMzbd4bWzFt2xtbMG0gG1szMDs0N20gsyDDX9za39wgG1szNzs0MG0NChtbMTszMG3ExMTExMTExMTExMQbWzA7MzA7NDdt3Nzc3Nzc3Nzc3BtbMzc7NDBtDQoNChtbMW1Mb2dpbiAbWzMxbUZBSUxFRBtbMzdtLhtbMG0NCg0KG1sxbVRvIHRyeSBhZ2FpbiwgcGxlYXNlIHByZXNzIBtbMzJtMBtbMG0NCg==","isPublic":1,"isAccessible":1,"type":"r","key":[0,null,null,null,null,null,null,null,null,null],"frame_fields": [],"date":"2020-08-09T11:42:40.643Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":98,"index":"a","owner":9,"cost":0,"content":"G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMzFt2sQbWzFt3BtbMDszMm3axBtbMW3cG1swOzM0bdrEG1sxbdwbWzA7MzZtIBtbMTszM23fG1swbSAbWzMwOzQ3bdrC3NrE3Nog3CAbWzM3OzQwbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMxbbMbWzA7MzFt39sbWzE7MzJtsxtbMDszNm0gG1szMm3bG1sxOzM0bS5cG1swOzM0bdwbWzMzbd4bWzFt2xtbMG0gG1szMDs0N20gsyDDX9za39wgG1szNzs0MG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG3ExMTExMTExMTExMQbWzA7MzA7NDdt3Nzc3Nzc3Nzc3BtbMzc7NDBtDQoNCg0KDQoNCg0KDQoNCg0KDQogICAgICAgICAbWzFtLiAgICAgLiAgIBtbMzBtsxtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtsxtbMG0NCiAgICAgICAbWzFt2r+/wr/avyC/2r8bWzBtIBtbMTszMG2zG1swbSAbWzE7MzFtVVNFUjobWzBtIBtbMW0bX1VTRVI7MjV0O/kbXBtbMG0gICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtDQogICAgICAgwL+zs7OzsyCzs7MgG1sxOzMwbbMbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtDQogICAgICAgG1sxOzMwbcDZ2cG02dkbWzM3bSAbWzMwbdnZ2RtbMzdtIBtbMzBtsxtbMG0gG1sxOzMxbVBBU1M6G1swbSAbWzFtG19QQVNTOzQwcDv5G1wbWzBtIBtbMTszMG2zG1swbQ0KICAgICAgICAgIBtbMTszMG3E2RtbMG0gICAgICAgG1sxOzMwbbMbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbbMbWzBtDQoNCg0KICAgICAgICAgICAgIBtbMTszMG1UbyByZWdpc3RlciBhbiBhY2NvdW50IGVudGVyG1swbSAbWzFtTkVXG1swbSAbWzE7MzBtZm9yIHRoZSB1c2VyIG5hbWUbWzBtDQogICAgICAgICAgICAgICAgG1sxOzMwbVVzZRtbMG0gG1sxOzMybSoqG1swbSAbWzE7MzBtdG8gY2xlYXIgeW91ciBpbnB1dBtbMG0gG1sxOzMybSowMBtbMG0gG1sxOzMwbXRvIHN0YXJ0IGFnYWluG1swbQ0K","isPublic":1,"isAccessible":1,"type":"l","key":[null,"login",null,null,null,null,null,null,null,null],"date":"2020-07-08T05:17:35.174Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":98,"index":"b","owner":9,"cost":0,"content":"G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMzFt2sQbWzFt3BtbMDszMm3axBtbMW3cG1swOzM0bdrEG1sxbdwbWzA7MzZtIBtbMTszM23fG1swbSAbWzMwOzQ3bdrC3NrE3Nog3CAbWzM3OzQwbQ0KIBtbMW3aIL/Cv7Pav9q/2sK/wr8bWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzFtsxtbMDszMW3f2xtbMTszMm2zG1swOzM2bSAbWzMybdsbWzE7MzRtLlwbWzA7MzRt3BtbMzNt3htbMW3bG1swbSAbWzMwOzQ3bSCzIMNf3Nrf3CAbWzM3OzQwbQ0KILOzs8PZs7Mgs7OzILPD2SAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbcTExMTExMTExMTExBtbMDszMDs0N23c3Nzc3Nzc3NzcG1szNzs0MG0NCiAbWzE7MzBtwMHZwdnAwNnA2cAg2cHZG1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtbm9kZTobWzBtIBtbMTszMm0bWG5vZGVpZDstMTEbXBtbMG0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxOzMwbWRhdGU6IBtbMTszMm0bWERBVEU6JVktJWItJWQ7LTExG1wbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG10aW1lOiAbWzE7MzJtG1hUSU1FOy0xMRtcG1swbQ0KDQogSGkbWzFtIBtYUkVBTE5BTUU7MjAbXA0KG1swbQ0KIFdlbGNvbWUgdG8bWzE7MzBtIBtbMzFtQRtbMzJtThtbMzRtUxtbMzNtSRtbMDszMDs0N210ZXgbWzM3OzQwbSwgYSBCQlMgaW5zcGlyZWQgYnkgVmlkZW90ZXgvVmlld2RhdGEgb2YgdGhlIDE5ODAncyBhbmQNCiAxOTkwJ3MuDQoNCiBUaGlzIEJCUyBpbnRlcmZhY2UgaXMgYnVpbGQgb24gdG9wIG9mIFN5bmNocm9uZXQgQkJTLCBhbmQgbWlnaHQgYmUgc29tZXRoaW5nDQogZGlmZmVyZW50IHRvIHdoYXQgeW91IGFyZSB1c2VkIHRvIHdpdGggb3RoZXIgU3luY2hyb25ldCBCQlMnLg0KG1sxOzMxbQ0KG1swbSBOYXZpZ2F0aW9uIGlzIHZpYSBwYWdlcywgd2hpY2ggY2FuIGJlIGFjY2Vzc2VkIHZpYSAbWzE7MzJtKhtbMDszMm1wYWdlbnVtG1sxbSMbWzBtLiBJbmZvcm1hdGlvbiBpcw0KIGF2YWlsYWJsZSB2aWEgcGFnZSAbWzE7MzJtKjUxNiMbWzBtLCB3aGljaCB3aWxsIGhlbHAgeW91IGdldCBzdGFydGVkIGlmIHlvdSBuZWVkIGl0Lg0KIFRvIGdldCB0byB0aGUgaG9tZSBwYWdlIGF0IGFueSB0aW1lLCB1c2UgG1sxOzMybSowIxtbMG0uDQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1sxbVByZXNzG1swbSAbWzE7NTszMm0wG1swOzE7MzNtIBtbMzdtTWFpbiBNZW51G1swbQ0K","isPublic":0,"isAccessible":1,"type":"i","key":[1,null,null,null,null,null,null,null,null,null],"date":"2020-07-08T01:30:48.608Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":998,"index":"a","owner":9,"cost":0,"content":"G1swbRtbMzFt2sQbWzFt3BtbMDszMm3axBtbMW3cG1swOzM0bdrEG1sxbdwbWzA7MzZtIBtbMTszM23fG1swbSAbWzMwOzQ3bdrC3NrE3Nog3CAbWzM3OzQwbQ0KG1sxOzMxbbMbWzA7MzFt39sbWzE7MzJtsxtbMDszNm0gG1szMm3bG1sxOzM0bS5cG1swOzM0bdwbWzMzbd4bWzFt2xtbMG0gG1szMDs0N20gsyDDX9za39wgG1szNzs0MG0NChtbMTszMG3ExMTExMTExMTExMQbWzA7MzA7NDdt3Nzc3Nzc3Nzc3BtbMzc7NDBtDQoNChtbMW1IbW0sIGEgc3lzdGVtIBtbMzFtRVJST1IbWzM3bSBvY2N1cnJlZC4bWzBtDQobWzFtTm9kZSAgOiAbWzMxbRtYbm9kZWlkOzgbXBtbMG0NChtbMW1TeXN0ZW06IBtbMzFtG1hCQlM7MTAbXBtbMG0NCg0KG1sxbUlmIHRoaXMga2VlcHMgaGFwcGVuaW5nLCB5b3UgbWF5IG5lZWQgdG8gdGVsbCB0aGUgc3lzdGVtIGFkbWluaXN0cmF0b3IbWzBtDQobWzFtdmlhIHBhZ2UgG1szMm0qMDgbWzBtDQoNChtbMW1UbyB0cnkgYWdhaW4sIHBsZWFzZSBwcmVzcyAbWzMybTAbWzBtDQo=","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"date":"2021-02-18T00:52:54.117Z"}
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":99,"index":"a","owner":9,"cost":0,"content":"G1swbSAgICAgICAgG1sxbdq/G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1szMW3axBtbMW3cG1swOzMybdrEG1sxbdwbWzA7MzRt2sQbWzFt3BtbMDszNm0gG1sxOzMzbd8bWzBtIBtbMzA7NDdt2sLc2sTc2iDcIBtbMzc7NDBtDQogG1sxbcK/2r/av9q0w7+/2sK/G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMW2zG1swOzMxbd/bG1sxOzMybbMbWzA7MzZtIBtbMzJt2xtbMTszNG0uXBtbMDszNG3cG1szM23eG1sxbdsbWzBtIBtbMzA7NDdtILMgw1/c2t/cIBtbMzc7NDBtDQogs7Ozs7Ozs7Ozs7Ozw9kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtxMTExMTExMTExMTEG1swOzMwOzQ3bdzc3Nzc3Nzc3NwbWzM3OzQwbQ0KIBtbMTszMG3BtMDZwNnA2cDZwLTB2RtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG1ub2RlOhtbMG0gG1sxOzMybRtYbm9kZWlkOy0xMRtcG1swbQ0KIBtbMTszMG3E2RtbMzFtICAgICAgICAbWzMwbcTZG1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtZGF0ZTobWzBtIBtbMTszMm0bWERBVEU6JVktJWItJWQ7LTExG1wbWzBtDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG10aW1lOhtbMG0gG1sxOzMybRtYVElNRTstMTEbXBtbMG0NCg0KIFlvdSBhcmUgYWJvdXQgdG8gZGlzY29ubmVjdCBmcm9tIBtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleBtbMzc7NDBtIBtbMW0bWFJFQUxOQU1FOzIwG1wbWzBtDQoNCiBJZiB5b3Ugd2FudGVkIHRvIHN0YXkgb25saW5lLCB5b3UgY2FuIHByZXNzIBtbMTszMm0qMCMbWzBtIHRvIGdldCBiYWNrIHRvIHRoZSBtYWluIG1lbnUNCg0KIE90aGVyd2lzZSBwcmVzcyAbWzE7NTszMm0jG1swOzE7MzNtIBtbMG10byBkaXNjb25uZWN0IG5vdy4NCg0KDQoNCg0KDQoNCg0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTs1OzMybSowIxtbMDsxbSB0byBnZXQgYmFjayB0byB0aGUgTWFpbiBNZW51G1swbQ0K","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"date":"2020-07-15T12:15:47.742Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":99,"index":"b","owner":9,"cost":0,"content":"G1swbSAgICAgICAgG1sxbdq/G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgG1szMW3axBtbMW3cG1swOzMybdrEG1sxbdwbWzA7MzRt2sQbWzFt3BtbMDszNm0gG1sxOzMzbd8bWzBtIBtbMzA7NDdt2sLc2sTc2iDcIBtbMzc7NDBtDQogG1sxbcK/2r/av9q0w7+/2sK/G1swbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMW2zG1swOzMxbd/bG1sxOzMybbMbWzA7MzZtIBtbMzJt2xtbMTszNG0uXBtbMDszNG3cG1szM23eG1sxbdsbWzBtIBtbMzA7NDdtILMgw1/c2t/cIBtbMzc7NDBtDQogs7Ozs7Ozs7Ozs7Ozw9kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtxMTExMTExMTExMTEG1swOzMwOzQ3bdzc3Nzc3Nzc3NwbWzM3OzQwbQ0KIBtbMTszMG3BtMDZwNnA2cDZwLTB2RtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG1ub2RlOhtbMG0gG1sxOzMybRtYbm9kZWlkOy0xMRtcG1swbSAgG1sxOzMwbcTZG1szMW0gICAgICAgIBtbMzBtxNkbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBtbMTszMG1kYXRlOhtbMG0gG1sxOzMybRtYREFURTolWS0lYi0lZDstMTEbXBtbMG0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzE7MzBtdGltZTobWzBtIBtbMTszMm0bWFRJTUU7LTExG1wbWzBtDQoNCiBUaGFua3MgZm9yIHZpc2l0aW5nIBtbMTszMW1BG1szMm1OG1szNG1TG1szM21JG1swOzMwOzQ3bXRleBtbMzc7NDBtIBtbMW0bWFJFQUxOQU1FOzIwG1wbWzBtDQoNCiBIb3BlIHRvIHNlZSB5b3UgYWdhaW4uLi4NCg==","isPublic":1,"isAccessible":1,"type":"t","key":[1,null,null,null,null,null,null,null,null,null],"date":"2020-07-08T01:48:01.797Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":1,"index":"a","owner":0,"cost":0,"content":"FzdjY2tzM3d7IzM1ICAgICAgIBEsbBJ8bBR8LBMsFzdrfyMzN2sjNRc1ampqIDQ1aiA1NSAgICAgICARf2sSf2oUL3wTfxc1aG8gPD0kLDUXdXp6enBxdXpwdTUgICAgICAgES8uEi8qFCwvEy8XdXB6cHF1enA1ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAU5PVEU6IEN1cnJlbnQgdW5kZXIgY29uc3RydWN0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICACKjUxNl8HQWJvdXQgQW5zaVRFWCAgICAgICAgICAgICAgICAgICAgAio5ODhfB1J1bGVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICACKjk5XwdEaXNjb25uZWN0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"i","key":[null,91,null,11,null,97,null,null,5160,516],"date":"2020-07-05T12:57:03.790Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":"980","index":"a","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICAgICAgICAgICAgIBF/axJ/ahQvfBN/FzVobyA8PSQsNSAgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgIA1XZWxjb21lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdlbGNvbWUsIHlvdSBoYXZlIGNvbm5lY3RlZCB0byAgICAgICAgIAFBAk4DUwRJB3RleCBhIEJCUyB0aGF0IGlzIGJhc2VkIG9uIHRoZSAgMTk4MCdzIFZpZGVvdGV4IHNlcnZpY2UuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBlbmRpbmcgb24gd2hpY2ggY291bnRyeSB5b3UgbGl2ZSBpbiAgdGhlIFZpZGVvdGV4IHNlcnZpY2Ugd2FzIGNhbGxlZCBWaWF0ZWwgIChBVSksIFByZXN0ZWwgKFVLKSwgTWluaXRlbCAoRlIpLCAgICAgICBUZWxpZG9uIChDQSksIEliZXJ0ZXggKFNQKSwgZXRjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElmIHlvdSBnb3QgaGVyZSBieSBtaXN0YWtlLCB5b3Ugc2hvdWxkICBkaXNjb25uZWN0IG5vdywgb3RoZXJ3aXNlIHlvdSBjYW4gICAgICAgcHJlc3MCMAd0byBnZXQgdG8gdGhlIGxvZ2luIHNjcmVlbi4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUbyBsb2dpbiB1c2luZyBTUVJMLCBwcmVzcwIx","isPublic":1,"isAccessible":1,"type":"i","key":[0,982,null,null,null,null,null,null,null,null],"date":"2020-07-05T12:57:03.790Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":981,"index":"a","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICAgAht5eXl5eXl5eRF/axJ/ahQvfBN/FzVobyA8PSQsNSAgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgDVJlZ2lzdGVyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICACKiogB3RvIGNsZWFyIGlucHV0ICAgICAgICAgICAgICAgICAgICAgAiowMAd0byBzdGFydCBhZ2FpbiAgICAgICBVc2UCXwd0byBFbnRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAXGiEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISAgAUVtYWlsICAgIDoHG2VlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWUgIAFVc2VyIElEICA6Bxtubm5ubm5ubm5ubm5ubm4gICAgICAgICAgICABUGFzc3dvcmQgOgcbcHBwcHBwcHBwcHBwcHBwICAgICAgICAgICAgAUZ1bGwgTmFtZToHG2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYgIAFUb2tlbiAgICA6Bxt6enp6enogICAgICAgICAgICAgICAgICAgICAXGjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdpc3RlcmluZyBhbmQgdXNpbmcgdGhpcyBzeXN0ZW0sIHlvdSAgYWdyZWUgdG8gYWJpZGUgYnkgdGhlIHN5c3RlbSBydWxlcy4gICAgIFNlZQIqOTg4XyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo=","isPublic":1,"isAccessible":1,"type":"r","key":[980,"register",null,null,null,null,null,null,null,null],"date":"2020-07-08T05:17:35.174Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":"981","index":"b","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICAgIAIwMDEwMDEwMRF/axJ/ahQvfBN/FzVobyA8PSQsNSAgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgIA1XZWxjb21lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdlbGNvbWUgdG8BQQJOBFMDSQd0ZXgsIGEgYnVsbGV0aW4gICAgICBib2FyZCBzeXN0ZW0gYmFzZWQgb24gdGhlIDE5ODAncyAgICAgICAgVmlkZW90ZXggc2VydmljZS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZb3VyIHVzYWdlIGhlcmUgaXMgZ292ZXJuZWQgYnkgc29tZSAgICAgcnVsZXMsIHdoaWNoIHlvdSBjYW4gc2VlIG9uIHBhZ2UCKjk4OF8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIFZpZGVvdGV4LCAgeW91IGNhbiBmaW5kIHNvbWUgaW5mb3JtYXRpb24gb24gcGFnZSAgAio1MTZfICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG8gZ2V0IHRvIHRoZSBob21lIHBhZ2UgYW55dGltZSwgdXNlICAgAiowXwdvciB1c2UCMAdub3cuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo=","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"date":"2023-12-30T12:57:03.790Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":983,"index":"a","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICACMDAwMTAwMDEwMRF/axJ/ahQvfBN/FzVobyA8PSQsNQcgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9naW4BDUZBSUxFRCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJlc3MCMAd0byB0cnkgYWdhaW4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJZiB0aGlzIGtlZXBzIGhhcHBlbmluZywgeW91IG1heSBsaWtlICAgdG8gcmVxdWVzdCB0aGF0IHlvdXIgcGFzc3dvcmQgaXMgICAgICAgIHJlc2V0LiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo=","isPublic":1,"isAccessible":1,"type":"r","key":[0,null,null,null,null,null,null,null,null,null],"frame_fields": [],"date":"2020-08-09T11:42:40.643Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":98,"index":"a","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICAgIAIwMDAxMDAwMRF/axJ/ahQvfBN/FzVobyA8PSQsNQcgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgIA1TaWduIEluICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAVVzZXIHLi4uLi4uLi4uLi4uLi4uICAgICAgICAgICAgICAgICAgIAFQYXNzBy4uLi4uLi4uLi4uLi4uLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbnRlcgJORVcHdG8gcmVnaXN0ZXIgbmV3IGFjY291bnQgICAgICAgUHJlc3MCKioHdG8gY2xlYXIgeW91ciBpbnB1dCAgICAgICAgICAgIFByZXNzAiowMAd0byBzdGFydCBhZ2Fpbgo=","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"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":98,"index":"b","owner":9,"cost":0,"content":"ESxsEnxsFHwsEywXN2t/IzM3ayM1ICAgICAgICACMDAwMTAwMDEwMRF/axJ/ahQvfBN/FzVobyA8PSQsNQcgICAgICAgICAgICAgICAgICARLy4SLyoULC8TLxd1cHpwcXV6cDUgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgIA1TaWduIEluICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdlbGNvbWUgdG8BQQJOA1MESQd0ZXguICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG8gZ2V0IHRvIHRoZSBtYWluIG1lbnUsIHlvdSBjYW4gICAgICAgIHByZXNzAiowXwdhbnkgdGltZS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJlc3MCMAd0byBjb250aW51ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":0,"isAccessible":1,"type":"i","key":[1,null,null,null,null,null,null,null,null,null],"date":"2020-07-08T01:30:48.608Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":998,"index":"a","owner":9,"cost":0,"content":"ICARLGwSfGwUfCwTLBc3a38jMzdrIzUgICAgICACMDAwMTAwMDEwMSAgEX9rEn9qFC98E38XNWhvIDw9JCw1ByAgICAgICAgICAgICAgICAgIBEvLhIvKhQsLxMvF3VwenBxdXpwNSAgICAgICAgICAgICAgICAgICAgVmlkZW90ZXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIbW0sIGFuAQ1FcnJvcgwHb2NjdXJyZWQuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElmIHRoaXMga2VlcHMgaGFwcGVuaW5nLCB5b3UgbWF5IGxpa2UgICB0byB0ZWxsIHRoZSBzeXN0ZW0gYWRtaW5pc3RyYXRvciB2aWEgICAgcGFnZQIqMDggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJlc3MCMAd0byByZXR1cm4gdG8gdGhlIG1haW4gbWVudS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"frame_fields": [],"date":"2020-08-09T11:42:40.643Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":999,"index":"a","owner":9,"cost":0,"content":"AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAwMRceD3MTGhYeHxgEDR0DRU5HSU5FRVJJTkcgEhwMHnMVDhEPFA8HMDIXHg9zExoWHh8YBA0dA0VOR0lORUVSSU5HIBIcDB5zFQ4RDxQPBzAyfn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn8wNBQaHnMRGRUAFQENIAUdAlRlc3QgUGFnZSAgHAweEnMWGBMAFxgBMDUUGh5zERkVABUBDSAFHQJUZXN0IFBhZ2UgIBwMHhJzFhgTABcYATA1AQABIAAgAR4gHiAXLBMTFhYSEhIVFRERFBQUICAUAAEAAQABAAEwN35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/MDgBAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABADA5fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn8xMAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAMTF+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+fzEyAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAxM35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/MTQBAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABADE1fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn9+f35/fn8xNldoaXRlA1llbGxvdwZDeWFuAkdyZWVuBU1hZ2VudGEBUmVkBEJsdWUXGiEiIxMkJSYnFigpKisSLC0uLxkwMTIzFTQ1NjcRODk6OxQ8PT4/ICAhIiMgJCUmJyAoKSorICwtLi8gMDEyMyA0NTY3IDg5OjsgPD0+PyBAQUJDIERFRkcgSElKSyBMTU5PIFBRUlMgVFVWVyBYWVpbIFxdXl8gYGFiYyBkZWZnIGhpamsgbG1ubyBwcXJzIHR1dncgeHl6eyB8fX5/FGBhYmMRZGVmZxVoaWprEmxtbm8acHFycxZ0dXZ3E3h5ensXfH1+fwMYQ29uY2VhbAhGbGFzaAMqCwtCb3gJU3RlYWR5GEdvbmU/Fl5/","isPublic":1,"isAccessible":1,"type":"i","key":[1,null,null,null,null,null,null,null,null,null],"date":"2019-10-27T00:52:54.117Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":999,"index":"b","owner":9,"cost":0,"content":"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAINQ2hhcmFjdGVyIE1hcAwHNyBiaXQgY2hhcmFjdGVyIHNldCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICABICAwIDEgMiAzIDQgNSA2IDcgOCA5IGEgYiBjIGQgZSBmICAgICAgATAAYgFyAmcDeQRiBW0GYwd3CGYJcyBFIFMgTiBIIEQgZCAgICAgIAExEEIRUhJHE1kUQhVNFkMXVwdDIE8gbyBzIEIgYiBIIHIgICAgICABMgcgICEgIiAjICQgJSAmICcgKCApICogKyAsIC0gLiAvICAgICAgATMHMCAxIDIgMyA0IDUgNiA3IDggOSA6IDsgPCA9ID4gPyAgICAgIAE0B0AgQSBCIEMgRCBFIEYgRyBIIEkgSiBLIEwgTSBOIE8gICAgICABNQdQIFEgUiBTIFQgVSBWIFcgWCBZIFogWyBcIF0gXiBfICAgICAgATYHYCBhIGIgYyBkIGUgZiBnIGggaSBqIGsgbCBtIG4gbyAgICAgIAE3B3AgcSByIHMgdCB1IHYgdyB4IHkgeiB7IHwgfSB+IH8gICAgAiBHcmFwaGljcyBDaGFycyAgICAgICAgICAgICAgICAgICAgICAgICAaATIXICAhICIgIyAkICUgJiAnICggKSAqICsgLCAtIC4gLyAgICAgGgEzFzAgMSAyIDMgNCA1IDYgNyA4IDkgOiA7IDwgPSA+ID8gICAgIBoBNhdgIGEgYiBjIGQgZSBmIGcgaCBpIGogayBsIG0gbiBvICAgICAaATcXcCBxIHIgcyB0IHUgdiB3IHggeSB6IHsgfCB9IH4gfyAgICACDVNwZWNpYWwgQ29kZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICABMDgvMDkHRmxhc2gvU3RlYWR5IAExOC8xRgdDb25jZWFsL1Nob3cgATBBLzBCB0VuZC9TdGFydCBCb3gBMUMHQmxrIEJhY2sgICAgICAgIAEwQy8wRAdOb3JtYWwvRG91YmxlATFEB05ldyBCYWNrICAgICAgICABMEUvMEYHRCBXaWRlL1NpemUgIAUxOS8xQQZTb2xpZC9CbG9jayAgATFFLzFGB0hvbGQvUmVsZWFzZSBNb3NpYWMgIAUxQgZDU0kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"i","key":[1,null,null,null,null,null,null,null,null,null],"date":"2019-10-27T00:52:54.117Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":99,"index":"a","owner":9,"cost":0,"content":"Fzcrby8/Py9vICAgICAgICAgIBEsbBJ8bBR8LBMsFzdrfyMzN2sjNRc1amogJTVzeiAgICAgICAgICARf2sSf2oUL3wTfxc1aG8gPD0kLDUXdXJ6c3F1c3ogICAgICAgICAgES8uEi8qFCwvEy8XdXB6cHF1enA1ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWW91IGFyZSBhYm91dCB0bwFkaXNjb25uZWN0B2Zyb20gICAgICAgAUECTgRTA0kHdGV4LiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG8gaGVhZCBiYWNrIHRvIHRoZSBtYWluIG1lbnUsIHBsZWFzZSAgIHVzZQIqMF8HICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3RoZXJ3aXNlIHRvIGRpc2Nvbm5lY3QgcHJlc3MCXyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA1UaGFua3MgZm9yIGNhbGxpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA==","isPublic":1,"isAccessible":1,"type":"i","key":[0,null,null,null,null,null,null,null,null,null],"date":"2020-07-15T12:15:47.742Z"}
|
|
@ -1 +0,0 @@
|
|||||||
{"version":1,"frame":99,"index":"b","owner":9,"cost":0,"content":"Fzcrby8/Py9vICAgICAgICAgIBEsbBJ8bBR8LBMsFzdrfyMzN2sjNRc1amogJTVzeiAgICAgICAgICARf2sSf2oUL3wTfxc1aG8gPD0kLDUXdXJ6c3F1c3ogICAgICAgICAgES8uEi8qFCwvEy8XdXB6cHF1enA1ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZpZGVvdGV4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANVGhhbmtzIGZvciBjYWxsaW5nICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSG9wZSB0byBzZWUgeW91IGFnYWluLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo=","isPublic":1,"isAccessible":1,"type":"t","key":[1,null,null,null,null,null,null,null,null,null],"date":"2020-07-08T01:48:01.797Z"}
|
|
243
tools/export.js
243
tools/export.js
@ -1,243 +0,0 @@
|
|||||||
load('smbdefs.js');
|
|
||||||
|
|
||||||
// ANSItex specific includes
|
|
||||||
load('ansitex/load/defs.js');
|
|
||||||
load('ansitex/load/funcs.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Work out which key should be used for a page, or validate if a key used is valid
|
|
||||||
*
|
|
||||||
* @param page
|
|
||||||
* @param signed
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function getKey(page,signed) {
|
|
||||||
log(LOG_DEBUG,'+ CHECKING key:'+signed+' for page: '+page);
|
|
||||||
|
|
||||||
var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini'));
|
|
||||||
var match = '';
|
|
||||||
|
|
||||||
if (f.open("r")) {
|
|
||||||
var BreakException = {};
|
|
||||||
|
|
||||||
// Default Key
|
|
||||||
var key = f.iniGetValue('prefix','key');
|
|
||||||
|
|
||||||
try {
|
|
||||||
f.iniGetSections("prefix:").forEach(function (prefix) {
|
|
||||||
var p = prefix.substr(7);
|
|
||||||
var pk = f.iniGetValue(prefix, 'Key', '');
|
|
||||||
var re = new RegExp('^' + p, 'g');
|
|
||||||
|
|
||||||
// If it was signed, is the key a value one
|
|
||||||
if (signed && signed.length) {
|
|
||||||
if (pk === signed) {
|
|
||||||
//print('SIGNED key is valid:' + signed);
|
|
||||||
key = signed;
|
|
||||||
throw BreakException;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Which key should sign this page
|
|
||||||
} else if (page.toString().match(re) && (p.length > match.length)) {
|
|
||||||
match = p;
|
|
||||||
key = pk;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
if (e !== BreakException) throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'- key:'+key);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export message from message base
|
|
||||||
*
|
|
||||||
* @param msgbase
|
|
||||||
*/
|
|
||||||
function msgBaseExport(msgbase) {
|
|
||||||
var ini = new File(msgbase.file+'.ini');
|
|
||||||
var i=0;
|
|
||||||
|
|
||||||
if (ini.open("r")) {
|
|
||||||
export_ptr=ini.iniGetValue('videotex','export_ptr',0);
|
|
||||||
ini.close();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to open INI for ['+msgbase.file+']');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If pointer doesnt exist, reset it from the message base.
|
|
||||||
if (export_ptr === undefined) {
|
|
||||||
var f = new File(file_getcase(msgbase.file+'.sbl'));
|
|
||||||
|
|
||||||
if (f.open('rb')) {
|
|
||||||
export_ptr = f.readBin(4);
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
highest = export_ptr;
|
|
||||||
var total_msgs = msgbase.total_msgs;
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'| msgBaseExport: export_ptr='+export_ptr+'|last_msg='+msgbase.last_msg+'|total_msgs='+total_msgs);
|
|
||||||
|
|
||||||
if (msgbase.last_msg >= export_ptr)
|
|
||||||
i = total_msgs-(msgbase.last_msg-export_ptr);
|
|
||||||
|
|
||||||
for (; i<total_msgs; i++) {
|
|
||||||
if (js.terminated)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var idx = msgbase.get_msg_index(true,i);
|
|
||||||
if (! idx) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Reading index of msg offset ['+i+'] : ['+msgbase.error+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip message until we get to our last one read.
|
|
||||||
if (idx.number <= export_ptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (idx.number > highest)
|
|
||||||
highest = idx.number;
|
|
||||||
|
|
||||||
// Only check messages from Videotex
|
|
||||||
if (idx.from !== crc16_calc('videotex')) {
|
|
||||||
log(LOG_DEBUG,'! IGNORING: Message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the message header
|
|
||||||
var hdr = msgbase.get_msg_header(true,i);
|
|
||||||
|
|
||||||
// Ignore deleted messages
|
|
||||||
if (hdr.attr&MSG_DELETE) {
|
|
||||||
log(LOG_DEBUG,'! IGNORING: Deleted message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore locally posted messages
|
|
||||||
if (! hdr.from_net_type) {
|
|
||||||
log(LOG_DEBUG,'! IGNORING: Local message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log(LOG_DEBUG,'+ PROCESSING: Message offset ['+i+'] in ['+msgbase.file+'] from ['+hdr.from_net_addr+'] ('+hdr.subject+')');
|
|
||||||
|
|
||||||
var body = msgbase.get_msg_body(
|
|
||||||
/* by_offset: */true,
|
|
||||||
i,
|
|
||||||
/* strip Ctrl-A */true,
|
|
||||||
/* rfc822-encoded: */false,
|
|
||||||
/* include tails: */false);
|
|
||||||
|
|
||||||
t = new File(system.temp_dir+'videotex.gpg');
|
|
||||||
if (! t.open('w+')) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to open temp file ['+system.temp_dir+'videotex.gpg'+']');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
t.write(base64_decode(body));
|
|
||||||
t.close();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
log(LOG_ERROR,error);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the signature is good
|
|
||||||
result = system.exec('gpgv --keyring '+system.mods_dir+'ansitex/keys/pubring.kbx '+system.temp_dir+'videotex.gpg');
|
|
||||||
|
|
||||||
if (result !== 0 ) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Invalid Signature for message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists(system.temp_dir+'videotex.tex'))
|
|
||||||
file_remove(system.temp_dir+'videotex.tex')
|
|
||||||
|
|
||||||
// Check that the signature is allowed to author the frames
|
|
||||||
result = system.exec('gpg --homedir '+system.mods_dir+'ansitex/keys --batch --status-fd 2 -o '+system.temp_dir+'videotex.tex '+system.temp_dir+'videotex.gpg 2>'+system.temp_dir+'videotex.log');
|
|
||||||
|
|
||||||
if (result !== 0 ) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Failed to extract message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put frame in place.
|
|
||||||
f = new File(system.temp_dir+'videotex.tex');
|
|
||||||
if (! f.exists || ! f.open('r')) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Failed to open frame for message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
frame = JSON.parse(f.read());
|
|
||||||
x = new Frame(0);
|
|
||||||
frame.render = x.render;
|
|
||||||
|
|
||||||
// @todo Figure out how to delete this duplicate code
|
|
||||||
Object.defineProperty(frame,'page', {
|
|
||||||
get: function() {return this.frame+this.index}
|
|
||||||
});
|
|
||||||
|
|
||||||
x = null;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
log(LOG_ERROR,error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the key that signed the message
|
|
||||||
f = new File(system.temp_dir+'videotex.log');
|
|
||||||
if (! f.exists || ! f.open('r')) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Failed to open gpg log for message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var signed = '';
|
|
||||||
while (string = f.readln()) {
|
|
||||||
var matches = string.match(/\s+GOODSIG\s+.*\<(.*)\>/);
|
|
||||||
|
|
||||||
if (matches) {
|
|
||||||
signed = matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
if (signed !== getKey(frame.frame,signed)) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Key ['+signed+' is not authorised for message offset ['+i+'] in ['+msgbase.file+']');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the frame.
|
|
||||||
log(LOG_INFO,'Updating page ['+frame.page+'] from ['+hdr.from_net_addr+'] signed with ['+signed+']');
|
|
||||||
saveFrame(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ini.open(file_exists(ini.name) ? 'r+':'w+')) {
|
|
||||||
ini.iniSetValue('videotex','export_ptr',highest);
|
|
||||||
ini.close();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to save to INI for ['+msgbase.file+']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgbase = new MsgBase(findMsgBase(null).code);
|
|
||||||
log(LOG_DEBUG,'+ ANSITEX_EXPORT: Open message base in ['+msgbase.file+']');
|
|
||||||
|
|
||||||
if (! msgbase.open()) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to open ['+msgbase.file+']');
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
msgBaseExport(msgbase);
|
|
||||||
msgbase.close();
|
|
@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Load a frame, optionally with a new ANSI/BIN and load it into the msgbase.
|
|
||||||
*/
|
|
||||||
|
|
||||||
load('ansitex/load/funcs.js');
|
|
||||||
// Our page handler
|
|
||||||
load('ansitex/load/page.js');
|
|
||||||
|
|
||||||
/* parse command arguments */
|
|
||||||
if (argv.length !== 3) {
|
|
||||||
writeln('! ERROR: Need 3 arguments only');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var frame = argv.shift();
|
|
||||||
var index = argv.shift();
|
|
||||||
var file = argv.shift();
|
|
||||||
|
|
||||||
var ext = file_getext(file).substr(1).toLowerCase();
|
|
||||||
|
|
||||||
// Type of frame to load
|
|
||||||
switch (ext) {
|
|
||||||
case 'tex':
|
|
||||||
case 'ans':
|
|
||||||
require('ansitex/load/session/ansitex.js','SESSION_ANSITEX');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'vtx':
|
|
||||||
case 'bin':
|
|
||||||
require('ansitex/load/session/viewdata.js','SESSION_VIEWDATA');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var page = new Page();
|
|
||||||
if (page.get(new PageObject(frame,index))) {
|
|
||||||
page.import(file,ext);
|
|
||||||
page.save();
|
|
||||||
|
|
||||||
} else if (['vtx','tex'].indexOf(ext) !== -1) {
|
|
||||||
page.import(file,ext);
|
|
||||||
page.save();
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
/**
|
|
||||||
* This will go through our videotex and ansitex frames and check for inconsistencies.
|
|
||||||
*
|
|
||||||
* We'll check for:
|
|
||||||
* + Mismatched metadata items
|
|
||||||
* + Out of sync content
|
|
||||||
*/
|
|
||||||
|
|
||||||
load('ansitex/load/funcs.js');
|
|
||||||
// Our page handler
|
|
||||||
load('ansitex/load/page.js');
|
|
||||||
|
|
||||||
/* parse command arguments */
|
|
||||||
if (argv.length !== 1) {
|
|
||||||
writeln('! ERROR: Need only 1 argument');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//const vtx_ext = 'tex';
|
|
||||||
const vtx_src = 'bin';
|
|
||||||
const tex_src = 'ans';
|
|
||||||
|
|
||||||
const page = argv.shift();
|
|
||||||
const vtx_srcname = page+'.'+vtx_src;
|
|
||||||
const tex_srcname = page+'.'+tex_src;
|
|
||||||
var errors = false;
|
|
||||||
|
|
||||||
PAGE_FILE_PREFX = /^[0-9]+[a-z]$/;
|
|
||||||
|
|
||||||
if (! PAGE_FILE_PREFX.test(page)) {
|
|
||||||
writeln('PAGE is not a frame: '+page);
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln('Comparing Frame: '+page);
|
|
||||||
|
|
||||||
// Load frame
|
|
||||||
require(ANSITEX_HOME+'/load/session/viewdata.js','SESSION_VIEWDATA');
|
|
||||||
vtx = new Page();
|
|
||||||
if (! vtx.import(FRAMES_HOME+SESSION_EXT+'/'+page)) {
|
|
||||||
writeln('- ! ERROR: VTX File doesnt exist? :'+page);
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Check content between TEX/ANS & VTX/BIN
|
|
||||||
vtx_srcfile = new File(FRAMES_HOME+SESSION_EXT+'/'+vtx_srcname);
|
|
||||||
|
|
||||||
if (! vtx_srcfile.exists || ! vtx_srcfile.open('r')) {
|
|
||||||
writeln('- ! ERROR: VTX SRC File doesnt exist? :'+vtx_srcname);
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln('- LOADING: VTX Source :'+vtx_srcname);
|
|
||||||
|
|
||||||
var x = md5_calc(vtx.raw);
|
|
||||||
var y = md5_calc(vtx_srcfile.read());
|
|
||||||
|
|
||||||
// Check Content
|
|
||||||
if (x !== y) {
|
|
||||||
writeln(' - Page Content :'+x);
|
|
||||||
writeln(' - Source Content :'+y);
|
|
||||||
writeln('- ! WARNING: Content Differs.');
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln('= Source matches.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
require(ANSITEX_HOME+'/load/session/ansitex.js','SESSION_ANSITEX');
|
|
||||||
tex = new Page();
|
|
||||||
if (! tex.import(FRAMES_HOME+SESSION_EXT+'/'+page)) {
|
|
||||||
writeln('- ! ERROR: TEX File doesnt exist? :'+page);
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Check content between TEX/ANS & VTX/BIN
|
|
||||||
tex_srcfile = new File(FRAMES_HOME+SESSION_EXT+'/'+tex_srcname);
|
|
||||||
if (! tex_srcfile.exists || ! tex_srcfile.open('r')) {
|
|
||||||
writeln('- ! ERROR: TEX SRC File doesnt exist? :'+tex_srcname);
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln('- LOADING: TEX Source :'+tex_srcname);
|
|
||||||
|
|
||||||
var x = md5_calc(tex.raw);
|
|
||||||
var y = md5_calc(tex_srcfile.read());
|
|
||||||
|
|
||||||
if (x !== y) {
|
|
||||||
// Check Content
|
|
||||||
writeln(' - Page Content :'+x);
|
|
||||||
writeln(' - Source Content :'+y);
|
|
||||||
writeln('- ! WARNING: Content Differs.');
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln('= Source matches.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking keys
|
|
||||||
if (vtx.raw && tex.raw) {
|
|
||||||
writeln('- Checking Page: ');
|
|
||||||
|
|
||||||
if (vtx.name.toString() !== tex.name.toString()) {
|
|
||||||
writeln(' - ! VTX: '+vtx.name.toString());
|
|
||||||
writeln(' - ! TEX: '+tex.name.toString());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln(' = PAGE: '+vtx.name.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
for each (var k in ['key','cost','type']) {
|
|
||||||
writeln('- Checking KEY: '+k);
|
|
||||||
|
|
||||||
if (JSON.stringify(vtx[k]) !== JSON.stringify(tex[k])) {
|
|
||||||
writeln(' - ! VTX: '+vtx[k]);
|
|
||||||
writeln(' - ! TEX: '+tex[k]);
|
|
||||||
errors = true;
|
|
||||||
} else {
|
|
||||||
writeln(' = KEY: '+vtx[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for each (var k in ['isAccessible','isPublic']) {
|
|
||||||
writeln('- Checking Property: '+k);
|
|
||||||
|
|
||||||
if (JSON.stringify(vtx.__properties__[k]) !== JSON.stringify(tex.__properties__[k])) {
|
|
||||||
writeln(' - ! VTX: '+vtx.__properties__[k]);
|
|
||||||
writeln(' - ! TEX: '+tex.__properties__[k]);
|
|
||||||
errors = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
writeln(' = KEY: '+vtx.__properties__[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors)
|
|
||||||
exit(1);
|
|
||||||
else
|
|
||||||
writeln('= OK');
|
|
@ -1,19 +0,0 @@
|
|||||||
load('load/string.js');
|
|
||||||
load('load/funcs.js');
|
|
||||||
load('ansitex/load/msgbases.js');
|
|
||||||
|
|
||||||
if (argv.length !== 1) {
|
|
||||||
writeln('ERROR: Need a msgbase page prefix');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ma = new MsgAreas();
|
|
||||||
area = ma.getArea(argv[0]);
|
|
||||||
|
|
||||||
writeln('Opening ['+argv[0]+'] - ('+area.msgbase.cfg.code+')');
|
|
||||||
writeln('- First:'+area.msgbase.first_msg);
|
|
||||||
writeln('- Last:'+area.msgbase.last_msg);
|
|
||||||
|
|
||||||
for (var x in area.headers) {
|
|
||||||
writeln(padright(area.headers[x].number,4,' ')+':'+area.headers[x].tags);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Go through our messages and tag a frame id for messages without one.
|
|
||||||
* @note: May need to run jsexec with -m 32MB to overcome memory issues
|
|
||||||
*/
|
|
||||||
|
|
||||||
load('load/string.js');
|
|
||||||
load('ansitex/load/msgbases.js');
|
|
||||||
|
|
||||||
const ma = new MsgAreas()
|
|
||||||
for (var i=0;i<ma.areas.length;i++) {
|
|
||||||
if (argv[0] && (argv[0] !== ma.areas[i].msgbase.cfg.code))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
writeln('Area : '+ma.areas[i].full_name);
|
|
||||||
writeln('Total Messages : '+ma.areas[i].headers.length);
|
|
||||||
writeln('- Tagged Messages : '+ma.areas[i].list_tagged.length);
|
|
||||||
writeln('- Untagged Messages: '+ma.areas[i].list_untagged.length);
|
|
||||||
|
|
||||||
ma.areas[i].tag_msgs();
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
load('ansitex/load/msgbases.js');
|
|
||||||
|
|
||||||
var ma = new MsgAreas();
|
|
||||||
ma.list;
|
|
119
tools/save.js
119
tools/save.js
@ -1,119 +0,0 @@
|
|||||||
// ANSItex specific includes
|
|
||||||
load('ansitex/load/defs.js');
|
|
||||||
load('ansitex/load/funcs.js');
|
|
||||||
|
|
||||||
var options = loadOptions();
|
|
||||||
|
|
||||||
// Modes of operation:
|
|
||||||
// -s -p x -i y = To send a stored page via the msgbase
|
|
||||||
// {-s} -p x -i y -f file = To update (and optionally send) a page with new content
|
|
||||||
// -all details = to create a new page
|
|
||||||
|
|
||||||
// Import
|
|
||||||
send = (argv.indexOf('-s') >= 0);
|
|
||||||
// Page
|
|
||||||
p = getArg('-p','No page specified with -p',true);
|
|
||||||
// Index
|
|
||||||
i = getArg('-i','No index specified with -i',true);
|
|
||||||
// File to convert
|
|
||||||
file = getArg('-f','No file specified with -f',false);
|
|
||||||
|
|
||||||
frame = new TexFrame();
|
|
||||||
frame.load(pageStr({frame: p,index: i}));
|
|
||||||
|
|
||||||
if (! send || ! frame || file) {
|
|
||||||
if (frame.page === null) {
|
|
||||||
frame = new TexFrame();
|
|
||||||
frame.frame = p;
|
|
||||||
frame.index = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key
|
|
||||||
key = getArg('-k','No index specified with -k',false);
|
|
||||||
// Cost
|
|
||||||
cost = getArg('-c','No index specified with -c',false);
|
|
||||||
// Owner
|
|
||||||
owner = getArg('-o','No owner specified with -o',false);
|
|
||||||
// Owner
|
|
||||||
type = getArg('-t','No type specified with -t',false);
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
f = new File(file);
|
|
||||||
if (! f.exists || ! f.open('r')) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to open ['+file+']');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.content = base64_encode(f.read());
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (owner) {
|
|
||||||
frame.owner = base64_encode(owner.replace(/\\1/g,"\1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
frame.key = key.split(',').map(function(t){return parseInt(t)});
|
|
||||||
|
|
||||||
if (frame.key.length !== 10) {
|
|
||||||
log(LOG_ERROR,'! ERROR: Must specify 10 keys with -k');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public
|
|
||||||
if (argv.indexOf('-P') >= 0)
|
|
||||||
frame.isPublic = true;
|
|
||||||
|
|
||||||
if (argv.indexOf('-A') >= 0)
|
|
||||||
frame.isAccessible = true;
|
|
||||||
|
|
||||||
if (cost)
|
|
||||||
frame.cost = cost;
|
|
||||||
|
|
||||||
if (type)
|
|
||||||
frame.type = type; // @todo validate this is a valid type.
|
|
||||||
|
|
||||||
// Date
|
|
||||||
frame.date = new Date().toISOString();
|
|
||||||
|
|
||||||
// Final validation
|
|
||||||
if (! frame.owner) {
|
|
||||||
log(LOG_ERROR,'! ERROR: No owner specified ['+file+']');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the frame in file
|
|
||||||
frame.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// @NOTE: We need to use a binary signature then base64 encode it, as mailers may strip 0x0a while messages are in transit.
|
|
||||||
if (send === 1 && options.gpg_key) {
|
|
||||||
if (! file) {
|
|
||||||
file = system.mods_dir+'ansitex/text/'+frame.page+'.tex';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! file_exists(file)) {
|
|
||||||
log(LOG_ERROR,'! ERROR: File '+file+' doesnt exist?');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists(file+'.asc'))
|
|
||||||
file_remove(file+'.asc')
|
|
||||||
|
|
||||||
result = system.exec('gpg --homedir '+system.mods_dir+'ansitex/keys --clearsign --batch --local-user '+options.gpg_key+' -s '+file);
|
|
||||||
w = new File(file+'.asc');
|
|
||||||
|
|
||||||
if (w.open('r')) {
|
|
||||||
msg = base64_encode(w.read());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log(LOG_ERROR,'! ERROR: Unable to send with GPG for '+frame.page + ' Error: '+w.error);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
w.close();
|
|
||||||
|
|
||||||
msgBaseImport(null,frame.page,msg);
|
|
||||||
printf('GPG Result: %s',result);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user