diff --git a/index.js b/index.js index 4c80b975..c3a2d4fd 100644 --- a/index.js +++ b/index.js @@ -22,11 +22,15 @@ const createClient = (profile, request = _request) => { } validateProfile(profile) - const departures = (station, opt = {}) => { + const _stationBoard = (station, type, opt = {}) => { if (isObj(station)) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') + if ('string' !== typeof type || !type) { + throw new Error('type must be a non-empty string.') + } + opt = Object.assign({ direction: null, // only show departures heading to this station duration: 10 // show departures for the next n minutes @@ -49,6 +53,10 @@ const createClient = (profile, request = _request) => { getPasslist: false } }) + } + + const departures = (station, opt = {}) => { + return _stationBoard(station, 'DEP', opt) .then((d) => { if (!Array.isArray(d.jnyL)) return [] // todo: throw err? const parse = profile.parseDeparture(profile, opt, { diff --git a/lib/default-profile.js b/lib/default-profile.js index c7dbd27b..64bc34dd 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -2,6 +2,7 @@ const parseDateTime = require('../parse/date-time') const parseDeparture = require('../parse/departure') +const parseArrival = require('../parse/arrival') const parseJourneyLeg = require('../parse/journey-leg') const parseJourney = require('../parse/journey') const parseLine = require('../parse/line') @@ -39,6 +40,7 @@ const defaultProfile = { parseDateTime, parseDeparture, + parseArrival, parseJourneyLeg, parseJourney, parseLine, diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 38ed6cd8..5f8dae6b 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -11,6 +11,7 @@ const types = { parseDateTime: 'function', parseDeparture: 'function', + parseArrival: 'function', parseJourneyLeg: 'function', parseJourney: 'function', parseLine: 'function', diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js new file mode 100644 index 00000000..fef3d84f --- /dev/null +++ b/parse/arrival-or-departure.js @@ -0,0 +1,57 @@ +'use strict' + +const findRemark = require('./find-remark') + +// todo: what is d.jny.dirFlg? +// todo: d.stbStop.dProgType/d.stbStop.aProgType + +const createParseArrOrDep = (profile, opt, data, prefix) => { + const {locations, lines, hints, warnings} = data + if (prefix !== 'a' && prefix !== 'd') throw new Error('invalid prefix') + + const parseArrOrDep = (d) => { + const t = d.stbStop[prefix + 'TimeR'] || d.stbStop[prefix + 'TimeS'] + const when = profile.parseDateTime(profile, d.date, t) + + const res = { + tripId: d.jid, + station: locations[parseInt(d.stbStop.locX)] || null, + when: when.toISO(), + direction: profile.parseStationName(d.dirTxt), + line: lines[parseInt(d.prodX)] || null, + remarks: (d.remL + ? d.remL.map(ref => findRemark(hints, warnings, ref)) + : [] + ), + // todo: res.trip from rawLine.prodCtx.num? + trip: +d.jid.split('|')[1] // todo: this seems brittle + } + + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg + const tR = d.stbStop[prefix + 'TimeR'] + const tP = d.stbStop[prefix + 'TimeS'] + if (tR && tP) { + const realtime = profile.parseDateTime(profile, d.date, tR) + const planned = profile.parseDateTime(profile, d.date, tP) + res.delay = Math.round((realtime - planned) / 1000) + } else res.delay = null + + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg + if (d.stbStop[prefix + 'Cncl']) { + res.cancelled = true + Object.defineProperty(res, 'canceled', {value: true}) + res.when = res.delay = null + + const when = profile.parseDateTime(profile, d.date, tP) + res.formerScheduledWhen = when.toISO() + } + + return res + } + + return parseArrOrDep +} + +module.exports = createParseArrOrDep diff --git a/parse/arrival.js b/parse/arrival.js new file mode 100644 index 00000000..e2c2b554 --- /dev/null +++ b/parse/arrival.js @@ -0,0 +1,10 @@ +'use strict' + +const createParseArrOrDep = require('./arrival-or-departure') + +const ARRIVAL = 'a' +const createParseArrival = (profile, opt, data) => { + return createParseArrOrDep(profile, opt, data, ARRIVAL) +} + +module.exports = createParseArrival diff --git a/parse/departure.js b/parse/departure.js index 7835767a..dc86f7a5 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -1,55 +1,10 @@ 'use strict' -const findRemark = require('./find-remark') - -// todos from public-transport/hafas-client#2 -// - stdStop.dPlatfS, stdStop.dPlatfR -// todo: what is d.jny.dirFlg? -// todo: d.stbStop.dProgType -// todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 +const createParseArrOrDep = require('./arrival-or-departure') +const DEPARTURE = 'd' const createParseDeparture = (profile, opt, data) => { - const {locations, lines, hints, warnings} = data - - const parseDeparture = (d) => { - const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) - const res = { - tripId: d.jid, - station: locations[parseInt(d.stbStop.locX)] || null, - when: when.toISO(), - direction: profile.parseStationName(d.dirTxt), - line: lines[parseInt(d.prodX)] || null, - remarks: (d.remL - ? d.remL.map(ref => findRemark(hints, warnings, ref)) - : [] - ), - trip: +d.jid.split('|')[1] // todo: this seems brittle - } - // todo: res.trip from rawLine.prodCtx.num? - - // todo: DRY with parseStopover - // todo: DRY with parseJourneyLeg - if (d.stbStop.dTimeR && d.stbStop.dTimeS) { - const realtime = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR) - const planned = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS) - res.delay = Math.round((realtime - planned) / 1000) - } else res.delay = null - - // todo: DRY with parseStopover - // todo: DRY with parseJourneyLeg - if (d.stbStop.aCncl || d.stbStop.dCncl) { - res.cancelled = true - Object.defineProperty(res, 'canceled', {value: true}) - res.when = res.delay = null - - const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS) - res.formerScheduledWhen = when.toISO() - } - - return res - } - - return parseDeparture + return createParseArrOrDep(profile, opt, data, DEPARTURE) } module.exports = createParseDeparture