Initial commit

This commit is contained in:
Midgard 2020-06-17 20:49:28 +02:00
commit 05abd1789b
Signed by: midgard
GPG key ID: 511C112F1331BBB4
32 changed files with 1859 additions and 0 deletions

147
htdocs/css/infoboards.css Normal file
View file

@ -0,0 +1,147 @@
/*** Stop info board ***************************************************************/
.stopInfoboard {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
margin: 0 auto 1em;
}
@media (min-width:500px) {
.stopInfoboard {
width: auto;
min-width: 30%;
max-width: 1000px;
}
}
td {
padding: 0.4em 0.1em;
text-align: left;
}
@media (min-width:500px) {
td {
padding: 0.4em;
}
}
.noLines {
font-style: italic;
color: #777;
}
/*** Shared info board styles ******************************************************/
tr.line.even td {
background-color: #eee;
}
tr.line .lineNumber {
text-align: right;
width: 3em;
font-size: 80%;
font-weight: bold;
}
@media (min-width:500px) {
tr.line .lineNumber {
font-size: 100%;
font-weight: normal;
}
}
tr.line .destination {
font-size: 85%;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
@media (min-width:500px) {
tr.line .destination {
font-size: 100%;
}
}
.delay {
font-size: 90%;
color: #daa;
margin-right: 0.5em;
text-align: right;
}
.delay.early {
color: #aad;
}
.departureH {
width: 0;
text-align: right;
padding-right: 0;
}
.departureSep {
width: 0;
text-align: center;
padding-left: 0;
padding-right: 0;
}
.departureMS {
width: 0;
padding-left: 0;
}
@media (min-width:500px) {
.departureH {
padding-left: 1em;
}
}
.theoretical {
font-size: 90%;
color: #a00;
font-family: sans;
}
.seconds {
font-size: 90%;
color: #888;
}
table .seconds {
margin-left: 0.2em;
}
.disturbance {
background: #fff8e0;
border: 1px solid #ffd800;
max-width: 50em;
margin: 0 auto 1em;
padding: 0.2em 0.4em;
}
.disturbance ul, .disturbance li, .disturbance p {
margin: 0;
margin-top: 0.2em;
padding: 0;
list-style: none;
white-space: pre-wrap;
}
.disturbance h3, .disturbance h4 {
margin: 0;
font-size: 100%;
}
.disturbance h3 {
margin: -0.2em -0.4em 0;
padding: 0.2em 0.4em;
background: #fff0a0;
border-bottom: 1px solid #ffd800;
}
.disturbance p.affectedLines {
font-size: 70%;
margin-bottom: 1em;
}
.affectedLines .line-nr {
padding: 0 0.05em;
}
tr.canceled.line.even td {
background: #edd;
}
tr.canceled.line.odd td {
background: #fee;
}

142
htdocs/css/main.css Normal file
View file

@ -0,0 +1,142 @@
body {
font-family: sans-serif;
margin: 0;
padding: 0 0 10px 0;
text-align: center;
}
.group-text {
display: inline-block;
}
.deemphasize {
color: #aaa;
}
input {
font-size: inherit;
max-width: 100%;
box-sizing: border-box;
}
input[type=text] {
font: inherit;
}
/*** Invisible space for correct text wrapping *************************************/
.invisiblespace {
font-size: 0pt;
}
/*** Header and footer *************************************************************/
.header {
background-color: #2b3034;
color: #fff;
border-bottom: 1px solid #ffd800;
padding: 0.5em 0;
text-align: center;
}
@media (min-width:500px) {
.header {
padding: 1em 0;
}
}
h1, .subtitle {
display: inline-block;
}
.subtitle {
margin-left: 0.5em;
}
h1, h2 {
margin: 0;
padding: 0;
font-size: 120%;
}
h1 a {
color: inherit;
text-decoration: none;
}
h2 {
margin-top: 1em;
font-size: 110%;
}
@media (min-width:500px) {
h1 {
font-size: 160%;
}
h2 {
font-size: 140%;
}
}
.info, .footnote {
text-align: center;
padding: 0 1em;
}
.info {
margin-bottom: 1em;
}
.info .stopref {
margin: 0.4em 0;
}
@media (min-width:500px) {
.info {
width: 400px;
text-align: right;
min-width: 30%;
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
.info .stopref {
float: left;
margin: 0;
}
}
.bottom-search div {
margin: 0.3em 0;
}
/*** Search elements ***************************************************************/
.mainsearch input {
font-size: 120%;
}
@media (min-width:500px) {
.mainsearch input {
font-size: 140%;
}
}
.search {
margin-top: 1em;
}
/*** Error messages ***************************************************************/
.user-error {
color: #333;
margin: 1em 0;
font-size: 150%;
}
/*** Line numbers ******************************************************************/
span.line-nr {
display: inline-block;
text-align: center;
width: 2em;
padding: 0.1em;
border: 2px solid transparent;
border-radius: 5px;
}
span.small.line-nr {
font-size: 75%;
}

View file

@ -0,0 +1,27 @@
.search-results .result {
margin: 1em 0;
}
.search-results a.result.stop {
display: block;
color: inherit;
text-decoration: none;
}
.search-results .result.stop .name {
font-weight: bold;
}
.search-results .result.stop .ref {
color: #aaa;
}
.search-results .result.stop .dest,
.search-results .result.stop .lines {
display: block;
}
.search-results .result.stop .not-served-msg {
color: #800;
}

View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="16"
height="16"
id="svg3187"
inkscape:version="0.48.4 r9939"
sodipodi:docname="delijn-pixelperfect.svg"
inkscape:export-filename="favicon-32.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="997"
id="namedview61"
showgrid="true"
inkscape:showpageshadow="false"
inkscape:zoom="22.627417"
inkscape:cx="16.153472"
inkscape:cy="8.4656119"
inkscape:window-x="-5"
inkscape:window-y="-10"
inkscape:window-maximized="1"
inkscape:current-layer="svg3187"
showborder="false">
<inkscape:grid
empspacing="4"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.25px"
spacingy="0.25px"
type="xygrid"
id="grid3038" />
</sodipodi:namedview>
<defs
id="defs3189" />
<metadata
id="metadata3192">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1;stroke-width:0.50000000000000000;stroke-miterlimit:4;stroke-dasharray:none"
d="m -1,-1 18,0 0,18 -18,0 z"
id="rect4215"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3023"
d="m 11,6 -2,0 0,4 2,0 0,-2 2,2 2,0 0,-4 -2,0 0,2 z"
sodipodi:nodetypes="ccccccccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3027"
d="M 2,9 2,6 0,6 0,10 3,10 3,9 z"
sodipodi:nodetypes="ccccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3031"
d="M 5,6 3,6 3,8 5,8 z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3043"
d="M 5.5,9 5,9 l 0,1 2,0 c 0.5,0 1,0 1,-1 L 8,6 6,6 6,8.5 C 6,9 5.75,9 5.5,9 z"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#ffd800;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 0,2 16,2 16,0 z"
id="path3047"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffd800;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,11 0,5 16,0 0,-5 z"
id="path3051"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3055"
d="m 4.25,4 0.5,0 0,-0.25 -0.5,0 0,-0.25 L 5,3.5 5,3 3.75,3 3.75,5 5,5 5,4.5 4.25,4.5 z"
sodipodi:nodetypes="ccccccccccccc" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3059"
d="M 3.5,4 C 3.5,3.25 3.25,3 3,3 L 2,3 2,5 3,5 C 3.25,5 3.5,4.75 3.5,4 z m -1,0.5 0,-1 C 2.75,3.5 3,3.5 3,4 3,4.5 2.75,4.5 2.5,4.5 z"
sodipodi:nodetypes="sccccsccsc" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3067"
d="M 9,5 9,3 6,3 6,5 z"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#004494;fill-opacity:1;stroke:none"
d="m 10,3 2,0 0,2 -2,0 z"
id="rect4206"
inkscape:connector-curvature="0" />
<path
style="fill:#004494;fill-opacity:1;stroke:none"
d="m 13,3 1,0 0,2 -1,0 z"
id="rect4208"
inkscape:connector-curvature="0" />
<path
style="fill:#004494;fill-opacity:1;stroke:none"
d="m 15,3 1,0 0,2 -1,0 z"
id="rect4212" />
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

166
htdocs/img/delijn.svg Normal file
View file

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="16"
height="16"
id="svg3187"
inkscape:version="0.48.4 r9939"
sodipodi:docname="delijn.svg">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="997"
id="namedview61"
showgrid="true"
inkscape:showpageshadow="false"
inkscape:zoom="33.941126"
inkscape:cx="7.4231543"
inkscape:cy="6.3421583"
inkscape:window-x="-5"
inkscape:window-y="-10"
inkscape:window-maximized="1"
inkscape:current-layer="svg3187">
<inkscape:grid
type="xygrid"
id="grid3038"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<defs
id="defs3189" />
<metadata
id="metadata3192">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3023"
d="m 7.8137831,5.8602123 -1.3288861,0 -0.4579433,2.5911185 1.3288861,0 0.2027545,-1.1520314 0.4447395,1.1520314 1.3964286,0 0.4579433,-2.5901028 -1.3960478,0 -0.2065632,1.1616803 -0.4413116,-1.162696 z" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3027"
d="m 1.5755977,7.8146792 0.3439336,-1.9534004 -1.43540528,0 -0.45591192,2.5913724 2.2421065,0 0.1119784,-0.637972 -0.8067013,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3031"
d="m 4.1864457,5.8624671 -1.4587658,0 -0.3208269,1.8119674 1.4583849,0 0.3212078,-1.8119674 z" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3043"
d="M 4.2852081,7.6183433 C 4.2589275,7.7667591 4.2020496,7.8143689 4.0917217,7.8143689 L 3.8405956,7.8132263 3.7281094,8.4524679 4.750515,8.4527218 c 0.5513856,0 0.8616748,-0.292134 0.9787316,-0.9557519 l 0.2881983,-1.6346048 -1.4233441,0 -0.3088927,1.7559782 z" />
<path
style="fill:#ffd800;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 5.46875 0.875 L 4.875 4.34375 L 15.375 4.34375 L 16 0.875 L 5.46875 0.875 z "
id="path3047" />
<path
style="fill:#ffd800;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 4.0625 8.875 L 2.8125 16 L 13.34375 16 L 14.59375 8.875 L 4.0625 8.875 z "
id="path3051" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3055"
d="m 4.0943878,5.1596437 0.1174376,0 0.022726,-0.1268326 -0.1176916,0 0.017267,-0.08811 0.2116416,0 0.027296,-0.1630161 -0.5268823,0 -0.1141367,0.6458435 0.5270093,0 0.030343,-0.1694911 -0.2111338,-1.27e-4 0.016124,-0.098267 z" />
<path
inkscape:connector-curvature="0"
style="fill:#576065;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3059"
d="M 3.470064,5.4189967 C 3.576964,5.3958901 3.6861492,5.3225074 3.7262685,5.0988046 3.7576275,4.9229655 3.6772621,4.8299042 3.6126396,4.8034966 3.5600784,4.7822943 3.4738728,4.7817865 3.4251203,4.7817865 L 2.9175358,4.7815325 2.8041609,5.42763 l 0.5086002,0 c 0.038977,0 0.1052495,0.00254 0.1573029,-0.00863 m -0.2711857,-0.1641588 -0.045325,0 0.052942,-0.30775 0.045324,0 c 0,0 0.050403,-0.00267 0.087094,0.00774 0.031359,0.00914 0.070844,0.060687 0.056116,0.1433374 -0.018917,0.1052495 -0.071859,0.1416869 -0.1221352,0.1524785 -0.024503,0.00533 -0.074017,0.00419 -0.074017,0.00419" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3063"
d="m 9.9322061,5.4288742 0.1141369,-0.6467322 -1.9619069,0 -0.1140097,0.6467322 1.9617797,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3067"
d="m 7.8823291,5.4288742 0.1141367,-0.6467322 -3.211824,0 -0.1141367,0.6467322 3.211824,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3071"
d="m 11.18771,5.4288742 0.114264,-0.6467322 -1.163204,0 -0.11401,0.6467322 1.16295,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3075"
d="m 11.978236,5.4288742 0.114137,-0.6467322 -0.685963,0 -0.114137,0.6467322 0.685963,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3079"
d="m 12.521382,5.4288742 0.114137,-0.6467322 -0.421633,0 -0.11401,0.6467322 0.421506,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3083"
d="m 12.920581,5.4288742 0.114137,-0.6467322 -0.261029,0 -0.114137,0.6467322 0.261029,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3087"
d="m 13.231391,5.4288742 0.114137,-0.6467322 -0.149813,0 -0.114136,0.6467322 0.149812,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3091"
d="m 13.540715,5.4288742 0.114137,-0.6467322 -0.116168,0 -0.114137,0.6467322 0.116168,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3095"
d="m 13.810695,5.4288742 0.114137,-0.6467322 -0.07148,0 -0.114136,0.6467322 0.07148,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3099"
d="m 14.112148,5.4288742 0.11401,-0.6467322 -0.0551,0 -0.114136,0.6467322 0.05523,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3103"
d="m 14.431007,5.4288742 0.114137,-0.6467322 -0.05129,0 -0.113883,0.6467322 0.05104,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3107"
d="m 14.781987,5.4288742 0.114137,-0.6467322 -0.03923,0 -0.114137,0.6467322 0.03923,0 z" />
<path
inkscape:connector-curvature="0"
style="fill:#004494;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3111"
d="m 15.193234,5.4288742 0.114137,-0.6467322 -0.03073,0 -0.114136,0.6467322 0.03072,0 z" />
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
htdocs/img/favicon-32.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
htdocs/img/favicon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

BIN
htdocs/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
htdocs/img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

64
htdocs/index.php Normal file
View file

@ -0,0 +1,64 @@
<?php
error_reporting(0);
ini_set('display_errors', 'Off');
require('./main.php');
$ui;
// Display stop info board or search for stops
if (isset($_GET['h']) && $_GET['h']) {
$stop = $_GET['h'];
if (preg_match('/^\d{5,}$/', $stop) === 1) {
$ui = new StopInfoboard($stop);
} else {
$searchquery = new SearchQuery($stop, SEARCH_STOPS);
$searchresults = $searchquery->execute();
$ui = new ResultsInterface($stop, $searchresults);
}
}
// Display line info board or search for lines
/* elseif (isset($_GET['l'])) {
$lineRef = $_GET['l'];
$ui = new LineInfoboard($lineRef);
}*/
// Show search interface
else {
$ui = new SearchUI();
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=240, initial-scale=1" />
<meta name="format-detection" content="telephone=no" />
<link rel="shortcut icon" href="img/favicon.ico" />
<title><?php $title = $ui->title(); if ($title) echo $title . ' '; ?>De Lijn Reisinformatie lite</title>
<style>
@import url('css/main.css');
@import url('css/searchresults.css');
@import url('css/infoboards.css');
</style>
</head>
<body>
<?php
$ui->display();
?>
</body>
</html>

39
htdocs/main.php Normal file
View file

@ -0,0 +1,39 @@
<?php
$timezone = new DateTimeZone('Europe/Brussels');
$includesDir = '/mnt/imagestore/rumores.eu/delijnincludes/';
/*** API's ***/
require($includesDir.'/ApiRequest.php');
require($includesDir.'/ApiCoreRequest.php');
require($includesDir.'/ApiSearchRequest.php');
require($includesDir.'/SearchQuery.php');
/*** HTML tools and UIs ***/
require($includesDir.'/HTMLTools.php');
require($includesDir.'/UI.php');
require($includesDir.'/Infoboard.php');
require($includesDir.'/StopInfoboard.php');
require($includesDir.'/LineInfoboard.php');
require($includesDir.'/ResultsInterface.php');
require($includesDir.'/SearchUI.php');
/*** Public transport elements ***/
require($includesDir.'/PTElement.php');
require($includesDir.'/PTLine.php');
require($includesDir.'/PTStop.php');
/*** Calculators and helpers ***/
require($includesDir.'/DepartureFormatter.php');
require($includesDir.'/DelayCalculator.php');
class NotImplementedException extends Exception {}
?>

91
htdocs/old/haltebord.css Normal file
View file

@ -0,0 +1,91 @@
body {
font-family: sans-serif;
margin: 0;
padding: 10px 0;
}
h1 {
margin: 0;
padding: 0;
font-size: 120%;
text-align: center;
}
.info, .footnote {
text-align: center;
padding: 0 1em;
}
.info {
margin-bottom: 1em;
}
@media (min-width:500px) {
.info {
width: 400px;
text-align: right;
min-width: 30%;
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
.info .stopref {
float: left;
}
}
table.stopInfoboard {
min-width: 30%;
max-width: 1000px;
margin: 0 auto;
border-spacing: 0;
border-collapse: collapse;
}
table.stopInfoboard td {
padding: 0.4em 0.1em;
}
@media (min-width:500px) {
table.stopInfoboard td {
padding: 0.4em;
}
}
tr.line.even td {
background-color: #eee;
}
tr.line .lineNumber {
text-align: right;
width: 3em;
}
tr.line .lineNumber span {
display: inline-block;
text-align: center;
width: 2em;
padding: 0.1em;
margin-right: 0.4em;
border: 2px solid transparent;
border-radius: 5px;
}
tr.line .departure {
text-align: right;
padding: 0 0.2em;
}
@media (min-width:500px) {
tr.line .departure {
padding: 0 1em;
}
}
.delay {
font-weight: bold;
font-size: 90%;
color: #f00;
}
.theoretical {
font-size: 90%;
color: #a00;
font-family: Impact, fantasy, serif;
}

27
htdocs/old/index.php Normal file
View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="format-detection" content="telephone=no" />
<title>Haltebord lite</title>
<style>
@import url('haltebord.css');
</style>
</head>
<body>
<?php
require('./main.php');
$stopRef = '506415';
if (isset($_GET['h'])) {
$stopRef = $_GET['h'];
}
$infoboard = new StopInfoboard($stopRef);
$infoboard->make();
?>
</body>
</html>

20
htdocs/old/main.php Normal file
View file

@ -0,0 +1,20 @@
<?php
$timezone = new DateTimeZone('Europe/Brussels');
$includesDir = '/mnt/imagestore/rumores.eu/delijnincludes/old/';
require($includesDir.'/ApiRequest.php');
require($includesDir.'/ApiCoreRequest.php');
require($includesDir.'/PTElement.php');
require($includesDir.'/PTLine.php');
require($includesDir.'/PTStop.php');
require($includesDir.'/StopInfoboard.php');
require($includesDir.'/DepartureFormatter.php');
require($includesDir.'/DelayCalculator.php');
class NotImplementedException extends Exception {}
?>

View file

@ -0,0 +1,18 @@
<?php
class ApiCoreRequest extends ApiRequest {
protected static $apiName = 'rise-api-core';
public function __toString () {
return 'ApiCoreRequest[action=' . $this->action . ', method=' . $this->method . ']';
}
public function __construct ($action, $method='GET') {
parent::__construct(static::$apiName, $action, $method);
}
}
?>

92
includes/ApiRequest.php Normal file
View file

@ -0,0 +1,92 @@
<?php
class ApiRequest {
protected static $baseURL = 'https://www.delijn.be';
protected $apiPath;
protected $action;
protected $url;
protected $http_response_header;
public function __toString () {
return 'ApiRequest[url=' . $this->url . ']';
}
public function __construct ($apiPath, $action, $method='GET') {
if (!is_string($action)) {
throw new InvalidArgumentException('$action must be a string');
}
$this->apiPath = $apiPath;
$this->action = $action;
$this->url = Static::$baseURL.'/'.$apiPath.'/'.$action;
if (preg_match('~//.*//~',$this->url) === 1) {
throw new InvalidArgumentException('Resulting URL ('.$this->url.') contains a double slash');
}
if (!is_string($method)) {
throw new InvalidArgumentException('$method must be a string');
}
switch ($method) {
case 'GET': case 'POST': case 'PUT': case 'DELETE': break;
throw new InvalidArgumentException('$method must be one of the following: "GET", "POST", "PUT", "DELETE"');
}
$this->method = $method;
}
public function exec ($dataToSend=null) {
$opts = array('http' =>
array(
'method' => $this->method
)
);
if (!is_null($dataToSend)) {
if (is_array($dataToSend)) {
$opts['http']['content'] = http_build_query($dataToSend);
} else {
throw new InvalidArgumentException('$dataToSend must be either NULL or an array');
}
}
$context = stream_context_create($opts);
$json = @file_get_contents(
$this->url, false, $context
);
// Make the HTTP response header available if they want to get it
// If there is no network connection, $http_response_header may be undefined
if (isset($http_response_header)) {
$this->http_response_header = $http_response_header;
} else {
$this->http_response_header = null;
}
$result = json_decode($json, true);
switch (json_last_error()) {
case JSON_ERROR_NONE:
return $result;
break;
case JSON_ERROR_SYNTAX:
throw new Exception('Result was not valid JSON: ' . json_last_error_msg());
break;
default:
throw new Exception('Result could not be parsed as JSON: ' . json_last_error_msg());
break;
}
return null;
}
public function getHttpResponseHeader () {
return $this->http_response_header;
}
}
?>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,18 @@
<?php
class ApiSearchRequest extends ApiRequest {
protected static $apiName = 'rise-api-search';
public function __toString () {
return 'ApiCoreRequest[action=' . $this->action . ', method=' . $this->method . ']';
}
public function __construct ($action, $method='GET') {
parent::__construct(static::$apiName, $action, $method);
}
}
?>

View file

@ -0,0 +1,32 @@
<?php
class DelayCalculator {
private $currentTime;
public function __construct ($currentTime) {
if (!is_string($currentTime)) {
throw new InvalidArgumentException('$currentTime must be a string');
}
if (
( $this->currentTime = new DateTime($currentTime, $GLOBALS['timezone']) ) === false
) {
throw new InvalidArgumentException('$currentTime must be HH:MM');
}
#echo PHP_EOL;var_dump($this->currentTime);echo PHP_EOL;
}
public function calculateFromRemainingTime (DateInterval $timeToDeparture, DateTime $scheduledTime) {
$ct = clone $this->currentTime;
return $ct->add($timeToDeparture)->diff($scheduledTime);
}
public function calculateFromExpectedTime (DateTime $departureTime, DateTime $scheduledTime) {
return $departureTime->diff($scheduledTime);
}
}
?>

View file

@ -0,0 +1,43 @@
<?php
class DepartureFormatter {
//$realdeparturecalendar, $scheduleddeparture,
public static function format ($realdeparture, DelayCalculator $delayc) {
//$scheduleddepartureObj = new DateTime('@'.($scheduleddeparture/1000), $GLOBALS['timezone']);
//$delay = $realdepartureObj->diff($scheduleddepartureObj);
$realtime = null;
// Not realtime
if ( preg_match('/([12]?\d):([0-5]\d)/', $realdeparture) === 1 ) {
$realtime = false;
$time = DateTime::createFromFormat("G:i", $realdeparture, $GLOBALS['timezone']);
}
// Realtime
else {
$matches = array();
if ( preg_match('/(\d+)\'/', $realdeparture, $matches) === 1 ) {
$realtime = true;
$time = new DateTime("now", $GLOBALS['timezone']);
$time->add(new DateInterval("PT".$matches[1]."M"));
}
}
//$scheduleddepartureObj->setTimezone($GLOBALS['timezone']);
return array(
//$scheduleddepartureObj->format('G:i'),
$time->format('G:i'),
"+0",
//$delay->format('%R%i'),
$realtime
);
}
}
?>

25
includes/HTMLTools.php Normal file
View file

@ -0,0 +1,25 @@
<?php
class HTMLTools {
private static $invisibleSpace = '<span class="invisiblespace"> </span>';
public static function makeSafe ($string) {
return htmlentities($string);
}
public static function makeSafeAndWrapBetter ($string) {
return preg_replace(
array('~/|-|\.~'),
array(static::$invisibleSpace.'$0'.static::$invisibleSpace), // Put an invisible space before and after each slash, hyphen and period
static::makeSafe($string)
);
}
}
?>

10
includes/Infoboard.php Normal file
View file

@ -0,0 +1,10 @@
<?php
abstract class Infoboard extends UI {
protected static $theoreticalBefore = '<span class="theoretical">+</span>';
protected static $theoreticalAfter = '<span class="theoretical">+</span>';
}
?>

View file

@ -0,0 +1,19 @@
<?php
class LineInfoboard extends Infoboard {
public function title () {
throw new NotImplementedException();
}
public function display () {
throw new NotImplementedException();
}
}
?>

54
includes/PTElement.php Normal file
View file

@ -0,0 +1,54 @@
<?php
abstract class PTElement {
const UNKNOWN = 1;
const TRAM = 1;
const BUS = 2;
const BELBUS = 4;
protected $ref;
protected $publicRef;
protected $infoFetched = false;
public function __construct ($ref) {
if (!is_int($ref)) {
throw new InvalidArgumentException('$ref must be an integer');
}
$this->ref = $ref;
}
public function __toString () {
return 'PTElement[' . $this->ref . ']';
}
public function getRef () {
return $this->ref;
}
protected abstract function fetchInfo ();
public static function lineTypeTextToConst ($lineTypeText) {
switch ($lineTypeText) {
case 'tram';
return static::TRAM;
break;
case 'bus';
return static::BUS;
break;
case 'belbus';
return static::BELBUS;
break;
default;
return static::UNKNOWN;
break;
}
}
}
?>

79
includes/PTLine.php Normal file
View file

@ -0,0 +1,79 @@
<?php
class PTLine extends PTElement {
protected $entity;
protected $direction;
protected $lineType;
protected $publicRef;
protected $directionDesc;
public function __construct ($entity, $ref, $direction) {
parent::__construct($ref);
if (!is_int($entity)) {
throw new InvalidArgumentException('$entity must be an integer');
}
$this->entity = $entity;
if (!is_int($direction)) {
throw new InvalidArgumentException('$direction must be an integer');
}
$this->direction = $direction;
}
public function __toString () {
return 'PTLine[' . $this->entity . '/' . $this->ref . '/' . $this->direction . ']';
}
public function getRef () {
return $this->ref;
}
public function getPublicRef () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->publicRef;
}
public function getLineType () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->lineType;
}
public function getDirectionDesc () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->directionDesc;
}
protected function fetchInfo () {
$apireq = new ApiCoreRequest('lijnen/titel/' . $this->entity . '/' . $this->ref . '/' . $this->direction);
$response = $apireq->exec();
$this->lineType = PTElement::lineTypeTextToConst($response['lijnType']);
$this->publicRef = $response['lijnNummerPubliek'];
$this->directionDesc = $response['lijnRichting'];
}
public function getTimetable () {
throw new NotImplementedException();
}
public function getTimetableAtStop (PTStop $stop) {
throw new NotImplementedException();
}
public static function htmlElement($bgColor, $bgBorder, $fgColor, $lineNumber, $class='') {
return '<span class="line-nr ' . $class . '" style="' .
'background:' . HTMLTools::makeSafe($bgColor) . ';' .
'border-color:' . HTMLTools::makeSafe($bgBorder) . ';' .
'color:' . HTMLTools::makeSafe($fgColor) . ';' .
'">' .$lineNumber . '</span>';
}
}
?>

59
includes/PTStop.php Normal file
View file

@ -0,0 +1,59 @@
<?php
class PTStop extends PTElement {
protected $exists;
protected $municipality;
protected $name;
protected $fullName;
protected $location;
public function __toString () {
return 'PTStop[' . $this->ref . ']';
}
public function getExists () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->exists;
}
public function getMunicipality () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->municipality;
}
public function getName () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->name;
}
public function getFullName () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->fullName;
}
public function getLocation () {
if (!$this->infoFetched) $this->fetchInfo();
return $this->location;
}
protected function fetchInfo () {
$apireq = new ApiCoreRequest('haltes/titel/' . $this->ref);
$response = $apireq->exec();
$this->exists = !is_null($response);
$this->municipality = $response['omschrijvingGemeente'];
$this->name = $response['omschrijvingKort'];
$this->fullName = $response['omschrijvingLang'];
$this->location = array(floatval($response['coordinaat']['lt']), floatval($response['coordinaat']['ln']));
}
public function getImminentVehicles ($numberOfLines) {
$apireq = new ApiCoreRequest('haltes/doorkomstenditmoment/' . $this->ref . '/' . $numberOfLines);
return $apireq->exec();
}
}
?>

