mirror of
https://github.com/milk-net/milk-net.github.io.git
synced 2025-04-18 17:23:40 -05:00
Compare commits
20 commits
76b98b3538
...
8bf93e2335
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8bf93e2335 | ||
![]() |
fe577420f1 | ||
![]() |
20438943f7 | ||
![]() |
7546557d69 | ||
![]() |
de99220386 | ||
![]() |
d312da0131 | ||
![]() |
a2118624ea | ||
![]() |
a6ab971546 | ||
![]() |
0ff46023cf | ||
![]() |
47d4ff39c4 | ||
![]() |
f606ab98c4 | ||
![]() |
b3860d6272 | ||
![]() |
ed3fd4346f | ||
![]() |
d629582772 | ||
![]() |
5a15dc7a3c | ||
![]() |
f16df3d649 | ||
![]() |
8a70da2462 | ||
![]() |
996a3a5181 | ||
![]() |
b16321b3f0 | ||
![]() |
be4fbebc4a |
15 changed files with 7430 additions and 1 deletions
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div class="center-text">
|
||||
<p class="center-text">Apps | More soon...</p>
|
||||
<a href="/browse">Proxy</a> • <a href="/chat">Chat (Matrix hosted by whatware.net)</a> • <a href="/irc">Chat (IRC hosted by telepath.im)</a> • <a href="/gamja">Gamja (Alt client for IRC)</a> • <a href="/apps/calculator">Calculator</a> • <a href="/library">Library (WIP)</a> • <a href="/webamp.txt">Music player (Webamp)</a>
|
||||
<a href="/browse">Proxy</a> • <a href="/chat">Chat (Matrix by whatware.net)</a> • <a href="/irc">Chat (IRC by telepath.im)</a> • <a href="/gamja">Gamja (Alt IRC)</a> • <a href="/calculator">Calculator</a> • <a href="/calculator/graph">Graphing calculator</a> • <a href="/library">Library (WIP)</a> • <a href="/webamp.txt">Music player (Webamp)</a> • <a href="/teletext">Teletext by zxnet.co.uk</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
|
|
184
calculator/graph/index.html
Normal file
184
calculator/graph/index.html
Normal file
|
@ -0,0 +1,184 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>MilkNet | Graphing Calculator</title>
|
||||
<link id="favicon" rel="icon" href="/assets/img/milk.png" type="image/x-icon">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjs/lib/browser/math.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input, button, select {
|
||||
font-size: 1rem;
|
||||
padding: 6px 10px;
|
||||
margin: 10px;
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
|
||||
input:focus, button:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: #0af;
|
||||
}
|
||||
|
||||
canvas {
|
||||
max-width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.color-inputs {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.range-controls {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Graphing Calculator</h1>
|
||||
<label for="functionInput">Enter functions (comma-separated, in terms of x):</label><br/>
|
||||
<input type="text" id="functionInput" value="sin(x), x^2" /><br/>
|
||||
<div class="color-inputs" id="colorInputs"></div>
|
||||
<div class="range-controls">
|
||||
<label>X Range: from <input type="number" id="xMin" value="-10" /> to <input type="number" id="xMax" value="10" /></label>
|
||||
<label> Step: <input type="number" id="xStep" value="0.1" step="0.1" /></label>
|
||||
</div>
|
||||
<button onclick="plotFunction()">Plot</button>
|
||||
<button onclick="downloadGraph()">Save Graph</button>
|
||||
<button onclick="resetZoom()">Reset Zoom</button>
|
||||
|
||||
<canvas id="graph" width="600" height="400"></canvas>
|
||||
|
||||
<script>
|
||||
const ctx = document.getElementById('graph').getContext('2d');
|
||||
let chart;
|
||||
|
||||
function generateColorPickers(functions) {
|
||||
const container = document.getElementById('colorInputs');
|
||||
functions.forEach((_, i) => {
|
||||
if (!document.getElementById(`color${i}`)) {
|
||||
const colorInput = document.createElement('input');
|
||||
colorInput.type = 'color';
|
||||
colorInput.value = getColorFromIndex(i);
|
||||
colorInput.id = `color${i}`;
|
||||
container.appendChild(colorInput);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getColorFromIndex(i) {
|
||||
const defaultColors = ['#ff6384', '#36a2eb', '#cc65fe', '#ffce56', '#00ff99', '#ff9933'];
|
||||
return defaultColors[i % defaultColors.length];
|
||||
}
|
||||
|
||||
function plotFunction() {
|
||||
const exprInput = document.getElementById('functionInput').value;
|
||||
const functions = exprInput.split(',').map(fn => fn.trim());
|
||||
const xMin = parseFloat(document.getElementById('xMin').value);
|
||||
const xMax = parseFloat(document.getElementById('xMax').value);
|
||||
const xStep = parseFloat(document.getElementById('xStep').value);
|
||||
|
||||
generateColorPickers(functions);
|
||||
|
||||
const xValues = [];
|
||||
for (let x = xMin; x <= xMax; x += xStep) {
|
||||
xValues.push(x.toFixed(2));
|
||||
}
|
||||
|
||||
const datasets = functions.map((expr, i) => {
|
||||
const yValues = [];
|
||||
for (let x = xMin; x <= xMax; x += xStep) {
|
||||
try {
|
||||
const scope = { x: x };
|
||||
const y = math.evaluate(expr, scope);
|
||||
yValues.push(y);
|
||||
} catch {
|
||||
yValues.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
const color = document.getElementById(`color${i}`).value;
|
||||
return {
|
||||
label: `y = ${expr}`,
|
||||
data: yValues,
|
||||
borderColor: color,
|
||||
backgroundColor: color,
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
pointRadius: 0
|
||||
};
|
||||
});
|
||||
|
||||
if (chart) chart.destroy();
|
||||
|
||||
chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: xValues,
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: { color: 'white' }
|
||||
},
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'xy',
|
||||
modifierKey: 'ctrl'
|
||||
},
|
||||
zoom: {
|
||||
wheel: { enabled: true },
|
||||
pinch: { enabled: true },
|
||||
mode: 'xy'
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: { display: true, text: 'x', color: 'white' },
|
||||
ticks: { color: 'white' },
|
||||
grid: { color: '#333' }
|
||||
},
|
||||
y: {
|
||||
title: { display: true, text: 'y', color: 'white' },
|
||||
ticks: { color: 'white' },
|
||||
grid: { color: '#333' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetZoom() {
|
||||
if (chart) chart.resetZoom();
|
||||
}
|
||||
|
||||
function downloadGraph() {
|
||||
const link = document.createElement('a');
|
||||
link.download = 'graph.png';
|
||||
link.href = document.getElementById('graph').toDataURL();
|
||||
link.click();
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
plotFunction(); // initial plot
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MilkNet | Calculator</title>
|
||||
<link rel="icon" type="image/png" href="/assets/img/milk.png"/>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
56
teletext/index.html
Normal file
56
teletext/index.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MilkNet | Teletext by zxnet.co.uk</title>
|
||||
<link id="favicon" rel="icon" href="https://forge.fsky.io/repo-avatars/c2504c43714bef6be3cc3500215091f832529292c1bc7338ad9d1b8262dbf84c" type="image/x-icon">
|
||||
<style type="text/css">
|
||||
body { background: #000; color:#000; font-family:sans-serif; margin:0px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
|
||||
#viewer { height:100vh; height:calc(var(--vh, 1vh)*100); width:100vw; display:flex; flex-direction: column; }
|
||||
H1 { margin: 0em; font-size:1.5em; }
|
||||
a, span { color:#000; text-decoration: none; }
|
||||
#header { margin-bottom:5px; color:#000; background:#000; padding:0.25em 0.5em; width:100%; display:flex; align-items: center; justify-content:space-between;}
|
||||
#bitmaps { display:none; }
|
||||
#screen { padding:5px; text-align:center; height:100%; flex:1}
|
||||
#container { flex:1; background:#000; display:inline-flex; width:100vw; height:100%; overflow:hidden }
|
||||
#right { display:none; max-width:30vw; max-height:100%; height:45em; width: 15em;}
|
||||
#below { display:none; height:calc(100vw*0.464);}
|
||||
#remote { padding:5px; height:100%; width:100%; display:none;}
|
||||
#remote2 { width:100vw; display:none; height:calc(100vw*0.464);}
|
||||
#teletextCanvas {position:relative; max-width:100%; max-height:100%; }
|
||||
*, *:before, *:after {-webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box;}
|
||||
</style>
|
||||
<meta property="og:image" content="preview.png"/>
|
||||
<meta name="description" content="Web based teletext emulator with different services on each channel. Includes Teefax, Ceefax, and Chunkytext."/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<script type="text/javascript" src="viewer.js"></script>
|
||||
<script type="text/javascript" src="settings.js"></script>
|
||||
<script type="text/javascript" src="tv.js"></script>
|
||||
<script type="text/javascript" src="remote.js"></script>
|
||||
<script type="text/javascript" src="teletext-resources/renderer.js"></script>
|
||||
<script type="text/javascript" src="teletext-resources/teletext.js"></script>
|
||||
<script type="text/javascript" src="/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="viewer">
|
||||
<div id="header">
|
||||
<H1>Teletext viewer</H1>
|
||||
<span style="color:#fff; padding:0em 1em">Use remote to select channel and press <span style="color:white;">(≡)</span> for teletext.</span>
|
||||
</div>
|
||||
<div id="container">
|
||||
<div id="screen">
|
||||
<canvas id="teletextCanvas" width="1024" height="576"></canvas>
|
||||
</div>
|
||||
<div id="right">
|
||||
<object id="remote" data="remote.svg" type="image/svg+xml"></object>
|
||||
</div>
|
||||
</div>
|
||||
<div id="below">
|
||||
<object id="remote2" data="remote2.svg" type="image/svg+xml"></object>
|
||||
</div>
|
||||
<div id="bitmaps">
|
||||
<img src="teletext-resources/static.png" id="static">
|
||||
<img src="start.png" id="start">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
teletext/preview.png
Normal file
BIN
teletext/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 KiB |
231
teletext/remote.js
Normal file
231
teletext/remote.js
Normal file
|
@ -0,0 +1,231 @@
|
|||
"use strict";
|
||||
|
||||
function initRemote(element){
|
||||
/* set up event listener for remote control buttons */
|
||||
let svgDoc = element.contentDocument;
|
||||
|
||||
if (svgDoc)
|
||||
svgLoaded(svgDoc)
|
||||
else
|
||||
element.addEventListener("load", function(){svgLoaded(this.contentDocument);});
|
||||
|
||||
element.style.display = "block";
|
||||
}
|
||||
|
||||
function svgLoaded(svgDoc){
|
||||
let buttonArray = ["powerbutton", "1button", "2button", "3button", "4button", "5button", "6button", "7button", "8button", "9button", "0button", "redbutton", "greenbutton", "yellowbutton", "bluebutton", "textbutton", "cancelbutton", "stretchbutton", "indexbutton", "stopbutton", "revealbutton", "mixbutton", "timetextbutton", "modebutton", "blank1", "pupbutton", "pdownbutton", "mutebutton"];
|
||||
|
||||
/* create listeners for each element in buttonarray */
|
||||
for (let index = 0, len = buttonArray.length; index < len; ++index) {
|
||||
addListenerForSVGButton(svgDoc.getElementById(buttonArray[index]), false);
|
||||
}
|
||||
|
||||
/* create listeners with key repeat */
|
||||
addListenerForSVGButton(svgDoc.getElementById("vupbutton"), true);
|
||||
addListenerForSVGButton(svgDoc.getElementById("vdownbutton"), true);
|
||||
|
||||
svgDoc.getElementById("body").addEventListener("touchend",function(e){e.preventDefault();},false); // ignore touching remote
|
||||
}
|
||||
|
||||
function buttonPush(event, buttonID, repeat){
|
||||
if (event)
|
||||
event.preventDefault();
|
||||
let bbox = buttonID.getBBox();
|
||||
let cx = bbox.x + bbox.width/2;
|
||||
let cy = bbox.y + bbox.height/2;
|
||||
buttonID.setAttribute("transform","translate(" + cx*0.1 + " " + cy*0.1 + ") scale(0.9)");
|
||||
if (repeat)
|
||||
buttonID.repeatInterval = setInterval(function(){buttonPush(false,buttonID)}, 200);
|
||||
if (!(buttonID.debounce))
|
||||
buttonPressHandler(buttonID.id);
|
||||
}
|
||||
|
||||
function buttonUnpush(buttonID){
|
||||
if (!(buttonID.debounce)){
|
||||
clearInterval(buttonID.debounceInterval);
|
||||
buttonID.debounceInterval = setInterval(function(){buttonID.debounce = false;}, 20);
|
||||
buttonID.debounce = true;
|
||||
}
|
||||
}
|
||||
|
||||
function addListenerForSVGButton(buttonID, repeat){
|
||||
buttonID.addEventListener("touchstart",function(e){
|
||||
if (navigator.vibrate)
|
||||
window.navigator.vibrate(10);
|
||||
e.preventDefault();
|
||||
buttonPush(e, buttonID, repeat);
|
||||
},false);
|
||||
buttonID.addEventListener("touchend",function(){
|
||||
buttonID.removeAttribute("transform");
|
||||
clearInterval(buttonID.repeatInterval);
|
||||
buttonUnpush(buttonID);
|
||||
},false);
|
||||
/* on mousedown scale button to 90% to indicate button press */
|
||||
buttonID.addEventListener("mousedown",function(e){buttonPush(e, buttonID, repeat)}, false);
|
||||
/* on mouseup or mouseleave remove transformations (return button to original size) */
|
||||
buttonID.addEventListener("mouseup",function(){buttonID.removeAttribute("transform"); clearInterval(buttonID.repeatInterval);},false);
|
||||
buttonID.addEventListener("mouseleave",function(){buttonID.removeAttribute("transform"); clearInterval(buttonID.repeatInterval);},false);
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", handleKeydown);
|
||||
|
||||
function handleKeydown(event){
|
||||
if (event.repeat)
|
||||
return;
|
||||
|
||||
switch (event.key){
|
||||
case "0":
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
case "8":
|
||||
case "9":
|
||||
buttonPressHandler(event.key + "button");
|
||||
break;
|
||||
case "p":
|
||||
case "P":
|
||||
buttonPressHandler("powerbutton");
|
||||
break;
|
||||
case "t":
|
||||
case "T":
|
||||
buttonPressHandler("textbutton");
|
||||
break;
|
||||
case "c":
|
||||
case "C":
|
||||
buttonPressHandler("cancelbutton");
|
||||
break;
|
||||
case "s":
|
||||
case "S":
|
||||
buttonPressHandler("stretchbutton");
|
||||
break;
|
||||
case "m":
|
||||
buttonPressHandler("mixbutton");
|
||||
break;
|
||||
case "?":
|
||||
buttonPressHandler("revealbutton");
|
||||
break;
|
||||
case "h":
|
||||
case "H":
|
||||
buttonPressHandler("stopbutton");
|
||||
break;
|
||||
case "i":
|
||||
case "I":
|
||||
buttonPressHandler("indexbutton");
|
||||
break;
|
||||
case "r":
|
||||
case "R":
|
||||
buttonPressHandler("redbutton");
|
||||
break;
|
||||
case "g":
|
||||
case "G":
|
||||
buttonPressHandler("greenbutton");
|
||||
break;
|
||||
case "y":
|
||||
case "Y":
|
||||
buttonPressHandler("yellowbutton");
|
||||
break;
|
||||
case "b":
|
||||
case "B":
|
||||
buttonPressHandler("bluebutton");
|
||||
break;
|
||||
case "M":
|
||||
buttonPressHandler("mutebutton");
|
||||
break;
|
||||
case "+":
|
||||
buttonPressHandler("vupbutton");
|
||||
break;
|
||||
case "-":
|
||||
buttonPressHandler("vdownbutton");
|
||||
break;
|
||||
case "ArrowUp":
|
||||
event.preventDefault();
|
||||
buttonPressHandler("pupbutton");
|
||||
break;
|
||||
case "ArrowDown":
|
||||
event.preventDefault();
|
||||
buttonPressHandler("pdownbutton");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* determine which button was pressed from the id passed by the listener and call appropriate handler function */
|
||||
function buttonPressHandler(buttonID){
|
||||
switch(buttonID) {
|
||||
case "powerbutton":
|
||||
powerbuttonhandler();
|
||||
break;
|
||||
case "textbutton":
|
||||
textbuttonhandler();
|
||||
break;
|
||||
case "cancelbutton":
|
||||
cancelbuttonhandler();
|
||||
break;
|
||||
case "stretchbutton":
|
||||
sizebuttonhandler();
|
||||
break;
|
||||
case "mixbutton":
|
||||
mixbuttonhandler();
|
||||
break;
|
||||
case "timetextbutton":
|
||||
timetextbuttonhandler();
|
||||
break;
|
||||
case "revealbutton":
|
||||
revealbuttonhandler();
|
||||
break;
|
||||
case "modebutton":
|
||||
break;
|
||||
/* match any of the number buttons */
|
||||
case "1button":
|
||||
case "2button":
|
||||
case "3button":
|
||||
case "4button":
|
||||
case "5button":
|
||||
case "6button":
|
||||
case "7button":
|
||||
case "8button":
|
||||
case "9button":
|
||||
case "0button":
|
||||
numberButtonHandler(parseInt(buttonID.substring(0,1)));
|
||||
break;
|
||||
case "blank1":
|
||||
blank1handler();
|
||||
break;
|
||||
case "vupbutton":
|
||||
volumebuttonhandler("up");
|
||||
break;
|
||||
case "vdownbutton":
|
||||
volumebuttonhandler("down");
|
||||
break;
|
||||
case "mutebutton":
|
||||
volumebuttonhandler("mute");
|
||||
break;
|
||||
case "pupbutton":
|
||||
channelbuttonhandler("up");
|
||||
break;
|
||||
case "pdownbutton":
|
||||
channelbuttonhandler("down");
|
||||
break;
|
||||
case "redbutton":
|
||||
linkbuttonhandler(0);
|
||||
break;
|
||||
case "greenbutton":
|
||||
linkbuttonhandler(1);
|
||||
break;
|
||||
case "yellowbutton":
|
||||
linkbuttonhandler(2);
|
||||
break;
|
||||
case "bluebutton":
|
||||
linkbuttonhandler(3);
|
||||
break;
|
||||
case "indexbutton":
|
||||
linkbuttonhandler(5);
|
||||
break;
|
||||
case "stopbutton":
|
||||
stopbuttonhandler();
|
||||
break;
|
||||
}
|
||||
}
|
1130
teletext/remote.svg
Normal file
1130
teletext/remote.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 63 KiB |
1110
teletext/remote2.svg
Normal file
1110
teletext/remote2.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 64 KiB |
14
teletext/settings.js
Normal file
14
teletext/settings.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* viewer settings */
|
||||
|
||||
widescreen = false; /* emulate a 16:9 tv */
|
||||
prompt = true; /* user is prompted to click if autoOn or a channel parameter passed (required for media to play) */
|
||||
unevenFlashRatio = false; /* use a 2:1 ON/OFF ratio instead of 50% duty cycle in 1Hz flash */
|
||||
autoPowerOffTime = 15; /* minutes before powering off TV if no interaction, or zero to disable */
|
||||
|
||||
/* set "tuning" for these channels when the page is loaded */
|
||||
localStorage.setItem("teletextViewer:channelID1","WyJ0ZWVmYXgiLCJ6eG5ldC5jby51ayIsIi93cy90ZWVmYXgiXQ==");
|
||||
localStorage.setItem("teletextViewer:channelID2","WyJubXMtY2VlZmF4IiwiaW50ZXJuYWwubmF0aGFubWVkaWFzZXJ2aWNlcy5jby51ayIsIi93ZWJzb2NrZXRzL2NlZWZheCJd");
|
||||
localStorage.setItem("teletextViewer:channelID3","WyJjaHVua3l0ZXh0IiwienhuZXQuY28udWsiLCIvd3MvY2h1bmt5dGV4dCJd");
|
||||
localStorage.setItem("teletextViewer:channelID4","WyJjaGFubmVsNC1jYXB0dXJlIiwienhuZXQuY28udWsiLCIvd3MvY2g0Il0=");
|
||||
localStorage.setItem("teletextViewer:channelID5","WyJ0ZXN0IiwienhuZXQuY28udWsiLCIvd3MvdGVzdCJd");
|
||||
localStorage.setItem("teletextViewer:channelID6","WyJzcGFyayIsInp4bmV0LmNvLnVrIiwiL3dzL3NwYXJrIl0=");
|
BIN
teletext/start.png
Normal file
BIN
teletext/start.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
2241
teletext/teletext-resources/renderer.js
Normal file
2241
teletext/teletext-resources/renderer.js
Normal file
File diff suppressed because one or more lines are too long
BIN
teletext/teletext-resources/static.png
Normal file
BIN
teletext/teletext-resources/static.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 597 KiB |
338
teletext/teletext-resources/teletext.js
Normal file
338
teletext/teletext-resources/teletext.js
Normal file
|
@ -0,0 +1,338 @@
|
|||
/* teletext encoding/decoding and definitions */
|
||||
|
||||
function setParity(data){
|
||||
var p = 1^(data&1)^((data>>1)&1)^((data>>2)&1)^((data>>3)&1)^((data>>4)&1)^((data>>5)&1)^((data>>6)&1)
|
||||
return (data&0x7F)|(p<<7);
|
||||
}
|
||||
|
||||
function hamming_8_4_decode(databyte){
|
||||
var decoded = hamming_8_4_inverse[databyte];
|
||||
if (decoded == 0xFF)
|
||||
console.log("hamming_8_4_decode error");
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function hamming_24_18_decode(triplet){
|
||||
var D1_D4 = hamming_24_18_inverse_D1_D4[triplet[0] >> 2];
|
||||
var D5_D11 = triplet[1] & 0x7F;
|
||||
var D12_D18 = triplet[2] & 0x7F;
|
||||
|
||||
var d = D1_D4 | (D5_D11 << 4) | (D12_D18 << 11);
|
||||
|
||||
var ABCDEF = (hamming_24_18_inverse_parity_0[triplet[0]] ^ hamming_24_18_inverse_parity_1[triplet[1]] ^ hamming_24_18_inverse_parity_2[triplet[2]]);
|
||||
|
||||
var decoded = d ^ hamming_24_18_inverse_error[ABCDEF];
|
||||
if (decoded & 0x80000000)
|
||||
console.log(triplet, "hamming_24_18_decode error")
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function hamming_24_18_encode(triplet){
|
||||
var Byte0 = (hamming_24_18_forwards_0[triplet & 0xFF] ^ hamming_24_18_forwards_1[(triplet >> 8) & 0xFF] ^ hamming_24_18_forwards_2[(triplet >> 16) & 0xFF]);
|
||||
|
||||
var D5_D11 = (triplet >> 4) & 0x7F;
|
||||
var D12_D18 = (triplet >> 11) & 0x7F;
|
||||
|
||||
var P5 = 0x80 & ~(hamming_24_18_inverse_parity_0[D12_D18] << 2);
|
||||
var P6 = 0x80 & ((hamming_24_18_inverse_parity_0[Byte0] ^ hamming_24_18_inverse_parity_0[D5_D11]) << 2);
|
||||
|
||||
return Byte0 | ((D5_D11 | P5) << 8) | ((D12_D18 | P6) << 16);
|
||||
}
|
||||
|
||||
function decodeEnhancementPacket(packet){
|
||||
var decoded = [];
|
||||
decoded.push(hamming_8_4_decode(packet[0])); // designation code
|
||||
for (var i=0;i<13;i++){
|
||||
var triplet = [];
|
||||
triplet.push(packet[i*3+1]);
|
||||
triplet.push(packet[i*3+2]);
|
||||
triplet.push(packet[i*3+3]);
|
||||
decoded.push(hamming_24_18_decode(triplet));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function calculatePageCRC(level1PageArray){
|
||||
/* calculate the CRC checksum of a page per ETS 300 706 9.6.1 */
|
||||
|
||||
var checksum = 0;
|
||||
|
||||
for (var c=8; c<32; c++){
|
||||
checksum = calculateCRCByte(setParity(level1PageArray[0][c]), checksum);
|
||||
}
|
||||
|
||||
for (var r=1; r<26; r++){
|
||||
for (var c=0; c<40; c++){
|
||||
checksum = calculateCRCByte(setParity(level1PageArray[r][c]), checksum);
|
||||
}
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
function calculateCRCByte(pageByte, checksum){
|
||||
for (var b=7; b>-1; b--){
|
||||
var outBit = ((pageByte>>b)&1) ^ ((checksum>>6)&1) ^ ((checksum>>8)&1) ^ ((checksum>>11)&1) ^ ((checksum>>15)&1);
|
||||
checksum = outBit | ((checksum&0x7FFF)<<1);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/* teletext control bit masks */
|
||||
ERASE = 0x0010;
|
||||
NEWSFLASH = 0x0020;
|
||||
SUBTITLE = 0x0040;
|
||||
SUPPRESS_HEADER = 0x0080;
|
||||
UPDATE = 0x0100;
|
||||
INTERRUPTED_SEQUENCE = 0x0200;
|
||||
INHIBIT_DISPLAY = 0x0400;
|
||||
MAGAZINE_SERIAL = 0x0800;
|
||||
NATIONAL_OPTION = 0x7000;
|
||||
|
||||
/* hamming code lookup tables */
|
||||
hamming_8_4_forwards = [0x15,0x02,0x49,0x5E,0x64,0x73,0x38,0x2F,0xD0,0xC7,0x8C,0x9B,0xA1,0xB6,0xFD,0xEA];
|
||||
|
||||
hamming_24_18_forwards_0 = [
|
||||
0x8b, 0x8c, 0x92, 0x95, 0xa1, 0xa6, 0xb8, 0xbf,
|
||||
0xc0, 0xc7, 0xd9, 0xde, 0xea, 0xed, 0xf3, 0xf4,
|
||||
0x0a, 0x0d, 0x13, 0x14, 0x20, 0x27, 0x39, 0x3e,
|
||||
0x41, 0x46, 0x58, 0x5f, 0x6b, 0x6c, 0x72, 0x75,
|
||||
0x09, 0x0e, 0x10, 0x17, 0x23, 0x24, 0x3a, 0x3d,
|
||||
0x42, 0x45, 0x5b, 0x5c, 0x68, 0x6f, 0x71, 0x76,
|
||||
0x88, 0x8f, 0x91, 0x96, 0xa2, 0xa5, 0xbb, 0xbc,
|
||||
0xc3, 0xc4, 0xda, 0xdd, 0xe9, 0xee, 0xf0, 0xf7,
|
||||
0x08, 0x0f, 0x11, 0x16, 0x22, 0x25, 0x3b, 0x3c,
|
||||
0x43, 0x44, 0x5a, 0x5d, 0x69, 0x6e, 0x70, 0x77,
|
||||
0x89, 0x8e, 0x90, 0x97, 0xa3, 0xa4, 0xba, 0xbd,
|
||||
0xc2, 0xc5, 0xdb, 0xdc, 0xe8, 0xef, 0xf1, 0xf6,
|
||||
0x8a, 0x8d, 0x93, 0x94, 0xa0, 0xa7, 0xb9, 0xbe,
|
||||
0xc1, 0xc6, 0xd8, 0xdf, 0xeb, 0xec, 0xf2, 0xf5,
|
||||
0x0b, 0x0c, 0x12, 0x15, 0x21, 0x26, 0x38, 0x3f,
|
||||
0x40, 0x47, 0x59, 0x5e, 0x6a, 0x6d, 0x73, 0x74,
|
||||
0x03, 0x04, 0x1a, 0x1d, 0x29, 0x2e, 0x30, 0x37,
|
||||
0x48, 0x4f, 0x51, 0x56, 0x62, 0x65, 0x7b, 0x7c,
|
||||
0x82, 0x85, 0x9b, 0x9c, 0xa8, 0xaf, 0xb1, 0xb6,
|
||||
0xc9, 0xce, 0xd0, 0xd7, 0xe3, 0xe4, 0xfa, 0xfd,
|
||||
0x81, 0x86, 0x98, 0x9f, 0xab, 0xac, 0xb2, 0xb5,
|
||||
0xca, 0xcd, 0xd3, 0xd4, 0xe0, 0xe7, 0xf9, 0xfe,
|
||||
0x00, 0x07, 0x19, 0x1e, 0x2a, 0x2d, 0x33, 0x34,
|
||||
0x4b, 0x4c, 0x52, 0x55, 0x61, 0x66, 0x78, 0x7f,
|
||||
0x80, 0x87, 0x99, 0x9e, 0xaa, 0xad, 0xb3, 0xb4,
|
||||
0xcb, 0xcc, 0xd2, 0xd5, 0xe1, 0xe6, 0xf8, 0xff,
|
||||
0x01, 0x06, 0x18, 0x1f, 0x2b, 0x2c, 0x32, 0x35,
|
||||
0x4a, 0x4d, 0x53, 0x54, 0x60, 0x67, 0x79, 0x7e,
|
||||
0x02, 0x05, 0x1b, 0x1c, 0x28, 0x2f, 0x31, 0x36,
|
||||
0x49, 0x4e, 0x50, 0x57, 0x63, 0x64, 0x7a, 0x7d,
|
||||
0x83, 0x84, 0x9a, 0x9d, 0xa9, 0xae, 0xb0, 0xb7,
|
||||
0xc8, 0xcf, 0xd1, 0xd6, 0xe2, 0xe5, 0xfb, 0xfc
|
||||
];
|
||||
|
||||
hamming_24_18_forwards_1 = [
|
||||
0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
|
||||
0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
|
||||
0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
|
||||
0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
|
||||
0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
|
||||
0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
|
||||
0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
|
||||
0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
|
||||
0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
|
||||
0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
|
||||
0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
|
||||
0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
|
||||
0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
|
||||
0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
|
||||
0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
|
||||
0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
|
||||
0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
|
||||
0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
|
||||
0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
|
||||
0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
|
||||
0x0a, 0x83, 0x80, 0x09, 0x81, 0x08, 0x0b, 0x82,
|
||||
0x0b, 0x82, 0x81, 0x08, 0x80, 0x09, 0x0a, 0x83,
|
||||
0x08, 0x81, 0x82, 0x0b, 0x83, 0x0a, 0x09, 0x80,
|
||||
0x09, 0x80, 0x83, 0x0a, 0x82, 0x0b, 0x08, 0x81,
|
||||
0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89,
|
||||
0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
|
||||
0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
|
||||
0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
|
||||
0x02, 0x8b, 0x88, 0x01, 0x89, 0x00, 0x03, 0x8a,
|
||||
0x03, 0x8a, 0x89, 0x00, 0x88, 0x01, 0x02, 0x8b,
|
||||
0x00, 0x89, 0x8a, 0x03, 0x8b, 0x02, 0x01, 0x88,
|
||||
0x01, 0x88, 0x8b, 0x02, 0x8a, 0x03, 0x00, 0x89
|
||||
];
|
||||
|
||||
hamming_24_18_forwards_2 = [
|
||||
0x00, 0x0a, 0x0b, 0x01
|
||||
];
|
||||
|
||||
hamming_8_4_inverse = [
|
||||
0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0x01, 0xff,
|
||||
0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07,
|
||||
0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0x00,
|
||||
0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff,
|
||||
0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07,
|
||||
0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x07,
|
||||
0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff,
|
||||
0x06, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07,
|
||||
0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09,
|
||||
0x02, 0x02, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff,
|
||||
0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff,
|
||||
0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03,
|
||||
0x04, 0xff, 0xff, 0x05, 0x04, 0x04, 0x04, 0xff,
|
||||
0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07,
|
||||
0xff, 0x05, 0x05, 0x05, 0x04, 0xff, 0xff, 0x05,
|
||||
0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff,
|
||||
0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09,
|
||||
0x0a, 0xff, 0xff, 0x0b, 0x0a, 0x0a, 0x0a, 0xff,
|
||||
0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff,
|
||||
0xff, 0x0b, 0x0b, 0x0b, 0x0a, 0xff, 0xff, 0x0b,
|
||||
0x0c, 0x0c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff,
|
||||
0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07,
|
||||
0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x0d, 0x0d,
|
||||
0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff,
|
||||
0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x09,
|
||||
0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09,
|
||||
0x08, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09,
|
||||
0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff,
|
||||
0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09,
|
||||
0x0f, 0xff, 0x0f, 0x0f, 0xff, 0x0e, 0x0f, 0xff,
|
||||
0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff,
|
||||
0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x0e, 0xff, 0x0e
|
||||
];
|
||||
|
||||
hamming_24_18_inverse_D1_D4 = [
|
||||
0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
|
||||
0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
|
||||
0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
|
||||
0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
|
||||
0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
|
||||
0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
|
||||
0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f
|
||||
];
|
||||
|
||||
hamming_24_18_inverse_parity_0 = [
|
||||
0x00, 0x21, 0x22, 0x03, 0x23, 0x02, 0x01, 0x20,
|
||||
0x24, 0x05, 0x06, 0x27, 0x07, 0x26, 0x25, 0x04,
|
||||
0x25, 0x04, 0x07, 0x26, 0x06, 0x27, 0x24, 0x05,
|
||||
0x01, 0x20, 0x23, 0x02, 0x22, 0x03, 0x00, 0x21,
|
||||
0x26, 0x07, 0x04, 0x25, 0x05, 0x24, 0x27, 0x06,
|
||||
0x02, 0x23, 0x20, 0x01, 0x21, 0x00, 0x03, 0x22,
|
||||
0x03, 0x22, 0x21, 0x00, 0x20, 0x01, 0x02, 0x23,
|
||||
0x27, 0x06, 0x05, 0x24, 0x04, 0x25, 0x26, 0x07,
|
||||
0x27, 0x06, 0x05, 0x24, 0x04, 0x25, 0x26, 0x07,
|
||||
0x03, 0x22, 0x21, 0x00, 0x20, 0x01, 0x02, 0x23,
|
||||
0x02, 0x23, 0x20, 0x01, 0x21, 0x00, 0x03, 0x22,
|
||||
0x26, 0x07, 0x04, 0x25, 0x05, 0x24, 0x27, 0x06,
|
||||
0x01, 0x20, 0x23, 0x02, 0x22, 0x03, 0x00, 0x21,
|
||||
0x25, 0x04, 0x07, 0x26, 0x06, 0x27, 0x24, 0x05,
|
||||
0x24, 0x05, 0x06, 0x27, 0x07, 0x26, 0x25, 0x04,
|
||||
0x00, 0x21, 0x22, 0x03, 0x23, 0x02, 0x01, 0x20,
|
||||
0x28, 0x09, 0x0a, 0x2b, 0x0b, 0x2a, 0x29, 0x08,
|
||||
0x0c, 0x2d, 0x2e, 0x0f, 0x2f, 0x0e, 0x0d, 0x2c,
|
||||
0x0d, 0x2c, 0x2f, 0x0e, 0x2e, 0x0f, 0x0c, 0x2d,
|
||||
0x29, 0x08, 0x0b, 0x2a, 0x0a, 0x2b, 0x28, 0x09,
|
||||
0x0e, 0x2f, 0x2c, 0x0d, 0x2d, 0x0c, 0x0f, 0x2e,
|
||||
0x2a, 0x0b, 0x08, 0x29, 0x09, 0x28, 0x2b, 0x0a,
|
||||
0x2b, 0x0a, 0x09, 0x28, 0x08, 0x29, 0x2a, 0x0b,
|
||||
0x0f, 0x2e, 0x2d, 0x0c, 0x2c, 0x0d, 0x0e, 0x2f,
|
||||
0x0f, 0x2e, 0x2d, 0x0c, 0x2c, 0x0d, 0x0e, 0x2f,
|
||||
0x2b, 0x0a, 0x09, 0x28, 0x08, 0x29, 0x2a, 0x0b,
|
||||
0x2a, 0x0b, 0x08, 0x29, 0x09, 0x28, 0x2b, 0x0a,
|
||||
0x0e, 0x2f, 0x2c, 0x0d, 0x2d, 0x0c, 0x0f, 0x2e,
|
||||
0x29, 0x08, 0x0b, 0x2a, 0x0a, 0x2b, 0x28, 0x09,
|
||||
0x0d, 0x2c, 0x2f, 0x0e, 0x2e, 0x0f, 0x0c, 0x2d,
|
||||
0x0c, 0x2d, 0x2e, 0x0f, 0x2f, 0x0e, 0x0d, 0x2c,
|
||||
0x28, 0x09, 0x0a, 0x2b, 0x0b, 0x2a, 0x29, 0x08
|
||||
];
|
||||
|
||||
hamming_24_18_inverse_parity_1 = [
|
||||
0x00, 0x29, 0x2a, 0x03, 0x2b, 0x02, 0x01, 0x28,
|
||||
0x2c, 0x05, 0x06, 0x2f, 0x07, 0x2e, 0x2d, 0x04,
|
||||
0x2d, 0x04, 0x07, 0x2e, 0x06, 0x2f, 0x2c, 0x05,
|
||||
0x01, 0x28, 0x2b, 0x02, 0x2a, 0x03, 0x00, 0x29,
|
||||
0x2e, 0x07, 0x04, 0x2d, 0x05, 0x2c, 0x2f, 0x06,
|
||||
0x02, 0x2b, 0x28, 0x01, 0x29, 0x00, 0x03, 0x2a,
|
||||
0x03, 0x2a, 0x29, 0x00, 0x28, 0x01, 0x02, 0x2b,
|
||||
0x2f, 0x06, 0x05, 0x2c, 0x04, 0x2d, 0x2e, 0x07,
|
||||
0x2f, 0x06, 0x05, 0x2c, 0x04, 0x2d, 0x2e, 0x07,
|
||||
0x03, 0x2a, 0x29, 0x00, 0x28, 0x01, 0x02, 0x2b,
|
||||
0x02, 0x2b, 0x28, 0x01, 0x29, 0x00, 0x03, 0x2a,
|
||||
0x2e, 0x07, 0x04, 0x2d, 0x05, 0x2c, 0x2f, 0x06,
|
||||
0x01, 0x28, 0x2b, 0x02, 0x2a, 0x03, 0x00, 0x29,
|
||||
0x2d, 0x04, 0x07, 0x2e, 0x06, 0x2f, 0x2c, 0x05,
|
||||
0x2c, 0x05, 0x06, 0x2f, 0x07, 0x2e, 0x2d, 0x04,
|
||||
0x00, 0x29, 0x2a, 0x03, 0x2b, 0x02, 0x01, 0x28,
|
||||
0x30, 0x19, 0x1a, 0x33, 0x1b, 0x32, 0x31, 0x18,
|
||||
0x1c, 0x35, 0x36, 0x1f, 0x37, 0x1e, 0x1d, 0x34,
|
||||
0x1d, 0x34, 0x37, 0x1e, 0x36, 0x1f, 0x1c, 0x35,
|
||||
0x31, 0x18, 0x1b, 0x32, 0x1a, 0x33, 0x30, 0x19,
|
||||
0x1e, 0x37, 0x34, 0x1d, 0x35, 0x1c, 0x1f, 0x36,
|
||||
0x32, 0x1b, 0x18, 0x31, 0x19, 0x30, 0x33, 0x1a,
|
||||
0x33, 0x1a, 0x19, 0x30, 0x18, 0x31, 0x32, 0x1b,
|
||||
0x1f, 0x36, 0x35, 0x1c, 0x34, 0x1d, 0x1e, 0x37,
|
||||
0x1f, 0x36, 0x35, 0x1c, 0x34, 0x1d, 0x1e, 0x37,
|
||||
0x33, 0x1a, 0x19, 0x30, 0x18, 0x31, 0x32, 0x1b,
|
||||
0x32, 0x1b, 0x18, 0x31, 0x19, 0x30, 0x33, 0x1a,
|
||||
0x1e, 0x37, 0x34, 0x1d, 0x35, 0x1c, 0x1f, 0x36,
|
||||
0x31, 0x18, 0x1b, 0x32, 0x1a, 0x33, 0x30, 0x19,
|
||||
0x1d, 0x34, 0x37, 0x1e, 0x36, 0x1f, 0x1c, 0x35,
|
||||
0x1c, 0x35, 0x36, 0x1f, 0x37, 0x1e, 0x1d, 0x34,
|
||||
0x30, 0x19, 0x1a, 0x33, 0x1b, 0x32, 0x31, 0x18
|
||||
];
|
||||
|
||||
hamming_24_18_inverse_parity_2 = [
|
||||
0x3f, 0x0e, 0x0d, 0x3c, 0x0c, 0x3d, 0x3e, 0x0f,
|
||||
0x0b, 0x3a, 0x39, 0x08, 0x38, 0x09, 0x0a, 0x3b,
|
||||
0x0a, 0x3b, 0x38, 0x09, 0x39, 0x08, 0x0b, 0x3a,
|
||||
0x3e, 0x0f, 0x0c, 0x3d, 0x0d, 0x3c, 0x3f, 0x0e,
|
||||
0x09, 0x38, 0x3b, 0x0a, 0x3a, 0x0b, 0x08, 0x39,
|
||||
0x3d, 0x0c, 0x0f, 0x3e, 0x0e, 0x3f, 0x3c, 0x0d,
|
||||
0x3c, 0x0d, 0x0e, 0x3f, 0x0f, 0x3e, 0x3d, 0x0c,
|
||||
0x08, 0x39, 0x3a, 0x0b, 0x3b, 0x0a, 0x09, 0x38,
|
||||
0x08, 0x39, 0x3a, 0x0b, 0x3b, 0x0a, 0x09, 0x38,
|
||||
0x3c, 0x0d, 0x0e, 0x3f, 0x0f, 0x3e, 0x3d, 0x0c,
|
||||
0x3d, 0x0c, 0x0f, 0x3e, 0x0e, 0x3f, 0x3c, 0x0d,
|
||||
0x09, 0x38, 0x3b, 0x0a, 0x3a, 0x0b, 0x08, 0x39,
|
||||
0x3e, 0x0f, 0x0c, 0x3d, 0x0d, 0x3c, 0x3f, 0x0e,
|
||||
0x0a, 0x3b, 0x38, 0x09, 0x39, 0x08, 0x0b, 0x3a,
|
||||
0x0b, 0x3a, 0x39, 0x08, 0x38, 0x09, 0x0a, 0x3b,
|
||||
0x3f, 0x0e, 0x0d, 0x3c, 0x0c, 0x3d, 0x3e, 0x0f,
|
||||
0x1f, 0x2e, 0x2d, 0x1c, 0x2c, 0x1d, 0x1e, 0x2f,
|
||||
0x2b, 0x1a, 0x19, 0x28, 0x18, 0x29, 0x2a, 0x1b,
|
||||
0x2a, 0x1b, 0x18, 0x29, 0x19, 0x28, 0x2b, 0x1a,
|
||||
0x1e, 0x2f, 0x2c, 0x1d, 0x2d, 0x1c, 0x1f, 0x2e,
|
||||
0x29, 0x18, 0x1b, 0x2a, 0x1a, 0x2b, 0x28, 0x19,
|
||||
0x1d, 0x2c, 0x2f, 0x1e, 0x2e, 0x1f, 0x1c, 0x2d,
|
||||
0x1c, 0x2d, 0x2e, 0x1f, 0x2f, 0x1e, 0x1d, 0x2c,
|
||||
0x28, 0x19, 0x1a, 0x2b, 0x1b, 0x2a, 0x29, 0x18,
|
||||
0x28, 0x19, 0x1a, 0x2b, 0x1b, 0x2a, 0x29, 0x18,
|
||||
0x1c, 0x2d, 0x2e, 0x1f, 0x2f, 0x1e, 0x1d, 0x2c,
|
||||
0x1d, 0x2c, 0x2f, 0x1e, 0x2e, 0x1f, 0x1c, 0x2d,
|
||||
0x29, 0x18, 0x1b, 0x2a, 0x1a, 0x2b, 0x28, 0x19,
|
||||
0x1e, 0x2f, 0x2c, 0x1d, 0x2d, 0x1c, 0x1f, 0x2e,
|
||||
0x2a, 0x1b, 0x18, 0x29, 0x19, 0x28, 0x2b, 0x1a,
|
||||
0x2b, 0x1a, 0x19, 0x28, 0x18, 0x29, 0x2a, 0x1b,
|
||||
0x1f, 0x2e, 0x2d, 0x1c, 0x2c, 0x1d, 0x1e, 0x2f
|
||||
];
|
||||
|
||||
hamming_24_18_inverse_error = [
|
||||
0x00000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x00000002, 0x00000004, 0x00000008,
|
||||
0x00000000, 0x00000010, 0x00000020, 0x00000040,
|
||||
0x00000080, 0x00000100, 0x00000200, 0x00000400,
|
||||
0x00000000, 0x00000800, 0x00001000, 0x00002000,
|
||||
0x00004000, 0x00008000, 0x00010000, 0x00020000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000,
|
||||
0x80000000, 0x80000000, 0x80000000, 0x80000000
|
||||
];
|
1947
teletext/tv.js
Normal file
1947
teletext/tv.js
Normal file
File diff suppressed because it is too large
Load diff
177
teletext/viewer.js
Normal file
177
teletext/viewer.js
Normal file
|
@ -0,0 +1,177 @@
|
|||
"use strict";
|
||||
|
||||
let staticImg;
|
||||
let videoCanvas = document.createElement("canvas");
|
||||
let videoCanvasContext = videoCanvas.getContext("2d");
|
||||
let renderer;
|
||||
let paramsNoUI = 0;
|
||||
let hashStringProperties = {};
|
||||
|
||||
/* define variables for settings.js */
|
||||
let widescreen = false;
|
||||
let prompt = false;
|
||||
let unevenFlashRatio = false;
|
||||
let autoPowerOffTime = 30;
|
||||
let defaultHashString = "";
|
||||
|
||||
window.addEventListener('load', init, false);
|
||||
|
||||
let hashString;
|
||||
window.addEventListener("hashchange", hashStringChanged, false); /* listener for hash changes */
|
||||
|
||||
function hashStringChanged(){
|
||||
if (window.location.hash.substring(1) != hashString)
|
||||
window.location.reload(); // hashstring changed out of our control
|
||||
}
|
||||
|
||||
function init(){
|
||||
let vars = window.location.search.substring(1).split("&");
|
||||
for (let i=0;i<vars.length;i++) {
|
||||
let pair = vars[i].split("=");
|
||||
switch (pair[0]){
|
||||
case "noUI":
|
||||
paramsNoUI = parseInt(pair[1]);
|
||||
break;
|
||||
case "widescreen":
|
||||
widescreen=parseInt(pair[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.hash.length == 0 && defaultHashString)
|
||||
{
|
||||
hashString = defaultHashString.replace(/^#/, '');
|
||||
window.location.hash = hashString;
|
||||
}
|
||||
|
||||
if (window.location.hash.length > 1)
|
||||
{
|
||||
let hashParts = window.location.hash.substring(1).split(":", -1);
|
||||
for (let i = 0; i < hashParts.length; i++){
|
||||
let keyPair = hashParts[i];
|
||||
let delimOffset = keyPair.search("=");
|
||||
if (delimOffset > 0){
|
||||
hashStringProperties[keyPair.slice(0,delimOffset)] = keyPair.slice(delimOffset+1);
|
||||
} else {
|
||||
console.log("invalid keypair ",keyPair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
videoCanvas.width = widescreen?1024:768;
|
||||
videoCanvas.height = 576;
|
||||
|
||||
renderer = new TeletextRenderer(document.getElementById("teletextCanvas"), videoCanvas, document.getElementById("screen"));
|
||||
|
||||
renderer.setTeletextOpacity(0); // hide teletext
|
||||
renderer.setWidescreen(widescreen);
|
||||
|
||||
renderer.setUnevenFlashRatio(unevenFlashRatio);
|
||||
|
||||
staticImg = document.getElementById("static");
|
||||
|
||||
videoCanvasContext.rect(0, 0, widescreen?1024:768, 576);
|
||||
videoCanvasContext.fillStyle = "black";
|
||||
videoCanvasContext.fill(); // blank image
|
||||
|
||||
if (paramsNoUI){
|
||||
if (typeof customNoUI !== "undefined"){
|
||||
customNoUI();
|
||||
} else {
|
||||
document.getElementById("header").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
if (prompt && hashStringProperties.id){
|
||||
let startImage = document.getElementById("start");
|
||||
let iw = startImage.width;
|
||||
let ih = startImage.height;
|
||||
let is;
|
||||
if ((iw / ih) > (4/3))
|
||||
is = (widescreen?1024:768) / iw;
|
||||
else
|
||||
is = 576 / ih;
|
||||
let w = iw * is;
|
||||
let h = ih * is;
|
||||
let cx = (widescreen?1024:768) / 2;
|
||||
videoCanvasContext.drawImage(startImage,cx - (w / 2),288 - (h / 2),w,h);
|
||||
document.body.onclick=function(){
|
||||
document.body.onclick="";
|
||||
videoCanvasContext.fillStyle = "black";
|
||||
videoCanvasContext.fill(); // blank image
|
||||
initTV();
|
||||
initRemote(document.getElementById("remote"));
|
||||
initRemote(document.getElementById("remote2"));
|
||||
}
|
||||
} else {
|
||||
initTV();
|
||||
initRemote(document.getElementById("remote"));
|
||||
initRemote(document.getElementById("remote2"));
|
||||
}
|
||||
|
||||
displayRemote();
|
||||
window.addEventListener("orientationchange", displayRemote);
|
||||
window.addEventListener("resize", displayRemote);
|
||||
}
|
||||
|
||||
function updateHashstring(){
|
||||
hashString = ""
|
||||
let num = Object.keys(hashStringProperties).length
|
||||
for (let i = 0; i < num; i++){
|
||||
hashString += Object.keys(hashStringProperties)[i]+"="+Object.values(hashStringProperties)[i]
|
||||
if (i < num - 1)
|
||||
hashString += ":"
|
||||
}
|
||||
window.location.hash = hashString;
|
||||
}
|
||||
|
||||
function displayRemote(){
|
||||
if (typeof customDisplayRemote !== "undefined"){
|
||||
// customDisplayRemote is an optional function to set css appropriately for custom UIs
|
||||
customDisplayRemote();
|
||||
} else {
|
||||
if (paramsNoUI){
|
||||
document.getElementById("right").style.display = "none";
|
||||
document.getElementById("below").style.display = "none";
|
||||
} else {
|
||||
if ((window.innerWidth > (widescreen?1024:768)) || (window.innerHeight <= window.innerWidth)){
|
||||
document.getElementById("right").style.display = "block";
|
||||
document.getElementById("below").style.display = "none";
|
||||
} else {
|
||||
document.getElementById("right").style.display = "none";
|
||||
document.getElementById("below").style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
let vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
}
|
||||
}
|
||||
|
||||
function blank1handler(){
|
||||
if (typeof customBlank1Handler !== "undefined"){
|
||||
// override secret button with custom handler
|
||||
customBlank1Handler();
|
||||
} else {
|
||||
let element = document.getElementById("viewer");
|
||||
|
||||
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement){
|
||||
if (document.exitFullscreen)
|
||||
document.exitFullscreen();
|
||||
else if (document.mozCancelFullScreen)
|
||||
document.mozCancelFullScreen();
|
||||
else if (document.webkitExitFullscreen)
|
||||
document.webkitExitFullscreen();
|
||||
} else {
|
||||
if (element.requestFullscreen)
|
||||
element.requestFullscreen();
|
||||
else if (element.mozRequestFullScreen)
|
||||
element.mozRequestFullScreen();
|
||||
else if (element.webkitRequestFullscreen)
|
||||
element.webkitRequestFullscreen();
|
||||
}
|
||||
|
||||
let vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue