diff --git a/index.js b/index.js index 3d7abf43..607180b6 100644 --- a/index.js +++ b/index.js @@ -1,41 +1,59 @@ 'use strict' -const parseLocation = require('./parse/location') +const parseDateTime = require('./parse/date-time') +const parseDeparture = require('./parse/departure') +const parseJourney = require('./parse/journey') const parseLine = require('./parse/line') -const parseRemark = require('./parse/remark') +const parseLocation = require('./parse/location') +const parseMovement = require('./parse/movement') +const parseNearby = require('./parse/nearby') const parseOperator = require('./parse/operator') +const parseRemark = require('./parse/remark') +const parseStopover = require('./parse/stopover') + +const formatAddress = require('./format/address') +const formatCoord = require('./format/coord') +const formatDate = require('./format/date') +const filters = require('./format/filters') +const formatLocationFilter = require('./format/location-filter') +const formatPoi = require('./format/poi') +const formatStation = require('./format/station') +const formatTime = require('./format/time') + const request = require('./lib/request') const id = x => x +// todo: find out which are actually necessary const defaultProfile = { transformReqBody: id, transformReq: id, - parseLocation: parseLocation, - parseLine: parseLine, - parseRemark: parseRemark, - parseOperator: parseOperator + + parseDateTime, + parseDeparture, + parseJourney, + parseLine, + parseLocation, + parseMovement, + parseNearby, + parseOperator, + parseRemark, + parseStopover, + + formatAddress, + formatCoord, + formatDate, + filters, + formatLocationFilter, + formatPoi, + formatStation, + formatTime } const createClient = (profile) => { profile = Object.assign({}, defaultProfile, profile) - if ('function' !== profile.transformReqBody) { - throw new Error('profile.transformReqBody must be a function.') - } - if ('function' !== profile.transformReq) { - throw new Error('profile.transformReq must be a function.') - } - if ('function' !== profile.parseLocation) { - throw new Error('profile.parseLocation must be a function.') - } - if ('function' !== profile.parseLine) { - throw new Error('profile.parseLine must be a function.') - } - if ('function' !== profile.parseRemark) { - throw new Error('profile.parseRemark must be a function.') - } - if ('function' !== profile.parseOperator) { - throw new Error('profile.parseOperator must be a function.') + if ('string' !== typeof profile.timezone) { + throw new Error('profile.timezone must be a string.') } const client = data => request(profile, data) diff --git a/lib/request.js b/lib/request.js index 6affd575..c693c092 100644 --- a/lib/request.js +++ b/lib/request.js @@ -42,10 +42,18 @@ const request = (profile, data) => { const d = b.svcResL[0].res const c = d.common || {} - if (Array.isArray(c.locL)) d.locations = c.locL.map(profile.parseLocation) - if (Array.isArray(c.prodL)) d.lines = c.prodL.map(profile.parseLine) - if (Array.isArray(c.remL)) d.remarks = c.remL.map(profile.parseRemark) - if (Array.isArray(c.opL)) d.operators = c.opL.map(profile.parseOperator) + if (Array.isArray(c.locL)) { + d.locations = c.locL.map(loc => profile.parseLocation(profile, loc)) + } + if (Array.isArray(c.prodL)) { + d.lines = c.prodL.map(line => profile.parseLine(profile, line)) + } + if (Array.isArray(c.remL)) { + d.remarks = c.remL.map(rem => profile.parseRemark(profile, rem)) + } + if (Array.isArray(c.opL)) { + d.operators = c.opL.map(op => profile.parseOperator(profile, op)) + } return d }) } diff --git a/parse/date-time.js b/parse/date-time.js index 4c913e1c..7625d0a6 100644 --- a/parse/date-time.js +++ b/parse/date-time.js @@ -2,14 +2,14 @@ const moment = require('moment-timezone') -const parseDateTime = (timezone, date, time) => { +const parseDateTime = (profile, date, time) => { let offset = 0 // in days if (time.length > 6) { offset = +time.slice(0, -6) time = time.slice(-6) } - return moment.tz(date + 'T' + time, timezone) + return moment.tz(date + 'T' + time, profile.timezone) .add(offset, 'days') } diff --git a/parse/departure.js b/parse/departure.js index d017973a..f159a98e 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -1,18 +1,17 @@ 'use strict' -const parseDateTime = require('./date-time') - // todos from derhuerst/hafas-client#2 // - stdStop.dCncl // - stdStop.dPlatfS, stdStop.dPlatfR // todo: what is d.jny.dirFlg? // todo: d.stbStop.dProgType -const createParseDeparture = (timezone, stations, lines, remarks) => { +const createParseDeparture = (profile, stations, lines, remarks) => { + const tz = profile.timezone const findRemark = rm => remarks[parseInt(rm.remX)] || null const parseDeparture = (d) => { - const when = parseDateTime(timezone, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) + const when = profile.parseDateTime(tz, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) const res = { ref: d.jid, station: stations[parseInt(d.stbStop.locX)] || null, @@ -24,8 +23,8 @@ const createParseDeparture = (timezone, stations, lines, remarks) => { } if (d.stbStop.dTimeR && d.stbStop.dTimeS) { - const realtime = parseDateTime(timezone, d.date, d.stbStop.dTimeR) - const planned = parseDateTime(timezone, d.date, d.stbStop.dTimeS) + const realtime = profile.parseDateTime(tz, d.date, d.stbStop.dTimeR) + const planned = profile.parseDateTime(tz, d.date, d.stbStop.dTimeS) res.delay = Math.round((realtime - planned) / 1000) } else res.delay = null diff --git a/parse/journey.js b/parse/journey.js index 343a4147..cb8831e8 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -4,8 +4,10 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) -const createParseStopover = (tz, stations, lines, remarks, j) => { // j = journey - const parseStopover = (st) => { +const createParseJourney = (profile, stations, lines, remarks) => { + const tz = profile.timezone + + const parseStopover = (j, st) => { const res = { station: stations[parseInt(st.locX)] } @@ -17,26 +19,20 @@ const createParseStopover = (tz, stations, lines, remarks, j) => { // j = journe const dep = parseDateTime(tz, j.date, st.dTimeR || st.dTimeS) res.departure = dep.format() } + return res } - return parseStopover -} - -const createApplyRemark = (stations, lines, remarks, j) => { // j = journey // todo: finish parse/remark.js first - const applyRemark = (rm) => {} - return applyRemark -} + const applyRemark = (j, rm) => {} -const createParsePart = (tz, stations, lines, remarks, j) => { // j = journey // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? // todo: how does pt.freq work? - const parsePart = (pt) => { - const dep = parseDateTime(tz, j.date, pt.dep.dTimeR || pt.dep.dTimeS) - const arr = parseDateTime(tz, j.date, pt.arr.aTimeR || pt.arr.aTimeS) + const parsePart = (j, pt) => { // j = journey, pt = part + const dep = profile.parseDateTime(tz, j.date, pt.dep.dTimeR || pt.dep.dTimeS) + const arr = profile.parseDateTime(tz, j.date, pt.arr.aTimeR || pt.arr.aTimeS) const res = { origin: clone(stations[parseInt(pt.dep.locX)]) || null, destination: clone(stations[parseInt(pt.arr.locX)]), @@ -45,8 +41,8 @@ const createParsePart = (tz, stations, lines, remarks, j) => { // j = journey } if (pt.dep.dTimeR && pt.dep.dTimeS) { - const realtime = parseDateTime(tz, j.date, pt.dep.dTimeR) - const planned = parseDateTime(tz, j.date, pt.dep.dTimeS) + const realtime = profile.parseDateTime(tz, j.date, pt.dep.dTimeR) + const planned = profile.parseDateTime(tz, j.date, pt.dep.dTimeS) res.delay = Math.round((realtime - planned) / 1000) } @@ -61,18 +57,21 @@ const createParsePart = (tz, stations, lines, remarks, j) => { // j = journey if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (pt.jny.stopL) { - const parseStopover = createParseStopover(tz, stations, lines, remarks, j) - res.passed = pt.jny.stopL.map(parseStopover) + res.passed = pt.jny.stopL.map(stopover => parseStopover(j, stopover)) } if (Array.isArray(pt.jny.remL)) { - pt.jny.remL.forEach(createApplyRemark(stations, lines, remarks, j)) + for (let remark of pt.jny.remL) applyRemark(j, remark) } if (pt.jny.freq && pt.jny.freq.jnyL) { - const parseAlternative = (a) => ({ - line: lines[parseInt(a.prodX)] || null, - when: parseDateTime(tz, j.date, a.stopL[0].dTimeS).format() // todo: realtime - }) + const parseAlternative = (a) => { + // todo: realtime + const when = profile.parseDateTime(tz, j.date, a.stopL[0].dTimeS) + return { + line: lines[parseInt(a.prodX)] || null, + when: when.format() + } + } res.alternatives = pt.jny.freq.jnyL .filter(a => a.stopL[0].locX === pt.dep.locX) .map(parseAlternative) @@ -81,17 +80,14 @@ const createParsePart = (tz, stations, lines, remarks, j) => { // j = journey return res } - return parsePart -} -const createParseJourney = (tz, stations, lines, remarks, p = createParsePart) => { // todo: c.sDays // todo: c.dep.dProgType, c.arr.dProgType // todo: c.conSubscr // todo: c.trfRes x vbb-parse-ticket // todo: use computed information from part - const parseJourney = (journey) => { - const parts = journey.secL.map(p(tz, stations, lines, remarks, journey)) + const parseJourney = (j) => { + const parts = j.secL.map(part => parsePart(j, part)) return { parts, origin: parts[0].origin, diff --git a/parse/line.js b/parse/line.js index 7094acd6..b9eb5e52 100644 --- a/parse/line.js +++ b/parse/line.js @@ -3,7 +3,8 @@ // todo: what is p.number vs p.line? // todo: what is p.icoX? // todo: what is p.oprX? -const parseLine = (p) => { +// todo: is passing in profile necessary? +const parseLine = (profile, p) => { if (!p) return null // todo: handle this upstream const res = {type: 'line', name: p.line || p.name} diff --git a/parse/location.js b/parse/location.js index 405498b8..70d59f81 100644 --- a/parse/location.js +++ b/parse/location.js @@ -6,9 +6,10 @@ types.S = 'station' types.A = 'address' // todo: what is s.rRefL? -const parseLocation = (l) => { +// todo: is passing in profile necessary? +const parseLocation = (profile, l) => { const type = types[l.type] || 'unknown' - const result = { + const res = { type, name: l.name, coordinates: l.crd ? { @@ -17,10 +18,10 @@ const parseLocation = (l) => { } : null } - if (type === 'poi' || type === 'station') result.id = l.extId - if ('pCls' in l) result.products = l.pCls + if (type === 'poi' || type === 'station') res.id = l.extId + if ('pCls' in l) res.products = l.pCls - return result + return res } module.exports = parseLocation diff --git a/parse/movement.js b/parse/movement.js index 8c44038e..e9662291 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,8 +1,8 @@ 'use strict' -const parseDateTime = require('./date-time') +const createParseMovement = (profile, locations, lines, remarks) => { + const tz = profile.timezone -const createParseMovement = (tz, locations, lines, remarks) => { // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? @@ -13,10 +13,10 @@ const createParseMovement = (tz, locations, lines, remarks) => { const parseMovement = (m) => { const parseNextStop = (s) => { const dep = s.dTimeR || s.dTimeS - ? parseDateTime(tz, m.date, s.dTimeR || s.dTimeS) + ? profile.parseDateTime(tz, m.date, s.dTimeR || s.dTimeS) : null const arr = s.aTimeR || s.aTimeS - ? parseDateTime(tz, m.date, s.aTimeR || s.aTimeS) + ? profile.parseDateTime(tz, m.date, s.aTimeR || s.aTimeS) : null return { diff --git a/parse/nearby.js b/parse/nearby.js index 2d96a5d1..e009b405 100644 --- a/parse/nearby.js +++ b/parse/nearby.js @@ -1,14 +1,12 @@ 'use strict' -const parseLocation = require('./location') - // todo: remarks // todo: lines // todo: what is s.pCls? // todo: what is s.wt? // todo: what is s.dur? -const parseNearby = (n) => { - const res = parseLocation(n) +const parseNearby = (profile, n) => { + const res = profile.parseLocation(profile, n) res.distance = n.dist return res } diff --git a/parse/operator.js b/parse/operator.js index a77f217b..b3682199 100644 --- a/parse/operator.js +++ b/parse/operator.js @@ -2,7 +2,8 @@ const slugg = require('slugg') -const parseOperator = (a) => { +// todo: is passing in profile necessary? +const parseOperator = (profile, a) => { return { type: 'operator', id: slugg(a.name), // todo: find a more reliable way diff --git a/parse/remark.js b/parse/remark.js index d08b1500..0b8c7a52 100644 --- a/parse/remark.js +++ b/parse/remark.js @@ -1,6 +1,7 @@ 'use strict' -const parseRemark = (r) => { +// todo: is passing in profile necessary? +const parseRemark = (profile, r) => { return null // todo } diff --git a/parse/stopover.js b/parse/stopover.js index ab49c27f..09b6a80b 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -1,18 +1,18 @@ 'use strict' -const parseDateTime = require('./date-time') +const createParseStopover = (profile, stations, lines, remarks, connection) => { + const tz = profile.timezone -const createParseStopover = (tz, stations, lines, remarks, connection) => { const parseStopover = (st) => { const res = { station: stations[parseInt(st.locX)] || null } if (st.aTimeR || st.aTimeS) { - const arr = parseDateTime(tz, connection.date, st.aTimeR || st.aTimeS) + const arr = profile.parseDateTime(tz, connection.date, st.aTimeR || st.aTimeS) res.arrival = arr.format() } if (st.dTimeR || st.dTimeS) { - const dep = parseDateTime(tz, connection.date, st.dTimeR || st.dTimeS) + const dep = profile.parseDateTime(tz, connection.date, st.dTimeR || st.dTimeS) res.departure = dep.format() } return res