View file

@ -0,0 +1,86 @@
<?php
class ResultsInterface extends UI {
private $query;
private $stops;
public function __construct($searchQuery, $searchResults) {
$this->query = $searchQuery;
$this->stops = $searchResults['haltes'];
}
public function title () {
return 'Zoekresultaten';
}
public function display() {
parent::header();
echo '<form class="titlesearch">';
echo '<h2>Zoeken naar halte <input type="text" name="h" value="' .
HTMLTools::makeSafe($this->query) .
'" id="stop-searchfield"></input><input type="submit" value="Zoek"></input></h2>';
echo '</form>';
if (isset($this->stops[0])) {
echo '<div class="search-results">';
foreach ($this->stops as $stop) {
echo '<a href="?h=' . rawurlencode($stop['halteNummer']) . '" class="result stop">';
echo '<span class="name">' . $stop['omschrijvingLang'] . '</span> ';
echo '<span class="ref">(' . $stop['halteNummer'] . ')</span>';
if (isset($stop['bestemmingen'][0])) {
echo '<span class="dest"><span class="deemphasize">richting</span> ' . parent::listify($stop['bestemmingen']) . '</span>';
} else {
echo '<span class="dest deemphasize">richting onbekend</span>';
}
echo '<span class="lines">';
if (isset($stop['lijnen'][0])) {
foreach ($stop['lijnen'] as $line) {
echo PTLine::htmlElement(
$line['kleurAchterGrond'],
$line['kleurAchterGrondRand'],
$line['kleurVoorGrond'],
$line['lijnNummerPubliek'],
'small');
echo ' ';
}
} else {
echo '<span class="not-served-msg">niet bediend</span>';
}
echo '</span>';
echo '</a>';
}
echo '</div>';
} else {
echo '<div class="user-error">Geen resultaten</div>';
}
parent::footer();
}
}
?>

