The package represents a teletext service, and is a wrapper for @techandsoftware/teletext. It adds page numbers, subpage and fastext navigation (colour buttons). By default, pages are fetched as JSON over HTTP, but that can be overriden by your own page fetcher. It supports casting pages to Chromecast using @techandsoftware/teletext-caster, and higher-resolution mosaic graphics using a pixel-art scaling algorithm via @techandsoftware/teletext-plugin-smooth-mosaic.
This implements the display portion of teletext and wraps it up as a web app. It's not an emulator as it doesn't decode the VBI transmission part like a TV or teletext display adapter.
Live demo
See https://teletextmoduledemo.tech-and-software.ltd.uk/teletext-service-demo.html
Usage
The TeletextService
class handles page retrieval and display. The TeletextServiceViewer
class is higher level with support for page numbers, subpages and fastext (coloured button links), and is tightly coupled to the provided HTML UI.
TeletextService class
Minimum code needed to use the TeletextService
class:
<div id="teletextscreen"></div>
<script type="module">
import { TeletextService } from './dist/teletext-service.min.js';
const service = new TeletextService({
DOMSelector: '#teletextscreen'
});
service.showPage("100");
// Will fetch 1.json and get page 100 from that,
// and display in the HTML <div>
</script>
You will need to supply your own page number entry method.
The JSON structure needed is described below.
TeletextServiceViewer class
TeletextServiceViewer
is a web app wrapper around TeletextService
. It handles page number entry, subpage nav, fastext button state changes and entry, reveal and mix, with control using the webapp UI or keyboard shortcuts. It's tightly-coupled to the HTML so it's not really an API but it does support some options. It's most likely you'll use the code as it is or fork it. The class also incorporates teletext-caster so that pages can be viewed on a Chromecast in supporting browsers.
The Javascript code is invoked with:
<div id="teletextscreen"></div>
<script type="module">
import { TeletextServiceViewer } from './dist/teletext-service.min.js';
new TeletextServiceViewer();
</script>
For the rest of HTML needed, see public/index.html
in the repo.
To run locally, clone the project then run npm install
and npm run dev
.
Use scripts/tti2json.js
to generate the JSON files needed as the page data source, or create your own. The JSON structure needed is documented below in the 'Default page data source' section.
tti2json script
scripts/tti2json.js
is a Node.js script, which reads a directory containing multiple .tti
files and converts to the JSON files and structure used by the default page fetcher.
Requirements: It needs node v16 as it uses ECMAScript modules.
Usage:
node scripts/tti2json.js sourceDirectory targetDirectory
sourceDirectory
is the directory containing the .tti
files
targetDirectory
is where to write the generated JSON files. They're named 1.json
to 8.json
, and will overwrite any existing files with the same name. It defaults to the current directory.
You might need to modify the script to change the regex used for getting the list of .tti
files. It's in the go()
function near the bottom.
If you get the error Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
when you run, then check the node version, as you need v16.
If you're looking where to get the .tti files, then see https://zxnet.co.uk/teletext/emulators/ for some sources from various repositories, such as Teefax.
Licensing
The core project is licensed under GNU Affero General Public License 3 (AGPL-3.0-only), or under a commercial software license (LicenseRef-uk.ltd.TechAndSoftware-1.0) if you have paid a licensing fee to Tech and Software Ltd. If you combine your own software with this package and distribute publically (whether via network access or not), the AGPL requires that your software is covered by AGPL; the commercial license does not have that requirement. In order to pay the fee for the commercial license, contact techandsoftwareltd@outlook.com for enquiries. The text of the licenses is in the LICENSES
directory.
Dependencies:
- The fonts in @techandsoftware/teletext-fonts are public domain
- @techandsoftware/teletext-plugin-smooth-mosaic is loaded via cdn.jsdelivr.net, and uses LGPL-2.1-or-later
- js-hqx uses LGPL-2.1-or-later
TeletextService and TeletextServiceViewer API
new TeletextService(options)
Creates a teletext service instance. options
is required, and has the following properties:
{
DOMSelector: string,
defaultG0Charset?: string,
caster?: object,
header?: string,
fetcher?: object,
baseURL?: string
}
-
DOMSelector
is required, and is a selector string for the teletext screen container, e.g.#teletextscreen
-
defaultG0Charset
sets the default G0 character set on the teletext instance. If not passed in, the default character set isg0_latin
. SeesetDefaultG0Charset()
in @techandsoftware/teletext for the available values -
caster
- pass in attxcaster
from @techandsoftware/teletext-caster and this will be used to show pages on the connected Chromecast -
header
is the string to use as the header row. If not passed in, a default header row is used. See below for tokens that can be used in the header. -
fetcher
- pass in an object used to fetch teletext pages. If this is not passed in, then pages are expected to be in JSON and retrieved from same directory of the web page containing the teletext instance. See the 'Default page data source' section on the expected data format for the default fetcher. See the 'fetcher object' section below for details on passing in your own fetcher. -
baseURL
- if you use the default fetcher, then you can pass in a URL to use as the base URL for getting JSON content. The default base URL is the current directory of the URL running the javascript code. The URL should include a trailing/
header
format
The header
string is 32 characters and can contain the following tokens:
Token | Used for | Length |
---|---|---|
%%# |
Page number | 3 characters |
%%a |
Day | 3 characters |
%%b |
Month | 3 characters |
%d |
Date | 2 digits with leading 0 |
%e |
Date | 2 digits with leading space |
%m |
Month | 2 digits with leading space |
%y |
Year | 2 digits |
%H |
Hour | 2 digits with leading 0 |
%M |
Minutes | 2 digits with leading 0 |
%S |
Seconds | 2 digits with leading 0 |
Teletext character attributes (control codes) can also be used. They can be represented in three ways: 1) As they are with no translation, or 2) They have 0x80 added to translate them to characters with codes 128-159, or 3) they are replaced by escape (character 0x1b) then the character with 0x40 added. This is taken from MRG's .tti Output Line format.
fetcher
object
You can supply your own object which fetches pages to override the default fetcher. The interface it needs to implement is:
{
async function fetchPage(pageNumber)
// pageNumber is a 3 character string, values 100 to 8FF
}
fetchPage
returns a promise. The promise resolves to a page
object or null
if the page couldn't be fetched for any reason. The page
object is described below in the 'Default page data source' section.
service.showPage(pageNumber)
Shows the page with the number. The page number must be three characters, from 100 to 8FF.
If using the default fetcher, this will get the magazine JSON containing the page, and get the page data from that. The magazine filenames are derived from the page number, and named 1.json
to 8.json
. For example, page 100 will cause 1.json
to be fetched and page 100 used from that. The first non-null subpage in the page data is displayed.
Response is a promise. When resolved, the value is:
-
null
- if the page couldn't be retrieved for any reason. This could be because the page number is invalid, or the magazine JSON couldn't be retrieved, or the page isn't in the JSON, or the page has no subpages. -
meta
: object with the following properties:-
pageNumber
: string - the current page number (100 to 8FF) -
subPage
: number - the current subpage number -
numSubPages
: number - how many subpages in total for the page -
fastext
: object - if the current page has fastext (coloured) links, the object will have corresponding properties. The property names arered
,green
,yellow
,blue
andindex
. The values for each are the linked page numbers -
webUrl
: string - thewebUrl
property from the current page JSON if any. It's not part of teletext, but could be used to link to the original content source if there is one -
image
: string - theimage
property from the current page JSON if any. It's not part of teletext, but could be used for meta tags or semantic markup
-
service.nextSubPage()
Shows the next subpage for the current page. If there's only one subpage there's no change.
The response is the meta
object - see the showPage()
response.
service.previousSubPage()
Shows the previous subpage for the current page. If there's only one subpage there's no change.
The response is the meta
object - see the showPage()
response.
service.showLink(link)
Shows the page with the link on the current page. These are used for the colour buttons on a remote, or fastext buttons. The link is a string with the value red
, green
, yellow
, blue
or index
. (The teletext spec calls this editorial linking, using packet X/27).
Response is a promise, and is the same as for showPage()
.
If the page doesn't have the link, the promise resolves to null
.
service.teletextInstance property
Returns the teletext instance object (an instance of @techandsoftware/teletext
), so you can call methods on this directly, for example to change the font, draw pages or any other API calls you need direct access to. For the API, see https://www.npmjs.com/package/@techandsoftware/teletext
new TeletextServiceViewer(options)
Creates a teletext service viewer instance. options
is optional and has the following properties:
{
DOMSelector?: string,
defaultG0Charset?: string,
header?: string
frontPage?: string | null,
smoothMosaics?: boolean,
baseURL?: string,
fontList? : array
serviceName? : string
}
-
DOMSelector
is a selector string for the teletext screen container. Default is#teletextscreen
-
defaultG0Charset
sets the default G0 character set on the teletext instance. Defaults tog0_latin__english
. SeesetDefaultG0Charset()
in @techandsoftware/teletext for the available values -
header
is the header row to replace the default header row. See the "header
format" section above for the format -
frontPage
is the front page number, which will be shown automatically. Defaults to page 100. Pass in null to not show the front page automatically -
smoothMosaics
- if true will use a pixel-art scaling algorithm to generate smoother mosaic graphics when the service starts. Default is false -
baseURL
- URL to use as the base URL for getting JSON content. The default base URL is the current directory of the URL running the javascript code. The URL should include a trailing/
-
fontList
- an array of CSS font families. The teletext screen will used the first one automatically. The others are selected if 'f' is pressed. If not specified the default list is used - see FONTS inServiceView.js
in the repo -
serviceName
- the teletext service name, used in the page title and meta tags. Default is 'FAXFAX'
Default page data source
The default page fetcher looks for files named 1.json
to 8.json
in the same location as the HTML page running your app. Each file is a magazine, containing all the pages for that magazine. The magazine corresponds to the hundreds digit of the page number.
An example structure follows for magazine 1. This has two pages. Page 100 has three subpages, but the first one is empty. Page 101 has two subpages with fastext links.
The subpages either supply outputLines
or packed
for the teletext data, described below.
{
"pages": {
"100": {
"subpages": [
null,
{
"outputLines": "....",
"encoding": "g0_latin__english"
},
{
"outputLines": "....",
"encoding": "g0_latin__english"
}
]
},
"101": {
"subpages": [
{
"outputLines": "....",
"encoding": "g0_latin__english",
"fastext": {
"red": "199",
"green": "150",
"yellow": "800",
"blue": "200",
"index": "100"
}
},
{
"packed": "....",
"encoding": "g0_latin__english",
"fastext": {
"red": "199",
"green": "150",
"yellow": "800",
"blue": "200",
"index": "100"
}
}
]
}
}
}
pages
object
Top level and The outermost object has a pages
key.
-
pages
is required. Its value is an object, in which the keys are the page numbers and the values are apage
object
page
object
Has up to two keys:
-
webUrl
key is optional. A URL corresponding to the page. TheTeletextServiceViewer
uses this in the UI to link to -
subpages
key is required. Its value is an array of 1 or moresubpage
objects. Any of the subpages can benull
subpage
object
Has the following keys:
-
encoding
is the default G0 character set for the subpage. It's optional. If not present, the character set passed in bydefaultG0Charset
in thenew TeletextService()
call is used. -
fastext
is optional. If present it can contain keys forred
,green
,yellow
,blue
andindex
. The values are the page numbers to link to. The numbers are actually strings to allow for 100 to 8FF. -
outputLines
orpacked
strings are the teletext data. You can supply either of these. One is required.
outputLines
format
String. The contents is in the Output Line format used in MRG's .tti files. Each line has the format:
OL,rowNum,line\n
In this:
-
rowNum
is between 0 and 24 -
line
is the string to display. Attribute characters (character codes less than 0x20) are represented in three ways: 1) As they are with no translation, or 2) They have 0x80 added to translate them to characters with codes 128-159, or 3) they are replaced by escape (character 0x1b) then the character with 0x40 added.
packed
format
String. The contents is a base64-encoded string of 7-bit characters for the 25 rows x 40 characters concatenated together. The encoded string uses the character repertoire defined in the base64url encoding. This format is taken from the edit.tf editor URL hash fragment - see further details here: https://github.com/rawles/edit.tf
An example is in public/1.json
, or see the URL hash at https://edit.tf (the part after the colon)
Credits
- The tokens used by the header were taken from vbit2's header config - https://github.com/peterkvt80/vbit2/
- Some example headers can be seen in https://github.com/peterkvt80/vbit2/blob/master/example-vbit.conf
- The attribute character encoding used for the header and the page data is taken from the Output Line format in MRG's .tti file spec - https://zxnet.co.uk/teletext/documents/ttiformat.pdf
- The packed page data format is taken from the hash format used by Simon Rawles online teletext editor - https://github.com/rawles/edit.tf
- Engineering test page in the example 1.json from Ceefax via Teletext Archeologist - https://archive.teletextarchaeologist.org/