2023-01-14 00:41:33 +00:00
console . log ( "[+] Wow, JavaScript!" ) ;
import * as wasm from "rsp6-decoder" ;
import { BrowserAztecCodeReader } from '@zxing/library' ;
let stations = require ( "./stations.json" ) ;
let fares = require ( "./fares.json" ) ;
2023-01-14 12:19:02 +00:00
let discounts = require ( "./discounts.json" ) ;
2023-01-14 00:41:33 +00:00
window . stations = stations ;
window . fares = fares ;
2023-01-14 12:19:02 +00:00
window . discounts = discounts ;
2023-01-14 00:55:43 +00:00
let loaded = false ;
2023-01-14 00:41:33 +00:00
2023-01-14 00:55:43 +00:00
function onload ( ) {
if ( loaded ) {
return ;
}
loaded = true ;
2023-01-14 00:41:33 +00:00
console . log ( "[+] initialising wasm" ) ;
window . wasm = wasm ;
window . wasm . init ( ) ;
let video _div = document . getElementById ( "video-container" ) ;
let scan _button = document . getElementById ( "scan-button" ) ;
let error _banner = document . getElementById ( "error-banner" ) ;
let error _header = document . getElementById ( "error-header" ) ;
let error _text = document . getElementById ( "error-text" ) ;
let decode _banner = document . getElementById ( "decode-banner" ) ;
let decode _reason = document . getElementById ( "decode-reason" ) ;
let raw _barcode = document . getElementById ( "raw-barcode" ) ;
let raw _barcode _wrapper = document . getElementById ( "raw-barcode-wrapper" ) ;
let raw _json = document . getElementById ( "raw-json" ) ;
let ticket _data = document . getElementById ( "ticket-data" ) ;
let ticket _content = document . getElementById ( "ticket-content" ) ;
let file _upload = document . getElementById ( "file-upload-1" ) ;
const codeReader = new BrowserAztecCodeReader ( ) ;
console . log ( "[+] Looks like everything initialised fine!" ) ;
2023-01-14 12:19:02 +00:00
document . getElementById ( 'code-version' ) . innerHTML = _ _COMMIT _HASH _ _ ;
2023-01-14 00:41:33 +00:00
let selectedDeviceId ;
function error ( header , text ) {
video _div . style . display = "none" ;
error _header . innerHTML = header ;
error _text . innerHTML = text ;
error _banner . style . display = "block" ;
error _banner . focus ( ) ;
error _banner . scrollIntoView ( ) ;
}
function decodeError ( text ) {
decode _banner . style . display = "block" ;
decode _reason . innerHTML = text ;
decode _banner . scrollIntoView ( ) ;
}
function row ( k , v ) {
let tr = document . createElement ( "tr" ) ;
tr . classList . add ( "govuk-table__row" ) ;
let th = document . createElement ( "th" ) ;
th . classList . add ( "govuk-table__header" ) ;
th . scope = "row" ;
let td = document . createElement ( "td" ) ;
td . classList . add ( "govuk-table__cell" ) ;
th . innerHTML = k ;
td . innerHTML = v ;
tr . appendChild ( th ) ;
tr . appendChild ( td ) ;
ticket _data . appendChild ( tr ) ;
}
function nlcify ( nlc ) {
let data = stations [ nlc ] ;
if ( ! data || ! data . desc ) {
return nlc ;
}
else {
if ( data . crs ) {
return data . crs ;
}
else {
return nlc ;
}
}
}
function handleTicket ( ticket ) {
window . ticket = ticket ;
video _div . style . display = "none" ;
codeReader . reset ( ) ;
console . log ( "Got ticket: " + ticket ) ;
raw _barcode . innerHTML = ticket ;
raw _barcode _wrapper . style . display = "block" ;
try {
let result = window . wasm . decode _ticket ( window . ticket ) ;
if ( result [ "Err" ] ) {
decodeError ( result [ "Err" ] ) ;
}
else {
let data = result [ "Ok" ] ;
raw _json . innerHTML = JSON . stringify ( data , null , 2 ) ;
ticket _data . innerHTML = "" ;
document . getElementById ( "origin" ) . innerHTML = nlcify ( data . origin _nlc ) ;
document . getElementById ( "dest" ) . innerHTML = nlcify ( data . destination _nlc ) ;
let fare = fares [ data . fare ] || ( "Unknown fare (" + data . fare + ")" ) ;
let origin = stations [ data . origin _nlc ] ? stations [ data . origin _nlc ] . desc : "NLC " + data . origin _nlc ;
let destination = stations [ data . destination _nlc ] ? stations [ data . destination _nlc ] . desc : "NLC " + data . destination _nlc ;
let type = "" ;
if ( data . coupon _type == "Single" ) {
type = "Single" ;
}
else if ( data . coupon _type == "ReturnOutbound" ) {
type = "Return (outbound)" ;
}
else if ( data . coupon _type == "ReturnInbound" ) {
type = "Return (inbound)" ;
}
else {
2023-01-14 12:19:02 +00:00
type = "Season" ;
}
if ( data . bidirectional ) {
type += " (bidirectional)" ;
2023-01-14 00:41:33 +00:00
}
2023-01-14 12:19:02 +00:00
let discount = null ;
let discount _name = null ;
if ( data . discount _code ) {
if ( discounts [ data . discount _code ] ) {
discount = discounts [ data . discount _code ] . desc ;
if ( discounts [ data . discount _code ] . code ) {
discount _name = discounts [ data . discount _code ] . code ;
}
}
else {
discount = "Unknown discount (" + data . discount _code + ")" ;
}
}
let adult = data . child _ticket ? "Child" : "Adult" ;
2023-03-02 22:39:22 +00:00
let first = data . standard _class ? "Standard Class" : "First Class" ;
2023-01-14 00:41:33 +00:00
document . getElementById ( "ticket-fromto" ) . innerHTML = origin + " to " + destination ;
2023-01-14 12:19:02 +00:00
if ( data . manually _inspect ) {
row ( "" , "<i>Manual inspection required</i>" ) ;
}
let brfares _link = "https://www.brfares.com/!faredetail?orig=" + data . origin _nlc + "&dest=" + data . destination _nlc + "&tkt=" + data . fare ;
if ( data . route _code && data . route _code != 0 ) {
brfares _link += "&rte=" + data . route _code ;
}
if ( discount _name ) {
brfares _link += "&rlc=" + discount _name ;
}
row ( "Fare" , fare + "<br/><small><a class=\"govuk-link\" href=\"" + brfares _link + "\">View on brfares.com</a></small>" ) ;
row ( "Class" , adult + " " + first ) ;
if ( discount ) {
row ( "Discount" , discount ) ;
}
2023-01-14 00:41:33 +00:00
row ( "Ticket type" , type ) ;
row ( "Start date" , data . start _date ) ;
if ( data . depart _time _flag || data . depart _time != "00:00:00.0" ) {
let extra = "" ;
if ( data . depart _time _flag == "Suggested" ) {
extra = " (suggested)" ;
}
else if ( data . depart _time _flag == "Specific" ) {
extra = " (mandatory)" ;
}
row ( "Depart at" , data . depart _time . substring ( 0 , 5 ) + extra ) ;
}
row ( "Ticket reference" , data . issuer _id + data . ticket _reference ) ;
if ( data . passenger _name ) {
row ( "Passenger name" , data . passenger _name ) ;
}
if ( data . purchase _details ) {
let dov = data . purchase _details . days _of _validity ;
let maybe _s = "" ;
if ( dov > 1 ) {
maybe _s = "s" ;
}
row ( "Valid for" , dov + " day" + maybe _s ) ;
}
2023-01-14 12:19:02 +00:00
if ( data . limited _duration ) {
row ( "Limited duration" , data . limited _duration ) ;
}
2023-01-14 00:41:33 +00:00
row ( "Fare code" , data . fare ) ;
if ( data . purchase _details ) {
let price = data . purchase _details . price _pence ;
row ( "Price" , "£" + Math . floor ( price / 100 ) + "." + ( price % 100 ) . toString ( ) . padStart ( 2 , "0" ) ) ;
row ( "Purchased" , data . purchase _details . purchase _time . substring ( 0 , 16 ) ) ;
if ( data . purchase _details . purchase _reference ) {
row ( "Purchase reference" , data . purchase _details . purchase _reference ) ;
}
}
2023-01-14 12:19:02 +00:00
if ( data . reservations . length > 0 ) {
let out = "" ;
data . reservations . forEach ( ( resv ) => {
if ( out != "" ) {
out += "<br/>" ;
}
out += "<i>Train " + resv . retail _service _id + "</i></br>" ;
2023-01-27 13:12:07 +00:00
if ( resv . coach == "*" || ( resv . seat _number == 0 && resv . coach == " " ) ) {
2023-01-14 12:19:02 +00:00
out += "No specific seat" ;
}
else {
if ( ! resv . coach || resv . coach == " " ) {
resv . coach = "?" ;
}
out += "Coach " + resv . coach + " Seat " + resv . seat _number + ( resv . seat _letter ? resv . seat _letter : "" ) ;
}
} ) ;
row ( "Reservations" , out ) ;
}
2023-01-14 00:41:33 +00:00
let retailer = stations [ data . retailer _id ] ? stations [ data . retailer _id ] . desc : "???" ;
row ( "Sold by" , retailer + " (" + data . retailer _id + ")" ) ;
if ( data . route _code != 0 ) {
row ( "Route code" , data . route _code ) ;
}
if ( data . restriction _code ) {
row ( "Restriction" , data . restriction _code ) ;
}
if ( data . discount _code != 0 ) {
2023-01-14 12:19:02 +00:00
row ( "Discount code" , ( discount _name ? discount _name + " / " : "" ) + data . discount _code ) ;
2023-01-14 00:41:33 +00:00
}
if ( data . passenger _id ) {
row ( "Passenger ID" , data . passenger _id ) ;
}
row ( "Lennon code" , data . lennon _ticket _type ) ;
row ( "Sequence number" , data . sub _utn ) ;
ticket _content . style . display = "block" ;
ticket _content . scrollIntoView ( ) ;
}
}
catch ( e ) {
decodeError ( "exception: " + e ) ;
}
}
window . handleTicket = handleTicket ;
codeReader . getVideoInputDevices ( )
. then ( ( videoInputDevices ) => {
console . log ( "[+] Detected " + videoInputDevices . length + " video sources" ) ;
if ( videoInputDevices . length >= 1 ) {
selectedDeviceId = videoInputDevices [ 0 ] . deviceId ;
scan _button . removeAttribute ( "disabled" ) ;
}
} )
. catch ( ( err ) => {
error ( "Scanner failed" , "Couldn't figure out what cameras you have on your device.<br/><pre>" + err + "</pre>" ) ;
} ) ;
2023-01-14 12:19:02 +00:00
let epoch = 0 ;
2023-01-14 00:41:33 +00:00
document . getElementById ( 'scan-button' ) . addEventListener ( 'click' , ( ) => {
2023-01-14 12:19:02 +00:00
let current _epoch = epoch + 1 ;
epoch = current _epoch ;
2023-01-14 00:41:33 +00:00
decode _banner . style . display = "none" ;
ticket _content . style . display = "none" ;
codeReader . decodeFromInputVideoDevice ( undefined , 'video' ) . then ( ( result ) => {
error _banner . style . display = "none" ;
handleTicket ( result ) ;
} ) . catch ( ( err ) => {
2023-01-14 12:19:02 +00:00
if ( epoch == current _epoch ) {
error ( "Scanner failed" , "Looks like the barcode scanner failed in some way. If this keeps happening, try reloading the page or using one of the alternative options.<br/><pre>" + err + "</pre>" ) ;
console . error ( err ) ;
}
2023-01-14 00:41:33 +00:00
} ) ;
video _div . style . display = "block" ;
console . log ( ` [+] Barcode scanner started on device ${ selectedDeviceId } ` ) ;
} ) ;
document . getElementById ( 'video' ) . addEventListener ( 'play' , ( ) => {
console . log ( '[+] Video started playing' ) ;
video _div . scrollIntoView ( ) ;
} ) ;
2023-01-14 12:24:20 +00:00
document . getElementById ( 'raw-text-form' ) . addEventListener ( 'submit' , ( e ) => {
e . preventDefault ( ) ;
2023-01-14 00:41:33 +00:00
console . log ( "[+] Raw text input pressed" ) ;
decode _banner . style . display = "none" ;
ticket _content . style . display = "none" ;
let value = document . getElementById ( 'raw-text-in' ) . value ;
document . getElementById ( 'text-details' ) . removeAttribute ( 'open' ) ;
handleTicket ( value ) ;
} ) ;
file _upload . addEventListener ( 'change' , ( ) => {
if ( file _upload . files . length > 0 ) {
console . log ( '[+] File selected' ) ;
const file = file _upload . files [ 0 ] ;
const uri = window . URL . createObjectURL ( file ) ;
console . log ( '[+] Scanning URL ' + uri ) ;
const img = document . createElement ( "img" ) ;
img . src = uri ;
img . videoWidth = 0 ;
let done = false ;
document . getElementById ( 'image-sanctuary' ) . innerHTML = '' ;
document . getElementById ( 'image-sanctuary' ) . appendChild ( img ) ;
decode _banner . style . display = "none" ;
ticket _content . style . display = "none" ;
codeReader . reset ( ) ;
setTimeout ( ( ) => {
if ( ! done ) {
console . log ( "[+] Timed out!" ) ;
codeReader . reset ( ) ;
}
} , 750 ) ;
codeReader . decodeFromImage ( img ) . then ( ( result ) => {
console . log ( "[+] Screenshot decode done!" ) ;
done = true ;
document . getElementById ( 'screenshot-details' ) . removeAttribute ( 'open' ) ;
error _banner . style . display = "none" ;
window . URL . revokeObjectURL ( uri ) ;
handleTicket ( result ) ;
} ) . catch ( ( err ) => {
done = true ;
console . error ( err ) ;
error ( "Barcode reader failed" , "Couldn't detect a barcode in that screenshot. Try another image.<br/><pre>" + err + "</pre>" ) ;
window . URL . revokeObjectURL ( uri ) ;
} ) ;
}
} ) ;
2023-01-14 00:55:43 +00:00
}
2023-01-14 00:41:33 +00:00
2023-01-14 00:55:43 +00:00
if ( document . readyState == 'complete' ) {
console . log ( "[+] Loaded already!" ) ;
onload ( ) ;
}
else {
window . addEventListener ( 'load' , onload ) ;
}