38
includes/SearchQuery.php Normal file
View file

@ -0,0 +1,38 @@
<?php
define('SEARCH_STOPS',1);
define('SEARCH_LINES',2);
define('SEARCH_ALL',SEARCH_STOPS|SEARCH_LINES);
class SearchQuery {
private $originalQueryString;
private $queryString;
private $searchForTypes;
public function __construct ($queryString, $searchForTypes=SEARCH_ALL) {
if (!is_string($queryString)) {
throw new InvalidArgumentException('$queryString must be a string');
}
if (!is_int($searchForTypes)) {
throw new InvalidArgumentException('$searchForTypes must be an integer (a bitwise OR-combination of SEARCH_...)');
}
$this->originalQueryString = $queryString;
$this->queryString = rawurlencode(strtolower(str_replace(array('"', "'", '-', '.', '%'), ' ', $queryString)));
$this->searchForTypes = $searchForTypes;
}
//https://www.delijn.be/rise-api-search/search/lijnen/knokke/1
public function execute () {
$req = new ApiSearchRequest('search/haltes/' . $this->queryString . '/1');
return $req->exec();
}
}
?>

31
includes/SearchUI.php Normal file
View file

@ -0,0 +1,31 @@
<?php
class SearchUI extends UI {
public function title () {
return false;
}
public function display () {
parent::header();
echo '<h2>Zoek halte</h2>';
echo '<form class="mainsearch">';
echo '<div class="inputlabel"><label for="stop-searchfield">Naam of haltenummer:</label></div>';
echo '<input type="text" name="h" id="stop-searchfield"/><input type="submit" value="Zoek"/>';
echo '</form>';
/* echo '<h2>Zoek lijn</h2>';
echo '<form class="mainsearch">';
echo '<div class="inputlabel"><label for="line-searchfield">Bestemming of lijnnummer:</label></div>';
echo '<input type="text" name="l" id="line-searchfield"/><input type="submit" value="Zoek"/>';
echo '</form>';*/
}
}
?>

