File indexing completed on 2024-12-29 04:51:04
0001 /* 0002 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 function readRSP6String(data, start, len) { 0007 let result = ""; 0008 for (let i = 0; i < len; ++i) { 0009 const c = data.readNumberMSB(i * 6 + start, 6) + 32; 0010 result += String.fromCharCode(c); 0011 } 0012 return result.trim() ? result : null; 0013 } 0014 0015 function readRSP6DateTime(data, start) { 0016 // this is in local time, ie. we need to convince JS to not mess with the timezone 0017 const days = data.readNumberMSB(start, 14); 0018 const minutes = data.readNumberMSB(start + 14, 11); 0019 let dt = new Date(Date.UTC(1997, 0, 1)); 0020 dt.setTime(dt.getTime() + days * 24 * 60 * 60 * 1000 + minutes * 1000 * 60); 0021 return dt.toJSON().substr(0, 19); 0022 } 0023 0024 // see https://git.eta.st/eta/rsp6-decoder/src/branch/master/spec.pdf 0025 function parseRSP6(text) { 0026 const rsp6Data = ByteArray.decodeRsp6Ticket(text); 0027 const rsp6 = ByteArray.toBitArray(rsp6Data); 0028 0029 let res = JsonLd.newTrainReservation(); 0030 res.reservationNumber = text.substr(13, 2) + readRSP6String(rsp6, 8, 9); 0031 res.reservationFor.departureStation.name = readRSP6String(rsp6, 109, 4); 0032 res.reservationFor.departureStation.identifier = 'nlc:' + readRSP6String(rsp6, 109, 4); 0033 res.reservationFor.arrivalStation.name = readRSP6String(rsp6, 133, 4); 0034 res.reservationFor.arrivalStation.identifier = 'nlc:' + readRSP6String(rsp6, 133, 4); 0035 0036 const departureTime = readRSP6DateTime(rsp6, 211); 0037 const departureTimeFlag = rsp6.readNumberMSB(236, 2); 0038 if (departureTimeFlag > 0) { 0039 res.reservationFor.departureTime = departureTime; 0040 } else { 0041 res.reservationFor.departureDay = departureTime; 0042 res.reservedTicket.validFrom = departureTime; 0043 } 0044 0045 res.underName.name = readRSP6String(rsp6, 255, 12); 0046 0047 console.log("version", rsp6.readNumberMSB(68, 4)); 0048 console.log("ticket type", readRSP6String(rsp6, 73, 3)); 0049 console.log("fare label", readRSP6String(rsp6, 91, 3)); 0050 console.log("retailer", readRSP6String(rsp6,157, 4)); 0051 console.log("coupon type", rsp6.readNumberMSB(182, 2)); 0052 console.log("discount code", rsp6.readNumberMSB(184, 10)); 0053 console.log("route code", rsp6.readNumberMSB(194, 17)); 0054 console.log("passenger ID", rsp6.readNumberMSB(238, 17)); 0055 console.log("passenger gender", rsp6.readNumberMSB(327, 2)); 0056 console.log("restriction code", readRSP6String(329, 7)); 0057 console.log("limited duration code", rsp6.readNumberMSB(379, 4)); 0058 0059 let offset = 0; 0060 if (rsp6.readNumberMSB(384, 1)) { 0061 console.log("purchase time", readRSP6DateTime(rsp6, 390)); 0062 console.log("purchase ref", readRSP6String(rsp6, 449, 8)); 0063 res.totalPrice = rsp6.readNumberMSB(415, 21) / 100.0; 0064 res.priceCurrency = 'GBP'; 0065 const daysOfValidity = rsp6.readNumberMSB(497, 9); 0066 if (departureTimeFlag == 0) { 0067 let dt = new Date(departureTime); 0068 dt.setTime(dt.getTime() + Math.max(1, daysOfValidity) * 24 * 60 * 60 * 1000); 0069 if (daysOfValidity == 0) { 0070 dt.setTime(dt.getTime() - 1000); 0071 } 0072 res.reservedTicket.validUntil = dt; 0073 } 0074 offset += 122; 0075 } 0076 0077 // TODO handle more than one reservation properly 0078 for (let i = 0; i < rsp6.readNumberMSB(386, 4); ++i) { 0079 res.reservationFor.trainNumber = readRSP6String(rsp6, 390 + offset, 2) + rsp6.readNumberMSB(402 + offset, 14); 0080 res.reservedTicket.ticketedSeat.seatSection = readRSP6String(rsp6, 416 + offset, 1); 0081 if (res.reservedTicket.ticketedSeat.seatSection == '*') { 0082 res.reservedTicket.ticketedSeat.seatSection = null; 0083 } 0084 const seatLetter = readRSP6String(rsp6, 422 + offset, 1); 0085 const seatNum = rsp6.readNumberMSB(428 + offset, 7); 0086 if ((seatLetter && seatLetter != '*') || seatNum) { 0087 res.reservedTicket.ticketedSeat.seatNumber = (seatLetter ? seatLetter : "") + (seatNum ? seatNum : ""); 0088 } 0089 offset += 45; 0090 } 0091 0092 if (rsp6.readNumberMSB(385, 1)) { 0093 console.log("free text", readRSP6String(390 + offset, 51)); 0094 } 0095 0096 res.reservedTicket.ticketToken = 'azteccode:' + text; 0097 return res; 0098 } 0099 0100 function parseTicket(pdf, node, triggerNode) { 0101 const text = pdf.pages[triggerNode.location].text; 0102 var res = triggerNode.result[0]; 0103 const header = text.match(/ +(\d{2}[ -][A-Z][a-z]{2}[ -]\d{4}) +(?:Out: |Ret: )?([A-Z]{3}) ?- ?([A-Z]{3})\n(.*) +(.*)/); 0104 const date = header[1].replace(/-/g, ' '); 0105 const itinerary = text.match(/Itinerary.*\n +(.*)\n +(\d{2}:\d{2})\n +([\S\s]*?)\n +(\d{2}:\d{2})\n +(.*)/); 0106 0107 res.reservationFor.departureStation.identifier = 'uk:' + header[2]; 0108 res.reservationFor.arrivalStation.identifier = 'uk:' + header[3]; 0109 if (itinerary) { 0110 res.reservationFor.departureStation.name = itinerary[1]; 0111 res.reservationFor.departureTime = JsonLd.toDateTime(date + ' ' + itinerary[2], 'dd MMM yyyy hh:mm', 'en'); 0112 res.reservationFor.arrivalStation.name = itinerary[5]; 0113 res.reservationFor.arrivalTime = JsonLd.toDateTime(date + ' ' + itinerary[4], 'dd MMM yyyy hh:mm', 'en'); 0114 res.reservationFor.trainName = itinerary[3].replace(/(.*?)(?: .*)?(?:\n +|$)/g, '$1 '); 0115 } else { 0116 // unbound ticket 0117 res.reservationFor.departureStation.name = header[4]; 0118 res.reservationFor.arrivalStation.name = header[5]; 0119 res.reservationFor.departureDay = JsonLd.toDateTime(date, 'dd MMM yyyy', 'en'); 0120 } 0121 0122 return res; 0123 }