237
includes/StopInfoboard.php Normal file
View file

@ -0,0 +1,237 @@
<?php
function compareDoorkomsten ($d1, $d2) {
$t1 = $d1['vertrekTheoretischeTijdstip'];
if (!empty($d1['vertrekRealtimeTijdstip'])) {
$t1 = $d1['vertrekRealtimeTijdstip'];
}
$t2 = $d2['vertrekTheoretischeTijdstip'];
if (!empty($d2['vertrekRealtimeTijdstip'])) {
$t2 = $d2['vertrekRealtimeTijdstip'];
}
return $t1 - $t2;
}
function formatDelay ($delay) {
$early = $delay < 0;
$totalSeconds = abs($delay);
$totalMinutes = round($totalSeconds / 60);
$hours = intdiv($totalMinutes, 60);
$minutes = $totalMinutes % 60;
$str = ($hours !== 0 ? $hours . 'h' : '') . ($minutes !== 0 ? $minutes . 'm' : '');
//return ($delay > 0 ? '+' : '-') . $str;
return $str . ($delay > 0 ? ' te laat' : ' te vroeg');
}
class StopInfoboard extends Infoboard {
private $found = false;
private $stop;
private $numberOfLines;
private $currentTime;
private $lines;
private $disturbed = false;
private $allLinesDisturbed = false;
private $disturbances = false;
private $notServed = false;
public function __construct ($stopRef, $numberOfLines=50) {
global $timezone;
$this->currentTime = new DateTime('now', $timezone);
$this->stop = new PTStop(intval($stopRef));
$this->numberOfLines = $numberOfLines;
$result = $this->stop->getImminentVehicles($numberOfLines);
$this->detours = $result['halteOmleidingen'];
$this->disturbed = $result['dienstRegelingVerstoord'];
$this->allLinesDisturbed = $result['alleLijnenVerstoord'];
$this->disturbances = $result['storingen'];
$this->notServed = $result['nietBediend'];
$this->lines = $result['lijnen'];
}
public function title() {
$name = $this->stop->getFullName();
if ($name) return $name;
return 'Halte';
}
public function display() {
global $timezone;
parent::header();
if (!$this->stop->getExists()) {
echo '<div class="user-error">De halte met haltenummer ' . HTMLTools::makeSafe($this->stop->getRef()) . ' werd niet gevonden.</div>';
parent::footer();
return;
}
echo '<h2>' . HTMLTools::makeSafe($this->stop->getFullName()) . '</h2>';
echo '<div class="info">';
echo '<div class="stopref">Haltenummer ' . HTMLTools::makeSafe($this->stop->getRef()) . '</div>';
echo '<div class="lastrefreshed">Info van ' . $this->currentTime->format('H:i') . '<span class="seconds">:' . $this->currentTime->format('s') . '</span></div>';
echo '</div>';
if (!empty($this->disturbances)) {
echo '<div class="disturbance"><h3>Storingen en mededelingen</h3><ul>';
foreach ($this->disturbances as $disturbance) {
echo '<li>' . HTMLTools::makeSafe($disturbance) . '</li>';
}
echo '</ul></div>';
}
if (!empty($this->detours)) {
echo '<div class="disturbance"><h3>Omleidingen</h3><ul>';
foreach ($this->detours as $detour) {
$description = $detour['omleidingen'][0];
echo '<li><h4>' . HTMLTools::makeSafe($description['title']) . '</h4><p>' . HTMLTools::makeSafe($description['omleiding']) . '</p>';
if (!empty($detour['verstoordeLijnenSet'])) {
echo '<p class="affectedLines">';
foreach ($detour['verstoordeLijnenSet'] as $line) {
echo PTLine::htmlElement(
$line['kleurAchterGrond'],
$line['kleurAchterGrondRand'],
$line['kleurVoorGrond'],
$line['lijnNummerPubliek']);
echo ' ';
}
echo '</p>';
}
echo '</li>';
}
echo '</ul></div>';
}
if ($this->notServed) {
echo '<div class="disturbance">Deze halte wordt momenteel niet bediend.</div>';
} elseif ($this->allLinesDisturbed) {
echo '<div class="disturbance">De dienstregeling van alle lijnen aan deze halte is verstoord.</div>';
} elseif ($this->disturbed) {
echo '<div class="disturbance">De dienstregeling van sommige lijnen aan deze halte is verstoord.</div>';
}
if (empty($this->lines)) {
echo '<div class="stopInfoboard noLines">Geen doorkomsten in de nabije toekomst</div>';
} else {
$anyNotRealtime = false;
echo '<table class="stopInfoboard" border="0">';
$evenrow = true;
usort($this->lines, compareDoorkomsten);
foreach ($this->lines as $line) {
$scheduledTime = intdiv($line['vertrekTheoretischeTijdstip'], 1000);
$realtimeTime = intdiv($line['vertrekRealtimeTijdstip'], 1000);
if ($realtimeTime == 0) $realtimeTime = null;
$realtime = !is_null($realtimeTime);
if (!$realtime) $anyNotRealtime = true;
$timeDT = new DateTime('now', $timezone);
if ($realtime) {
$timeDT->setTimestamp($realtimeTime);
} else {
$timeDT->setTimestamp($scheduledTime);
}
$delay = $realtime ? $realtimeTime - $scheduledTime : 0;
$canceled = in_array('DELETED', $line['predictionStatussen']) || in_array('CANCELLED', $line['predictionStatussen']);
echo '<tr class="line' . ($evenrow?' even':' odd') . ($canceled?' canceled':'') . '">';
// Line number
echo '<td class="lineNumber">' . PTLine::htmlElement(
$line['kleurAchterGrond'],
$line['kleurAchterGrondRand'],
$line['kleurVoorGrond'],
$line['lijnNummerPubliek']) . '</td> ';
// Destination
echo '<td class="destination">';
if ($canceled) echo '<del>';
echo HTMLTools::makeSafeAndWrapBetter($line['bestemming']);
if ($canceled) echo '</del>';
echo '</td>';
// Delay
echo '<td class="delay ' . ($delay < 0 ? 'early' : '') . '">';
if ($canceled) {
echo 'Rijdt niet';
} elseif (abs($delay) >= 60) {
echo formatDelay($delay) . ':';
}
echo '</td>';
// Departure time
echo '<td class="departureH ' . ($realtime ? 'rt' : 'th') . '">';
if (!$realtime) {
echo static::$theoreticalBefore;
}
echo $timeDT->format("H");
echo '</td>';
echo '<td class="departureSep">:</td>';
echo '<td class="departureMS ' . ($realtime ? 'rt' : 'th') . '">';
echo $timeDT->format("i");
if ($realtime && intval($timeDT->format('s')) !== 0) {
echo '<span class="seconds">:' . $timeDT->format('s') . '</span>';
}
if (!$realtime) {
echo static::$theoreticalAfter;
}
echo '</td>';
echo '</tr>';
$evenrow = !$evenrow;
}
echo '</table>';
if ($anyNotRealtime) {
echo '<div class="footnote">' . static::$theoreticalBefore . 'Theoretische vertrektijd' . static::$theoreticalAfter . '</div>';
}
}
$coords = $this->stop->getLocation();
echo '<a href="geo:' . $coords[0] . ',' . $coords[1] . '?z=17">Locatie van halte</a>';
parent::footer();
}
}
?>

57
includes/UI.php Normal file
View file

@ -0,0 +1,57 @@
<?php
abstract class UI {
abstract public function title();
abstract public function display();
protected function header() {
echo '<div class="header">';
echo '<h1><a href="?">De Lijn Reisinformatie lite</a></h1> ';
echo '<div class="subtitle">(niet-officieel)</div>';
echo '</div>';
}
protected function footer() {
echo '<div class="bottom-search">';
echo '<h2>Zoek iets anders</h2>';
echo '<div><form>';
echo '<label for="stop-searchfield">Zoek halte:</label> <input type="text" name="h" id="stop-searchfield"></input><input type="submit" value="Zoek"></input>';
// echo '</form></div><div><form>';
// echo '<label for="line-searchfield">Zoek lijn:</label> <input type="text" name="l" id="line-searchfield"></input><input type="submit" value="Zoek"></input>';
echo '</form></div>';
echo '</div>';
}
public function listify($array) {
$listString = '';
$length = count($array);
switch ($length) {
case 0:
return false;
break;
case 1:
return HTMLTools::makeSafeAndWrapBetter($array[0]);
break;
case 2:
return HTMLTools::makeSafeAndWrapBetter($array[0]) . ' <span class="deemphasize">en</span> ' . HTMLTools::makeSafeAndWrapBetter($array[1]);
break;
default:
return HTMLTools::makeSafeAndWrapBetter($array[0]) . '<span class="deemphasize">,</span> ' . HTMLTools::makeSafeAndWrapBetter($array[1]) . '<span class="deemphasize">...</span>';
break;
}
}
}
?>

1
omleiding.json Normal file

File diff suppressed because one or more lines are too long