Reintroducing WebWorkers (pfeeew, Vite makes it tough !)
This commit is contained in:
parent
02f02984e5
commit
5de30283b1
File diff suppressed because it is too large
Load Diff
|
@ -27,10 +27,16 @@
|
||||||
"@capacitor/android": "^3.4.3",
|
"@capacitor/android": "^3.4.3",
|
||||||
"@capacitor/core": "^3.4.3",
|
"@capacitor/core": "^3.4.3",
|
||||||
"@capacitor/ios": "^3.4.3",
|
"@capacitor/ios": "^3.4.3",
|
||||||
|
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
|
||||||
|
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||||
"@solidjs/router": "^0.5.1",
|
"@solidjs/router": "^0.5.1",
|
||||||
"@suid/icons-material": "^0.5.1",
|
"@suid/icons-material": "^0.5.1",
|
||||||
"@suid/material": "^0.8.0",
|
"@suid/material": "^0.8.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"ol": "^7.1.0",
|
"ol": "^7.1.0",
|
||||||
|
"pouchdb": "^7.3.1",
|
||||||
|
"pouchdb-browser": "^7.3.1",
|
||||||
|
"pouchdb-find": "^7.3.1",
|
||||||
"solid-js": "^1.3.12"
|
"solid-js": "^1.3.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { initDb } from '.';
|
||||||
|
import { existsGpx, putNewGpx } from './gpx';
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The gpx module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new Gpx when required', async () => {
|
||||||
|
await putNewGpx({ gpx: 0 });
|
||||||
|
expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/0000000000000000',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
$: {
|
||||||
|
creator: 'dyomedea version 0.000002',
|
||||||
|
version: '1.1',
|
||||||
|
xmlns: 'http://www.topografix.com/GPX/1/1',
|
||||||
|
'xmlns:dyo': 'http://xmlns.dyomedea.com/',
|
||||||
|
'xmlns:gpxtpx':
|
||||||
|
'http://www.garmin.com/xmlschemas/TrackPointExtension/v1',
|
||||||
|
'xmlns:gpxx': 'http://www.garmin.com/xmlschemas/GpxExtensions/v3',
|
||||||
|
'xmlns:wptx1':
|
||||||
|
'http://www.garmin.com/xmlschemas/WaypointExtension/v1',
|
||||||
|
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'xsi:schemaLocation':
|
||||||
|
'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd',
|
||||||
|
},
|
||||||
|
extensions: undefined,
|
||||||
|
metadata: {
|
||||||
|
author: undefined,
|
||||||
|
bounds: undefined,
|
||||||
|
copyright: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
keywords: undefined,
|
||||||
|
link: undefined,
|
||||||
|
name: undefined,
|
||||||
|
time: '1970-01-01T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
rte: undefined,
|
||||||
|
trk: undefined,
|
||||||
|
wpt: undefined,
|
||||||
|
},
|
||||||
|
type: 'gpx',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id if needed', async () => {
|
||||||
|
const id = await putNewGpx();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('The gpx module with a real db', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await initDb({});
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
db = undefined;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
test("existsGpx returns false if the GPX doesn't exist", async () => {
|
||||||
|
const exists = await existsGpx({ gpx: 1 });
|
||||||
|
expect(exists).toBeFalsy();
|
||||||
|
});
|
||||||
|
test('existsGpx returns false if the GPX exists', async () => {
|
||||||
|
const id = { gpx: 1 };
|
||||||
|
await putNewGpx(id);
|
||||||
|
const exists = await existsGpx(id);
|
||||||
|
expect(exists).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,269 @@
|
||||||
|
import { PureComponent } from 'react';
|
||||||
|
import { Point, Rectangle } from '../components/map/types';
|
||||||
|
import { lat2tile, lon2tile, rectanglesIntersect } from '../lib/geo';
|
||||||
|
import getUri, { intToGpxId } from '../lib/ids';
|
||||||
|
import { get, getDocsByType, getFamily, put, putAll } from './lib';
|
||||||
|
|
||||||
|
const emptyGpx: Gpx = {
|
||||||
|
$: {
|
||||||
|
version: '1.1',
|
||||||
|
creator: 'dyomedea version 0.000002',
|
||||||
|
xmlns: 'http://www.topografix.com/GPX/1/1',
|
||||||
|
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'xsi:schemaLocation':
|
||||||
|
'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd',
|
||||||
|
'xmlns:gpxx': 'http://www.garmin.com/xmlschemas/GpxExtensions/v3',
|
||||||
|
'xmlns:wptx1': 'http://www.garmin.com/xmlschemas/WaypointExtension/v1',
|
||||||
|
'xmlns:gpxtpx': 'http://www.garmin.com/xmlschemas/TrackPointExtension/v1',
|
||||||
|
'xmlns:dyo': 'http://xmlns.dyomedea.com/',
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
name: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
author: undefined,
|
||||||
|
copyright: undefined,
|
||||||
|
link: undefined,
|
||||||
|
time: undefined,
|
||||||
|
keywords: undefined,
|
||||||
|
bounds: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
},
|
||||||
|
wpt: undefined,
|
||||||
|
rte: undefined,
|
||||||
|
trk: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewGpx = async (
|
||||||
|
id: IdGpx = { gpx: intToGpxId(Date.now()) }
|
||||||
|
) => {
|
||||||
|
const uri = getUri('gpx', id);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'gpx',
|
||||||
|
(gpx) => {
|
||||||
|
(gpx.metadata ??= {}).time = new Date(Date.now()).toISOString();
|
||||||
|
return gpx;
|
||||||
|
},
|
||||||
|
emptyGpx
|
||||||
|
);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const existsGpx = async (id: IdGpx) => {
|
||||||
|
const uri = getUri('gpx', id);
|
||||||
|
try {
|
||||||
|
await get(uri);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prune = (id: any, object: any, docs: any[]) => {
|
||||||
|
if (typeof object === 'object') {
|
||||||
|
for (const key in object) {
|
||||||
|
if (
|
||||||
|
key === 'wpt' ||
|
||||||
|
key === 'rte' ||
|
||||||
|
key === 'rtept' ||
|
||||||
|
key === 'trk' ||
|
||||||
|
key === 'trkseg' ||
|
||||||
|
key === 'trkpt'
|
||||||
|
) {
|
||||||
|
const subObjects = object[key];
|
||||||
|
for (const index in subObjects) {
|
||||||
|
const subId = { ...id };
|
||||||
|
subId[key] = index;
|
||||||
|
docs.push({
|
||||||
|
_id: getUri(key, subId),
|
||||||
|
type: key,
|
||||||
|
doc: subObjects[index],
|
||||||
|
});
|
||||||
|
prune(subId, subObjects[index], docs);
|
||||||
|
}
|
||||||
|
object[key] = undefined;
|
||||||
|
} else prune(id, object[key], docs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const extensionsFromObject = (
|
||||||
|
object: any,
|
||||||
|
extensions = {
|
||||||
|
viewport: { topLeft: <Point>{}, bottomRight: <Point>{} },
|
||||||
|
bbox: {
|
||||||
|
minLon: <number | undefined>undefined,
|
||||||
|
minLat: <number | undefined>undefined,
|
||||||
|
maxLon: <number | undefined>undefined,
|
||||||
|
maxLat: <number | undefined>undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
if (typeof object === 'object') {
|
||||||
|
if ('$' in object) {
|
||||||
|
const attributes = object.$;
|
||||||
|
if ('lat' in attributes) {
|
||||||
|
const lat = +attributes.lat;
|
||||||
|
if (
|
||||||
|
extensions.bbox.minLat === undefined ||
|
||||||
|
lat < extensions.bbox.minLat
|
||||||
|
) {
|
||||||
|
extensions.bbox.minLat = lat;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
extensions.bbox.maxLat === undefined ||
|
||||||
|
lat > extensions.bbox.maxLat
|
||||||
|
) {
|
||||||
|
extensions.bbox.maxLat = lat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ('lon' in attributes) {
|
||||||
|
const lon = +attributes.lon;
|
||||||
|
if (
|
||||||
|
extensions.bbox.minLon === undefined ||
|
||||||
|
lon < extensions.bbox.minLon
|
||||||
|
) {
|
||||||
|
extensions.bbox.minLon = lon;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
extensions.bbox.maxLon === undefined ||
|
||||||
|
lon > extensions.bbox.maxLon
|
||||||
|
) {
|
||||||
|
extensions.bbox.maxLon = lon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key in object) {
|
||||||
|
extensionsFromObject(object[key], extensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extensionsFromGpx = (gpx: Gpx) => {
|
||||||
|
const extensions = { ...gpx.extensions, ...extensionsFromObject(gpx) };
|
||||||
|
gpx.extensions = undefined;
|
||||||
|
if (
|
||||||
|
extensions.bbox.maxLat !== undefined &&
|
||||||
|
extensions.bbox.minLon !== undefined
|
||||||
|
) {
|
||||||
|
extensions.viewport.topLeft = {
|
||||||
|
x: lon2tile(extensions.bbox.minLon, 0),
|
||||||
|
y: lat2tile(extensions.bbox.maxLat, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
extensions.bbox.minLat !== undefined &&
|
||||||
|
extensions.bbox.maxLon !== undefined
|
||||||
|
) {
|
||||||
|
extensions.viewport.bottomRight = {
|
||||||
|
x: lon2tile(extensions.bbox.maxLon, 0),
|
||||||
|
y: lat2tile(extensions.bbox.minLat, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return extensions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pruneAndSaveImportedGpx = async (params: any) => {
|
||||||
|
const { id, gpx, extensions } = params;
|
||||||
|
let docs: any[] = [
|
||||||
|
{ _id: getUri('gpx', id), type: 'gpx', doc: gpx },
|
||||||
|
{
|
||||||
|
_id: getUri('extensions', id),
|
||||||
|
type: 'extensions',
|
||||||
|
doc: { ...extensions, ...extensionsFromGpx(gpx) },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
prune(id, gpx, docs);
|
||||||
|
console.log(JSON.stringify(docs));
|
||||||
|
try {
|
||||||
|
const result = await putAll(docs);
|
||||||
|
console.log(JSON.stringify(result));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`error: ${err}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGpxesForViewport = async (params: any) => {
|
||||||
|
const { viewport, zoomLevel } = params;
|
||||||
|
const zoomedViewport: Rectangle = {
|
||||||
|
topLeft: {
|
||||||
|
x: viewport.topLeft.x / 2 ** zoomLevel,
|
||||||
|
y: viewport.topLeft.y / 2 ** zoomLevel,
|
||||||
|
},
|
||||||
|
bottomRight: {
|
||||||
|
x: (viewport.bottomRight.x + 1) / 2 ** zoomLevel,
|
||||||
|
y: (viewport.bottomRight.y + 1) / 2 ** zoomLevel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const allExtensions = await getDocsByType('extensions');
|
||||||
|
console.log(
|
||||||
|
`getGpxesForViewport, allExtensions: ${JSON.stringify(allExtensions)}`
|
||||||
|
);
|
||||||
|
return allExtensions
|
||||||
|
.filter((extensions: any) => {
|
||||||
|
return rectanglesIntersect(zoomedViewport, extensions.doc.viewport);
|
||||||
|
})
|
||||||
|
.map((extensions: any) =>
|
||||||
|
getUri('gpx', getUri('extensions', extensions._id))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendToArray = (target: any, key: string, value: any) => {
|
||||||
|
if (!(key in target)) {
|
||||||
|
target[key] = <any>[];
|
||||||
|
}
|
||||||
|
target[key].push(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFullGpx = async (params: any) => {
|
||||||
|
const { id } = params;
|
||||||
|
const docs = await getFamily(id, { include_docs: true });
|
||||||
|
let target: any[];
|
||||||
|
let gpx: Gpx | undefined = undefined;
|
||||||
|
docs.rows.forEach((row: any) => {
|
||||||
|
// level 0
|
||||||
|
if (row.doc.type === 'gpx') {
|
||||||
|
target = [row.doc.doc];
|
||||||
|
gpx = row.doc.doc;
|
||||||
|
}
|
||||||
|
//level 1
|
||||||
|
if (
|
||||||
|
row.doc.type === 'wpt' ||
|
||||||
|
row.doc.type === 'rte' ||
|
||||||
|
row.doc.type === 'trk' ||
|
||||||
|
row.doc.type === 'extensions'
|
||||||
|
) {
|
||||||
|
target.splice(1);
|
||||||
|
appendToArray(target.at(-1), row.doc.type, row.doc.doc);
|
||||||
|
target.push(row.doc.doc);
|
||||||
|
}
|
||||||
|
// level 2
|
||||||
|
if (row.doc.type === 'rtept' || row.doc.type === 'trkseg') {
|
||||||
|
target.splice(2);
|
||||||
|
appendToArray(target.at(-1), row.doc.type, row.doc.doc);
|
||||||
|
target.push(row.doc.doc);
|
||||||
|
}
|
||||||
|
// level 3
|
||||||
|
if (row.doc.type === 'trkpt') {
|
||||||
|
appendToArray(target.at(-1), row.doc.type, row.doc.doc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return gpx;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGpx = async (params: any) => {
|
||||||
|
const { id } = params;
|
||||||
|
const docs = await getFamily(id, { include_docs: true });
|
||||||
|
console.log(`getGpx, uri: ${id} docs: ${JSON.stringify(docs)}`);
|
||||||
|
return docs.rows.filter(
|
||||||
|
(row: any) =>
|
||||||
|
row.doc.type === 'gpx' ||
|
||||||
|
row.doc.type === 'wpt' ||
|
||||||
|
row.doc.type === 'rte' ||
|
||||||
|
row.doc.type === 'trk' ||
|
||||||
|
row.doc.type === 'extensions'
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,117 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import PouchDB from 'pouchdb';
|
||||||
|
import PouchDBFind from 'pouchdb-find';
|
||||||
|
import uri from '../lib/ids';
|
||||||
|
|
||||||
|
PouchDB.plugin(PouchDBFind);
|
||||||
|
|
||||||
|
const dbDefinitionId = uri('dbdef', {});
|
||||||
|
|
||||||
|
const currentDbDefinition = {
|
||||||
|
_id: dbDefinitionId,
|
||||||
|
type: dbDefinitionId,
|
||||||
|
def: { version: '0.000001' },
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initDb = async (params: any) => {
|
||||||
|
if (globalThis.db === undefined) {
|
||||||
|
globalThis.db = new PouchDB('dyomedea', {
|
||||||
|
auto_compaction: true,
|
||||||
|
revs_limit: 10,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const db = globalThis.db;
|
||||||
|
var previousDbDefinition = {
|
||||||
|
_id: dbDefinitionId,
|
||||||
|
type: dbDefinitionId,
|
||||||
|
def: { version: '0' },
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
previousDbDefinition = await db.get(dbDefinitionId);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.status !== 404) {
|
||||||
|
console.log(
|
||||||
|
`Unexpected error fetching db definition: ${JSON.stringify(error)}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousDbDefinition.def.version < currentDbDefinition.def.version) {
|
||||||
|
previousDbDefinition.def = currentDbDefinition.def;
|
||||||
|
db.put(previousDbDefinition);
|
||||||
|
// TODO: support migrations
|
||||||
|
}
|
||||||
|
await await db.compact();
|
||||||
|
await db.viewCleanup();
|
||||||
|
|
||||||
|
// WARNING: defs must use the canonical form and be identical to what will be returned by db.getIndexes
|
||||||
|
const requiredIndexes: any = [
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
def: {
|
||||||
|
fields: [{ type: 'asc' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const existingIndexes = (await db.getIndexes()).indexes;
|
||||||
|
|
||||||
|
const pruneIndex = ({ name, def }: any) => ({ name, def });
|
||||||
|
|
||||||
|
const isSameIndex = (idx1: any, idx2: any) => {
|
||||||
|
return _.isEqual(pruneIndex(idx1), pruneIndex(idx2));
|
||||||
|
};
|
||||||
|
|
||||||
|
const findIndex = (targetIndexes: any, index: any) =>
|
||||||
|
targetIndexes.find((targetIndex: any) => {
|
||||||
|
return isSameIndex(targetIndex, index);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var index of existingIndexes) {
|
||||||
|
if (index.type === 'json') {
|
||||||
|
// Non system indexes
|
||||||
|
// console.log(`Checking existing index :${JSON.stringify(index)}`);
|
||||||
|
if (!findIndex(requiredIndexes, index)) {
|
||||||
|
// console.log(`db.deleteIndex(${JSON.stringify(index)})`);
|
||||||
|
await db.deleteIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index of requiredIndexes) {
|
||||||
|
if (!findIndex(existingIndexes, index)) {
|
||||||
|
// console.log(`db.createIndex(${JSON.stringify(index)})`);
|
||||||
|
await db.createIndex({ name: index.name, ...index.def });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.dbReady = true;
|
||||||
|
|
||||||
|
/* const indexes = await db.getIndexes();
|
||||||
|
console.log(`indexes: ${JSON.stringify(indexes)}`);
|
||||||
|
|
||||||
|
const explain1 = await db.explain({
|
||||||
|
selector: {
|
||||||
|
type: 'trkpt',
|
||||||
|
gpx: 'xxxx',
|
||||||
|
},
|
||||||
|
// sort: ['trkpt.time'],
|
||||||
|
// use_index: 'type-trkpt-gpx-time',
|
||||||
|
});
|
||||||
|
console.log(`explain1: ${JSON.stringify(explain1)}`);
|
||||||
|
const explain2 = await db.explain({
|
||||||
|
selector: {
|
||||||
|
type: 'gpx',
|
||||||
|
},
|
||||||
|
// sort: ['trkpt.time'],
|
||||||
|
// use_index: 'type-trkpt-gpx-time',
|
||||||
|
});
|
||||||
|
console.log(`explain2: ${JSON.stringify(explain2)}`);
|
||||||
|
*/
|
||||||
|
};
|
|
@ -0,0 +1,299 @@
|
||||||
|
import { initDb } from '.';
|
||||||
|
import uri from '../lib/ids';
|
||||||
|
import { getDocsByType, getFamily } from './lib';
|
||||||
|
import { putNewRte } from './rte';
|
||||||
|
import { putNewRtept } from './rtept';
|
||||||
|
import { putNewTrk } from './trk';
|
||||||
|
import { putNewTrkpt } from './trkpt';
|
||||||
|
import { putNewTrkseg } from './trkseg';
|
||||||
|
import { putNewWpt } from './wpt';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('getFamily', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await initDb({});
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
db = undefined;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('returns two rows after a gpx and a track have been inserted.', async () => {
|
||||||
|
await putNewTrk();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-4c114f3ae0073151e4082ff1d220c2a4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 4,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('also returns the docs if required.', async () => {
|
||||||
|
await putNewTrk();
|
||||||
|
const allDocs: any = await getFamily('gpx/', {
|
||||||
|
include_docs: true,
|
||||||
|
});
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"doc": Object {
|
||||||
|
"_id": "gpx/4320000000000000",
|
||||||
|
"_rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
"doc": Object {
|
||||||
|
"$": Object {
|
||||||
|
"creator": "dyomedea version 0.000002",
|
||||||
|
"version": "1.1",
|
||||||
|
"xmlns": "http://www.topografix.com/GPX/1/1",
|
||||||
|
"xmlns:dyo": "http://xmlns.dyomedea.com/",
|
||||||
|
"xmlns:gpxtpx": "http://www.garmin.com/xmlschemas/TrackPointExtension/v1",
|
||||||
|
"xmlns:gpxx": "http://www.garmin.com/xmlschemas/GpxExtensions/v3",
|
||||||
|
"xmlns:wptx1": "http://www.garmin.com/xmlschemas/WaypointExtension/v1",
|
||||||
|
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
"xsi:schemaLocation": "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd",
|
||||||
|
},
|
||||||
|
"metadata": Object {
|
||||||
|
"time": "1970-01-01T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "gpx",
|
||||||
|
},
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"doc": Object {
|
||||||
|
"_id": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"_rev": "1-4c114f3ae0073151e4082ff1d220c2a4",
|
||||||
|
"doc": Object {
|
||||||
|
"number": 0,
|
||||||
|
},
|
||||||
|
"type": "trk",
|
||||||
|
},
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-4c114f3ae0073151e4082ff1d220c2a4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 4,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('returns three rows after a gpx and a track segment have been inserted.', async () => {
|
||||||
|
await putNewTrkseg();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-4c114f3ae0073151e4082ff1d220c2a4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-68d7de0569de570229ea9f9e1a0b13cb",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 5,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('returns four rows after a gpx and a track point have been inserted.', async () => {
|
||||||
|
await putNewTrkpt();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-4c114f3ae0073151e4082ff1d220c2a4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-68d7de0569de570229ea9f9e1a0b13cb",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/3trk/000000/000000/000000",
|
||||||
|
"key": "gpx/4320000000000000/3trk/000000/000000/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-7d917d1f3505fe0e3092161694904b53",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 6,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns two rows after a gpx and a waypoint have been inserted.', async () => {
|
||||||
|
await putNewWpt();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/1wpt/000000",
|
||||||
|
"key": "gpx/4320000000000000/1wpt/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-c6793365fd0dd56236ab8734a41c7ae7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 4,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns two rows after a gpx and a route have been inserted.', async () => {
|
||||||
|
await putNewRte();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"key": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-2ca14f512a9c83f5a239389e580befce",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 4,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns three rows after a gpx and a route point have been inserted.', async () => {
|
||||||
|
await putNewRtept();
|
||||||
|
const allDocs: any = await getFamily('gpx/');
|
||||||
|
expect(allDocs).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"offset": 0,
|
||||||
|
"rows": Array [
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000",
|
||||||
|
"key": "gpx/4320000000000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-49baa096ec0c89962f2cafd3ff50b80b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"key": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-2ca14f512a9c83f5a239389e580befce",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "gpx/4320000000000000/2rte/000000/000000",
|
||||||
|
"key": "gpx/4320000000000000/2rte/000000/000000",
|
||||||
|
"value": Object {
|
||||||
|
"rev": "1-0f4064d20f6bfac3888a7758851fbac5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total_rows": 5,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDocsByType', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await initDb({});
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
db = undefined;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('gets the rte amongst other docs', async () => {
|
||||||
|
await putNewRtept();
|
||||||
|
const rtes = await getDocsByType('rte');
|
||||||
|
expect(rtes).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"_id": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"_rev": "1-2ca14f512a9c83f5a239389e580befce",
|
||||||
|
"doc": Object {
|
||||||
|
"number": 0,
|
||||||
|
},
|
||||||
|
"type": "rte",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const put = async (
|
||||||
|
_id: string,
|
||||||
|
type: string,
|
||||||
|
update: (doc: any) => any,
|
||||||
|
defaultDoc: any
|
||||||
|
) => {
|
||||||
|
var current;
|
||||||
|
try {
|
||||||
|
current = await db.get(_id);
|
||||||
|
} catch {
|
||||||
|
current = { _rev: undefined, doc: cloneDeep(defaultDoc) };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
db.put({ _id, _rev: current._rev, type, doc: update(current.doc) });
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.name === 'conflict') {
|
||||||
|
await put(_id, type, update, defaultDoc);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`put(${_id}, ${JSON.stringify(
|
||||||
|
update(current.doc)
|
||||||
|
)}), error: ${JSON.stringify(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFamily = async (key: string, options: any = {}) => {
|
||||||
|
return await db.allDocs({
|
||||||
|
startkey: key,
|
||||||
|
endkey: key + '\ufff0',
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get = async (id: string) => {
|
||||||
|
await db.get(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putAll = async (docs: any[]) => {
|
||||||
|
return await db.bulkDocs(docs);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDocsByType = async (type: string) => {
|
||||||
|
return (await db.find({ selector: { type: type } })).docs;
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { putNewRte } from './rte';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The rte module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new rte when required', async () => {
|
||||||
|
putNewRte({ gpx: 4320000000000000, rte: 25 });
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/4320000000000000/2rte/000025',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
link: undefined,
|
||||||
|
name: undefined,
|
||||||
|
number: 0,
|
||||||
|
rtept: undefined,
|
||||||
|
src: undefined,
|
||||||
|
type: undefined,
|
||||||
|
},
|
||||||
|
type: 'rte',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the trk if needed', async () => {
|
||||||
|
const id = await putNewRte({ gpx: 0 });
|
||||||
|
expect(id).toEqual({ gpx: 0, rte: 0 });
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and trk if needed', async () => {
|
||||||
|
const id = await putNewRte();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000, rte: 0 });
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { putNewGpx } from './gpx';
|
||||||
|
import { put } from './lib';
|
||||||
|
|
||||||
|
export const emptyRte: Rte = {
|
||||||
|
name: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
src: undefined,
|
||||||
|
link: undefined,
|
||||||
|
number: 0,
|
||||||
|
type: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
rtept: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewRte = async (id?: IdRte | IdGpx) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('rte' in finalId)) {
|
||||||
|
const gpxId = await putNewGpx(id);
|
||||||
|
finalId = { ...gpxId, rte: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('rte', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'rte',
|
||||||
|
(rte) => {
|
||||||
|
return rte;
|
||||||
|
},
|
||||||
|
emptyRte
|
||||||
|
);
|
||||||
|
return finalId as IdRte;
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { putNewRtept } from './rtept';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The rtept module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new rtept when required', async () => {
|
||||||
|
putNewRtept({ gpx: 0, rte: 0, rtept: 0 });
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/0000000000000000/2rte/000000/000000',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
ele: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
fix: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
link: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
name: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
src: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
time: undefined,
|
||||||
|
type: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
},
|
||||||
|
type: 'rtept',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the rtept if needed', async () => {
|
||||||
|
const id = await putNewRtept({ gpx: 0 });
|
||||||
|
expect(id).toEqual({ gpx: 0, rte: 0, rtept: 0 });
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and rte if needed', async () => {
|
||||||
|
const id = await putNewRtept();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000, rte: 0, rtept: 0 });
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,44 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { put } from './lib';
|
||||||
|
import { putNewRte } from './rte';
|
||||||
|
|
||||||
|
export const emptyRtept: Wpt = {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ele: undefined,
|
||||||
|
time: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
name: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
src: undefined,
|
||||||
|
link: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
type: undefined,
|
||||||
|
fix: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewRtept = async (id?: IdGpx | IdRte | IdRtept) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('rtept' in finalId)) {
|
||||||
|
const rteId = await putNewRte(id);
|
||||||
|
finalId = { ...rteId, rtept: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('rtept', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'rtept',
|
||||||
|
(rtept) => {
|
||||||
|
return rtept;
|
||||||
|
},
|
||||||
|
emptyRtept
|
||||||
|
);
|
||||||
|
return finalId as IdRtept;
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { putNewTrk } from './trk';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The trk module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new trk when required', async () => {
|
||||||
|
putNewTrk({ gpx: 1, trk: 2 });
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/0000000000000001/3trk/000002',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
link: undefined,
|
||||||
|
name: undefined,
|
||||||
|
number: 0,
|
||||||
|
src: undefined,
|
||||||
|
trkseg: undefined,
|
||||||
|
type: undefined,
|
||||||
|
},
|
||||||
|
type: 'trk',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the trk if needed', async () => {
|
||||||
|
const id = await putNewTrk({ gpx: 2 });
|
||||||
|
expect(id).toEqual({ gpx: 2, trk: 0});
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and trk if needed', async () => {
|
||||||
|
const id = await putNewTrk();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000, trk: 0});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { putNewGpx } from './gpx';
|
||||||
|
import { getFamily, put } from './lib';
|
||||||
|
|
||||||
|
export const emptyTrk: Trk = {
|
||||||
|
name: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
src: undefined,
|
||||||
|
link: undefined,
|
||||||
|
number: 0,
|
||||||
|
type: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
trkseg: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewTrk = async (id?: IdTrk | IdGpx) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('trk' in finalId)) {
|
||||||
|
const gpxId = await putNewGpx(id);
|
||||||
|
finalId = { ...gpxId, trk: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('trk', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'trk',
|
||||||
|
(trk) => {
|
||||||
|
return trk;
|
||||||
|
},
|
||||||
|
emptyTrk
|
||||||
|
);
|
||||||
|
return finalId as IdTrk;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTrk = async (params: any) => {
|
||||||
|
const { id } = params;
|
||||||
|
const docs = await getFamily(id, { include_docs: true });
|
||||||
|
console.log(`getTrk, uri: ${id} docs: ${JSON.stringify(docs)}`);
|
||||||
|
return docs.rows.filter(
|
||||||
|
(row: any) => row.doc.type === 'trk' || row.doc.type === 'trkseg'
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { putNewTrkpt } from './trkpt';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The trkpt module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new trkpt when required', async () => {
|
||||||
|
putNewTrkpt({
|
||||||
|
gpx: 1,
|
||||||
|
trk: 2,
|
||||||
|
trkseg: 3,
|
||||||
|
trkpt: 4,
|
||||||
|
});
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/0000000000000001/3trk/000002/000003/000004',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
ele: undefined,
|
||||||
|
extensions: {
|
||||||
|
'dyo:accuracy': undefined,
|
||||||
|
'dyo:batterylevel': undefined,
|
||||||
|
'dyo:course': undefined,
|
||||||
|
'dyo:speed': undefined,
|
||||||
|
'dyo:useragent': undefined,
|
||||||
|
},
|
||||||
|
fix: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
link: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
name: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
src: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
time: undefined,
|
||||||
|
type: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
},
|
||||||
|
type: 'trkpt',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the trk if needed', async () => {
|
||||||
|
const id = await putNewTrkpt({ gpx: 5 });
|
||||||
|
expect(id).toEqual({
|
||||||
|
gpx: 5,
|
||||||
|
trk: 0,
|
||||||
|
trkseg: 0,
|
||||||
|
trkpt: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and trk if needed', async () => {
|
||||||
|
const id = await putNewTrkpt();
|
||||||
|
expect(id).toEqual({
|
||||||
|
gpx: 4320000000000000,
|
||||||
|
trk: 0,
|
||||||
|
trkseg: 0,
|
||||||
|
trkpt: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { put } from './lib';
|
||||||
|
import { putNewTrkseg } from './trkseg';
|
||||||
|
|
||||||
|
const emptyTrkpt: Wpt = {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ele: undefined,
|
||||||
|
time: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
name: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
src: undefined,
|
||||||
|
link: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
type: undefined,
|
||||||
|
fix: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
extensions: {
|
||||||
|
'dyo:speed': undefined,
|
||||||
|
'dyo:course': undefined,
|
||||||
|
'dyo:accuracy': undefined,
|
||||||
|
'dyo:batterylevel': undefined,
|
||||||
|
'dyo:useragent': undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewTrkpt = async (id?: IdTrk | IdGpx | IdTrkseg | IdTrkpt) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('trkpt' in finalId)) {
|
||||||
|
const trksegId = await putNewTrkseg(id);
|
||||||
|
finalId = { ...trksegId, trkpt: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('trkpt', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'trkpt',
|
||||||
|
(trkpt) => {
|
||||||
|
return trkpt;
|
||||||
|
},
|
||||||
|
emptyTrkpt
|
||||||
|
);
|
||||||
|
return finalId;
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { putNewTrkseg } from './trkseg';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The trkseg module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new trk when required', async () => {
|
||||||
|
putNewTrkseg({ gpx: 1234567890123456, trk: 123456, trkseg: 5 });
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/1234567890123456/3trk/123456/000005',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
trkpt: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
},
|
||||||
|
type: 'trkseg',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the trk if needed', async () => {
|
||||||
|
const id = await putNewTrkseg({ gpx: 1 });
|
||||||
|
expect(id).toEqual({ gpx: 1, trk: 0, trkseg: 0 });
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and trk if needed', async () => {
|
||||||
|
const id = await putNewTrkseg();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000, trk: 0, trkseg: 0 });
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { getFamily, put } from './lib';
|
||||||
|
import { putNewTrk } from './trk';
|
||||||
|
|
||||||
|
const emptyTrkseg: Trkseg = {
|
||||||
|
trkpt: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewTrkseg = async (id?: IdTrk | IdGpx | IdTrkseg) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('trkseg' in finalId)) {
|
||||||
|
const trkId = await putNewTrk(id);
|
||||||
|
finalId = { ...trkId, trkseg: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('trkseg', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'trkseg',
|
||||||
|
(trkseg) => {
|
||||||
|
return trkseg;
|
||||||
|
},
|
||||||
|
emptyTrkseg
|
||||||
|
);
|
||||||
|
return finalId as IdTrkseg;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTrkseg = async (params: any) => {
|
||||||
|
const { id } = params;
|
||||||
|
const docs = await getFamily(id, { include_docs: true });
|
||||||
|
console.log(`getTrkseg, uri: ${id} docs: ${JSON.stringify(docs)}`);
|
||||||
|
return docs.rows;
|
||||||
|
};
|
|
@ -0,0 +1,119 @@
|
||||||
|
interface Gpx {
|
||||||
|
$: Gpx_;
|
||||||
|
metadata?: Metadata;
|
||||||
|
wpt?: Wpt[];
|
||||||
|
rte?: Rte[];
|
||||||
|
trk?: Trk[];
|
||||||
|
extensions?: Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Gpx_ {
|
||||||
|
version: '1.1';
|
||||||
|
creator: string;
|
||||||
|
xmlns: 'http://www.topografix.com/GPX/1/1';
|
||||||
|
'xmlns:xsi'?: 'http://www.w3.org/2001/XMLSchema-instance';
|
||||||
|
'xsi:schemaLocation'?: string;
|
||||||
|
'xmlns:gpxx'?: string;
|
||||||
|
'xmlns:wptx1'?: string;
|
||||||
|
'xmlns:gpxtpx'?: string;
|
||||||
|
'xmlns:dyo'?: 'http://xmlns.dyomedea.com/';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Metadata {
|
||||||
|
name?: string;
|
||||||
|
desc?: string;
|
||||||
|
author?: string;
|
||||||
|
copyright?: string;
|
||||||
|
link?: Link[];
|
||||||
|
time?: string;
|
||||||
|
keywords?: string;
|
||||||
|
bounds?: Bounds;
|
||||||
|
extensions?: Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Bounds {
|
||||||
|
$: Bounds_;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Bounds_ {
|
||||||
|
minlat: number;
|
||||||
|
minlon: number;
|
||||||
|
maxlat: number;
|
||||||
|
maxlon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Extensions {
|
||||||
|
'dyo:speed'?: number;
|
||||||
|
'dyo:course'?: number;
|
||||||
|
'dyo:accuracy'?: number;
|
||||||
|
'dyo:batterylevel'?: number;
|
||||||
|
'dyo:useragent'?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Trk {
|
||||||
|
name?: string;
|
||||||
|
cmt?: string;
|
||||||
|
desc?: string;
|
||||||
|
src?: string;
|
||||||
|
link?: Link[];
|
||||||
|
number?: number;
|
||||||
|
type?: string;
|
||||||
|
extensions?: Extensions;
|
||||||
|
trkseg?: Trkseg[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Link {
|
||||||
|
$: Link_;
|
||||||
|
text?: string;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Link_ {
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Trkseg {
|
||||||
|
trkpt?: Wpt[];
|
||||||
|
extensions?: Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Wpt {
|
||||||
|
$: Wpt_;
|
||||||
|
ele?: number;
|
||||||
|
time?: string;
|
||||||
|
magvar?: number;
|
||||||
|
geoidheight?: number;
|
||||||
|
name?: string;
|
||||||
|
cmt?: string;
|
||||||
|
desc?: string;
|
||||||
|
src?: string;
|
||||||
|
link?: Link;
|
||||||
|
sym?: string;
|
||||||
|
type?: string;
|
||||||
|
fix?: string;
|
||||||
|
sat?: number;
|
||||||
|
hdop?: number;
|
||||||
|
vdop?: number;
|
||||||
|
pdop?: number;
|
||||||
|
ageofdgpsdata?: number;
|
||||||
|
dgpsid?: number;
|
||||||
|
extensions?: Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Wpt_ {
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Rte {
|
||||||
|
name?: string;
|
||||||
|
cmt?: string;
|
||||||
|
desc?: string;
|
||||||
|
src?: string;
|
||||||
|
link?: Link[];
|
||||||
|
number?: number;
|
||||||
|
type?: string;
|
||||||
|
extensions?: Extensions;
|
||||||
|
rtept?: Wpt[];
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { putNewWpt } from './wpt';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var db: any;
|
||||||
|
var dbReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalDb = globalThis.db;
|
||||||
|
const originalDateNow = globalThis.Date.now;
|
||||||
|
|
||||||
|
describe('The wpt module', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
globalThis.db = { put: jest.fn() };
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
globalThis.db = originalDb;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('db.put() a new wpt when required', async () => {
|
||||||
|
putNewWpt({ gpx: 1, wpt: 2 });
|
||||||
|
await expect(globalThis.db.put).toBeCalledWith({
|
||||||
|
_id: 'gpx/0000000000000001/1wpt/000002',
|
||||||
|
_rev: undefined,
|
||||||
|
doc: {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
ele: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
fix: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
link: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
name: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
src: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
time: undefined,
|
||||||
|
type: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
},
|
||||||
|
type: 'wpt',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('db.put() generates an id for the wpt if needed', async () => {
|
||||||
|
const id = await putNewWpt({ gpx: 1 });
|
||||||
|
expect(id).toEqual({ gpx: 1, wpt: 0 });
|
||||||
|
});
|
||||||
|
test('db.put() generates ids for both gpx and trk if needed', async () => {
|
||||||
|
const id = await putNewWpt();
|
||||||
|
expect(id).toEqual({ gpx: 4320000000000000, wpt: 0 });
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,44 @@
|
||||||
|
import getUri from '../lib/ids';
|
||||||
|
import { putNewGpx } from './gpx';
|
||||||
|
import { put } from './lib';
|
||||||
|
|
||||||
|
export const emptyWpt: Wpt = {
|
||||||
|
$: { lat: 0, lon: 0 },
|
||||||
|
ele: undefined,
|
||||||
|
time: undefined,
|
||||||
|
magvar: undefined,
|
||||||
|
geoidheight: undefined,
|
||||||
|
name: undefined,
|
||||||
|
cmt: undefined,
|
||||||
|
desc: undefined,
|
||||||
|
src: undefined,
|
||||||
|
link: undefined,
|
||||||
|
sym: undefined,
|
||||||
|
type: undefined,
|
||||||
|
fix: undefined,
|
||||||
|
sat: undefined,
|
||||||
|
hdop: undefined,
|
||||||
|
vdop: undefined,
|
||||||
|
pdop: undefined,
|
||||||
|
ageofdgpsdata: undefined,
|
||||||
|
dgpsid: undefined,
|
||||||
|
extensions: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const putNewWpt = async (id?: IdGpx | IdWpt) => {
|
||||||
|
let finalId = { ...id };
|
||||||
|
if (!('wpt' in finalId)) {
|
||||||
|
const gpxId = await putNewGpx(id);
|
||||||
|
finalId = { ...gpxId, wpt: 0 };
|
||||||
|
}
|
||||||
|
const uri = getUri('wpt', finalId);
|
||||||
|
await put(
|
||||||
|
uri,
|
||||||
|
'wpt',
|
||||||
|
(wpt) => {
|
||||||
|
return wpt;
|
||||||
|
},
|
||||||
|
emptyWpt
|
||||||
|
);
|
||||||
|
return finalId as IdWpt;
|
||||||
|
};
|
|
@ -2,7 +2,39 @@
|
||||||
import { render } from 'solid-js/web';
|
import { render } from 'solid-js/web';
|
||||||
import { Router, hashIntegration } from '@solidjs/router';
|
import { Router, hashIntegration } from '@solidjs/router';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
//source={hashIntegration()}
|
import dispatch from './workers/dispatcher-main';
|
||||||
|
|
||||||
|
/* // See https://stackoverflow.com/questions/71538643/property-wakelock-does-not-exist-on-type-navigator
|
||||||
|
const requestWakeLock = async () => {
|
||||||
|
const anyNav: any = navigator;
|
||||||
|
if ('wakeLock' in navigator) {
|
||||||
|
try {
|
||||||
|
const wakeLock = await anyNav['wakeLock'].request('screen');
|
||||||
|
} catch (err: any) {
|
||||||
|
// The wake lock request fails - usually system-related, such as low battery.
|
||||||
|
console.log(`Wake lock request failed: ${err.name}, ${err.message}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('No wake lock support here...');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
console.log('Application hidden');
|
||||||
|
} else {
|
||||||
|
console.log('Application visible');
|
||||||
|
requestWakeLock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// See https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange, false);
|
||||||
|
requestWakeLock(); */
|
||||||
|
|
||||||
|
// Init the database
|
||||||
|
dispatch({ action: 'initDb' });
|
||||||
|
|
||||||
render(
|
render(
|
||||||
() => (
|
() => (
|
||||||
<Router>
|
<Router>
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
export const thisIsAModule = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var _allCaches: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis._allCaches = new Map();
|
||||||
|
|
||||||
|
const cache = {
|
||||||
|
set: (params: any) => {
|
||||||
|
const { cacheId, key, value } = params;
|
||||||
|
if (!_allCaches.has(cacheId)) {
|
||||||
|
_allCaches.set(cacheId, new Map());
|
||||||
|
}
|
||||||
|
const k = _allCaches.get(cacheId);
|
||||||
|
k.set(key, value);
|
||||||
|
},
|
||||||
|
get: (params: any) => {
|
||||||
|
const { cacheId, key } = params;
|
||||||
|
if (!_allCaches.has(cacheId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const k = _allCaches.get(cacheId);
|
||||||
|
if (!k.has(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const value = k.get(key);
|
||||||
|
k.delete(key);
|
||||||
|
k.set(key, value);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
delete: (params: any) => {
|
||||||
|
const { cacheId, key } = params;
|
||||||
|
if (!_allCaches.has(cacheId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const k = _allCaches.get(cacheId);
|
||||||
|
if (!k.has(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const value = k.get(key);
|
||||||
|
k.delete(key);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
map: (params: any) => {
|
||||||
|
const { cacheId } = params;
|
||||||
|
if (!_allCaches.has(cacheId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _allCaches.get(cacheId);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cache;
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* DocURI: Rich document ids for CouchDB.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 null2 GmbH Berlin
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// type/id/subtype/index/version
|
||||||
|
|
||||||
|
// var docuri = (module.exports = exports = {});
|
||||||
|
|
||||||
|
// Cached regular expressions for matching named param parts and splatted parts
|
||||||
|
// of route strings.
|
||||||
|
// http://backbonejs.org/docs/backbone.html#section-158
|
||||||
|
var optionalParam = /\((.*?)\)/g;
|
||||||
|
var namedParam = /(\(\?)?:\w+/g;
|
||||||
|
var splatParam = /\*\w+/g;
|
||||||
|
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
|
||||||
|
var paramKeys = /[*:]\w+/g;
|
||||||
|
|
||||||
|
// Convert a route string into a regular expression,
|
||||||
|
// with named regular expressions for named arguments.
|
||||||
|
// http://backbonejs.org/docs/backbone.html#section-165
|
||||||
|
function routeToRegExp(src) {
|
||||||
|
var keys = [],
|
||||||
|
match;
|
||||||
|
|
||||||
|
while ((match = paramKeys.exec(src)) !== null) {
|
||||||
|
keys.push(match[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var route = src
|
||||||
|
.replace(escapeRegExp, '\\$&')
|
||||||
|
.replace(optionalParam, '(?:$1)?')
|
||||||
|
.replace(namedParam, function (match, optional) {
|
||||||
|
return optional ? match : '([^/?]+)';
|
||||||
|
})
|
||||||
|
.replace(splatParam, '([^?]*?)');
|
||||||
|
|
||||||
|
keys = keys.reduce(function (memo, key) {
|
||||||
|
var value = '\\' + key;
|
||||||
|
|
||||||
|
memo[key] = new RegExp(value + '(\\/|\\)|\\(|$)');
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
src: src,
|
||||||
|
exp: new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'),
|
||||||
|
keys: keys,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a route and a DocURI return an object of extracted parameters.
|
||||||
|
// Unmatched DocURIs will be treated as false.
|
||||||
|
// http://backbonejs.org/docs/backbone.html#section-166
|
||||||
|
function extractParameters(route, fragment, coding) {
|
||||||
|
var params = route.exp.exec(fragment);
|
||||||
|
|
||||||
|
if (!params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = params.slice(1);
|
||||||
|
|
||||||
|
return Object.keys(route.keys).reduce(function (memo, key, i) {
|
||||||
|
var param = params[i];
|
||||||
|
|
||||||
|
if (param) {
|
||||||
|
var k = key.substr(1);
|
||||||
|
var decoder = (coding[k] ?? { decoder: decodeURIComponent }).decoder;
|
||||||
|
|
||||||
|
if (key[0] === '*') {
|
||||||
|
param = param.split('/').map(decoder);
|
||||||
|
} else {
|
||||||
|
param = decoder(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
memo[key.substr(1)] = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert named parameters from object.
|
||||||
|
function insertParameters(route, obj, coding) {
|
||||||
|
var str = route.src;
|
||||||
|
|
||||||
|
Object.keys(route.keys).forEach(function (key) {
|
||||||
|
var k = key.substr(1);
|
||||||
|
var value = obj[k] !== undefined ? obj[k] : '';
|
||||||
|
|
||||||
|
var encoder = (coding[k] ?? { encoder: encodeURIComponent }).encoder;
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value = value.map(encoder).join('/');
|
||||||
|
} else {
|
||||||
|
value = encoder(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.replace(route.keys[key], value + '$1');
|
||||||
|
});
|
||||||
|
|
||||||
|
// massage optional parameter
|
||||||
|
return str.replace(/\(\/\)/g, '').replace(/[)(]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const route = (route, coding = {}) => {
|
||||||
|
route = routeToRegExp(route);
|
||||||
|
|
||||||
|
return function (source, target) {
|
||||||
|
source = source || {};
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
source = extractParameters(route, source, coding);
|
||||||
|
Object.keys(target).forEach(function (key) {
|
||||||
|
source[key] = target[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof source === 'object') {
|
||||||
|
return insertParameters(route, source, coding);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof source === 'string') {
|
||||||
|
return extractParameters(route, source, coding);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default route;
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Rectangle } from '../components/map/types';
|
||||||
|
|
||||||
|
// cf https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_(JavaScript/ActionScript,_etc.)
|
||||||
|
export const lon2tile = (lon: number, zoom: number) => {
|
||||||
|
return ((Number(lon) + 180) / 360) * Math.pow(2, zoom);
|
||||||
|
};
|
||||||
|
export const lat2tile = (lat: number, zoom: number) => {
|
||||||
|
return (
|
||||||
|
((1 -
|
||||||
|
Math.log(
|
||||||
|
Math.tan((Number(lat) * Math.PI) / 180) +
|
||||||
|
1 / Math.cos((Number(lat) * Math.PI) / 180)
|
||||||
|
) /
|
||||||
|
Math.PI) /
|
||||||
|
2) *
|
||||||
|
Math.pow(2, zoom)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function tile2long(x: number, z: number) {
|
||||||
|
return (x / Math.pow(2, z)) * 360 - 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tile2lat(y: number, z: number) {
|
||||||
|
var n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z);
|
||||||
|
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rectanglesIntersect = (r1: Rectangle, r2: Rectangle) =>
|
||||||
|
!(
|
||||||
|
r2.topLeft.x > r1.bottomRight.x ||
|
||||||
|
r2.bottomRight.x < r1.topLeft.x ||
|
||||||
|
r2.topLeft.y > r1.bottomRight.y ||
|
||||||
|
r2.bottomRight.y < r1.topLeft.y
|
||||||
|
);
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Zheng-Xiang Ke
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,91 @@
|
||||||
|
# gpx-parser-builder
|
||||||
|
A simple gpx parser and builder between GPX string and JavaScript object. It is dependent on [isomorphic-xml2js](https://github.com/RikkiGibson/isomorphic-xml2js).
|
||||||
|
|
||||||
|
[![npm](https://img.shields.io/npm/dt/gpx-parser-builder.svg)](https://www.npmjs.com/package/gpx-parser-builder)
|
||||||
|
[![GitHub stars](https://img.shields.io/github/stars/kf99916/gpx-parser-builder.svg)](https://github.com/kf99916/gpx-parser-builder/stargazers)
|
||||||
|
[![GitHub forks](https://img.shields.io/github/forks/kf99916/gpx-parser-builder.svg)](https://github.com/kf99916/gpx-parser-builder/network)
|
||||||
|
[![npm](https://img.shields.io/npm/v/gpx-parser-builder.svg)](https://www.npmjs.com/package/gpx-parser-builder)
|
||||||
|
[![GitHub license](https://img.shields.io/github/license/kf99916/gpx-parser-builder.svg)](https://github.com/kf99916/gpx-parser-builder/blob/master/LICENSE)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
gpx-parser-builder is written with ECMAScript 6. You can leverage [Babel](https://babeljs.io/) and [Webpack](https://webpack.js.org/) to make all browsers available.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gpx-parser-builder --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
v1.0.0+ is a breaking change for v0.2.2-. v1.0.0+ fully supports gpx files including waypoints, routes, and tracks. Every gpx type is 1-1 corresponding to a JavaScript class.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import GPX from 'gpx-parser-builder';
|
||||||
|
|
||||||
|
// Parse gpx
|
||||||
|
const gpx = GPX.parse('GPX_STRING');
|
||||||
|
|
||||||
|
window.console.dir(gpx.metadata);
|
||||||
|
window.console.dir(gpx.wpt);
|
||||||
|
window.console.dir(gpx.trk);
|
||||||
|
|
||||||
|
// Build gpx
|
||||||
|
window.console.log(gpx.toString());
|
||||||
|
```
|
||||||
|
|
||||||
|
Get more details about usage with the unit tests.
|
||||||
|
|
||||||
|
### GPX
|
||||||
|
|
||||||
|
The GPX JavaScript object.
|
||||||
|
|
||||||
|
`constructor(object)`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const gpx = new Gpx({$:{...}, metadat: {...}, wpt:[{...},{...}]}, trk: {...}, rte: {...})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Member Variables
|
||||||
|
|
||||||
|
`$` the attributes for the gpx element. Default value:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
'version': '1.1',
|
||||||
|
'creator': 'gpx-parser-builder',
|
||||||
|
'xmlns': 'http://www.topografix.com/GPX/1/1',
|
||||||
|
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'xsi:schemaLocation': 'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`metadata` the metadata for the gpx.
|
||||||
|
|
||||||
|
`wpt` array of waypoints. It is corresponded to `<wpt>`. The type of all elements in `wpt` is `Waypoint`;
|
||||||
|
|
||||||
|
`rte` array of routes. It is corresponded to `<rte>`. The type of all elements in `rte` is `Route`;
|
||||||
|
|
||||||
|
`trk` array of tracks. It is corresponded to `<trk>`. The type of all elements in `trk` is `Track`;
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
|
`parse(gpxString)` parse gpx string to Gpx object. return `null` if parsing failed.
|
||||||
|
|
||||||
|
#### Member Methods
|
||||||
|
|
||||||
|
`toString(options)` GPX object to gpx string. The options is for [isomorphic-xml2js](https://github.com/RikkiGibson/isomorphic-xml2js).
|
||||||
|
|
||||||
|
## Save as GPX file in the frontend
|
||||||
|
|
||||||
|
You can leverage [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js) or [FileSaver.js](https://github.com/eligrey/FileSaver.js) to save as GPX file. ⚠️Not all borwsers support the above file techniques. ⚠️️️
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Zheng-Xiang Ke, kf99916@gmail.com
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
gpx-parser-builder is available under the MIT license. See the LICENSE file for more info.
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "gpx-parser-builder",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "A simple gpx parser and builder between GPX string and JavaScript object",
|
||||||
|
"main": "./src/gpx.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha --require @babel/register test/**/*.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/kf99916/gpx-parser-builder.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"gpx",
|
||||||
|
"parser",
|
||||||
|
"builder"
|
||||||
|
],
|
||||||
|
"author": "Zheng-Xiang Ke",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/kf99916/gpx-parser-builder/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/kf99916/gpx-parser-builder",
|
||||||
|
"files": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "~7.7",
|
||||||
|
"@babel/preset-env": "~7.7",
|
||||||
|
"@babel/register": "~7.7",
|
||||||
|
"mocha": "~6.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isomorphic-xml2js": "~0.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export default class Bounds {
|
||||||
|
constructor(object) {
|
||||||
|
this.minlat = object.minlat;
|
||||||
|
this.minlon = object.minlon;
|
||||||
|
this.maxlat = object.maxlat;
|
||||||
|
this.maxlon = object.maxlon;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default class Copyright {
|
||||||
|
constructor(object) {
|
||||||
|
this.author = object.author;
|
||||||
|
this.year = object.year;
|
||||||
|
this.license = object.license;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
declare module 'gpx-parser-builder' {
|
||||||
|
class GPX {
|
||||||
|
static parse(gpxString: any): any;
|
||||||
|
constructor(object: any);
|
||||||
|
$: any;
|
||||||
|
extensions: any;
|
||||||
|
metadata: any;
|
||||||
|
wpt: any;
|
||||||
|
rte: any;
|
||||||
|
trk: any;
|
||||||
|
toString(options: any): string;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
import * as xml2js from 'isomorphic-xml2js';
|
||||||
|
import Metadata from './metadata';
|
||||||
|
import Waypoint from './waypoint';
|
||||||
|
import Route from './route';
|
||||||
|
import Track from './track';
|
||||||
|
import {removeEmpty, allDatesToISOString} from './utils';
|
||||||
|
|
||||||
|
const defaultAttributes = {
|
||||||
|
version: '1.1',
|
||||||
|
creator: 'gpx-parser-builder',
|
||||||
|
xmlns: 'http://www.topografix.com/GPX/1/1',
|
||||||
|
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'xsi:schemaLocation':
|
||||||
|
'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class GPX {
|
||||||
|
constructor(object) {
|
||||||
|
this.$ = Object.assign({}, defaultAttributes, object.$ || object.attributes || {});
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
|
||||||
|
if (object.metadata) {
|
||||||
|
this.metadata = new Metadata(object.metadata);
|
||||||
|
}
|
||||||
|
if (object.wpt) {
|
||||||
|
if (!Array.isArray(object.wpt)) {
|
||||||
|
object.wpt = [object.wpt];
|
||||||
|
}
|
||||||
|
this.wpt = object.wpt.map(wpt => new Waypoint(wpt));
|
||||||
|
}
|
||||||
|
if (object.rte) {
|
||||||
|
if (!Array.isArray(object.rte)) {
|
||||||
|
object.rte = [object.rte];
|
||||||
|
}
|
||||||
|
this.rte = object.rte.map(rte => new Route(rte));
|
||||||
|
}
|
||||||
|
if (object.trk) {
|
||||||
|
if (!Array.isArray(object.trk)) {
|
||||||
|
object.trk = [object.trk];
|
||||||
|
}
|
||||||
|
this.trk = object.trk.map(trk => new Track(trk));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEmpty(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static parse(gpxString) {
|
||||||
|
let gpx;
|
||||||
|
xml2js.parseString(gpxString, {
|
||||||
|
explicitArray: false
|
||||||
|
}, (err, xml) => {
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!xml.gpx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpx = new GPX({
|
||||||
|
attributes: xml.gpx.$,
|
||||||
|
metadata: xml.gpx.metadata,
|
||||||
|
wpt: xml.gpx.wpt,
|
||||||
|
rte: xml.gpx.rte,
|
||||||
|
trk: xml.gpx.trk
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return gpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(options) {
|
||||||
|
options = options || {};
|
||||||
|
options.rootName = 'gpx';
|
||||||
|
|
||||||
|
const builder = new xml2js.Builder(options), gpx = new GPX(this);
|
||||||
|
allDatesToISOString(gpx);
|
||||||
|
return builder.buildObject(gpx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export default class Link {
|
||||||
|
constructor(object) {
|
||||||
|
this.$ = {};
|
||||||
|
this.$.href = object.$.href || object.href;
|
||||||
|
this.text = object.text;
|
||||||
|
this.type = object.type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Copyright from './copyright';
|
||||||
|
import Link from './link';
|
||||||
|
import Person from './person';
|
||||||
|
import Bounds from './bounds';
|
||||||
|
|
||||||
|
export default class Metadata {
|
||||||
|
constructor(object) {
|
||||||
|
this.name = object.name;
|
||||||
|
this.desc = object.desc;
|
||||||
|
this.time = object.time ? new Date(object.time) : new Date();
|
||||||
|
this.keywords = object.keywords;
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
if (object.author) {
|
||||||
|
this.author = new Person(object.author);
|
||||||
|
}
|
||||||
|
if (object.link) {
|
||||||
|
if (!Array.isArray(object.link)) {
|
||||||
|
object.link = [object.link];
|
||||||
|
}
|
||||||
|
this.link = object.link.map(l => new Link(l));
|
||||||
|
}
|
||||||
|
if (object.bounds) {
|
||||||
|
this.bounds = new Bounds(object.bounds);
|
||||||
|
}
|
||||||
|
if (object.copyright) {
|
||||||
|
this.copyright = new Copyright(object.copyright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Link from './link';
|
||||||
|
|
||||||
|
export default class Person {
|
||||||
|
constructor(object) {
|
||||||
|
this.name = object.name;
|
||||||
|
this.email = object.email;
|
||||||
|
if (object.link) {
|
||||||
|
this.link = new Link(object.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import Waypoint from './waypoint';
|
||||||
|
import Link from './link';
|
||||||
|
|
||||||
|
export default class Route {
|
||||||
|
constructor(object) {
|
||||||
|
this.name = object.name;
|
||||||
|
this.cmt = object.cmt;
|
||||||
|
this.desc = object.desc;
|
||||||
|
this.src = object.src;
|
||||||
|
this.number = object.number;
|
||||||
|
this.type = object.type;
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
if (object.link) {
|
||||||
|
if (!Array.isArray(object.link)) {
|
||||||
|
this.link = [object.link];
|
||||||
|
}
|
||||||
|
this.link = object.link.map(l => new Link(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.rtept) {
|
||||||
|
if (!Array.isArray(object.rtept)) {
|
||||||
|
this.rtept = [object.rtept];
|
||||||
|
}
|
||||||
|
this.rtept = object.rtept.map(pt => new Waypoint(pt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Waypoint from './waypoint';
|
||||||
|
|
||||||
|
export default class TrackSegment {
|
||||||
|
constructor(object) {
|
||||||
|
if (object.trkpt) {
|
||||||
|
if (!Array.isArray(object.trkpt)) {
|
||||||
|
object.trkpt = [object.trkpt];
|
||||||
|
}
|
||||||
|
this.trkpt = object.trkpt.map(pt => new Waypoint(pt));
|
||||||
|
}
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import TrackSegment from './track-segment';
|
||||||
|
import Link from './link';
|
||||||
|
|
||||||
|
export default class Track {
|
||||||
|
constructor(object) {
|
||||||
|
this.name = object.name;
|
||||||
|
this.cmt = object.cmt;
|
||||||
|
this.desc = object.desc;
|
||||||
|
this.src = object.src;
|
||||||
|
this.number = object.number;
|
||||||
|
this.type = object.type;
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
if (object.link) {
|
||||||
|
if (!Array.isArray(object.link)) {
|
||||||
|
object.link = [object.link];
|
||||||
|
}
|
||||||
|
this.link = object.link.map(l => new Link(l));
|
||||||
|
}
|
||||||
|
if (object.trkseg) {
|
||||||
|
if (!Array.isArray(object.trkseg)) {
|
||||||
|
object.trkseg = [object.trkseg];
|
||||||
|
}
|
||||||
|
this.trkseg = object.trkseg.map(seg => new TrackSegment(seg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
function removeEmpty(obj) {
|
||||||
|
Object.entries(obj).forEach(([key, val]) => {
|
||||||
|
if (val && val instanceof Object) {
|
||||||
|
removeEmpty(val);
|
||||||
|
} else if (val == null) {
|
||||||
|
delete obj[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function allDatesToISOString(obj) {
|
||||||
|
Object.entries(obj).forEach(([key, val]) => {
|
||||||
|
if (val) {
|
||||||
|
if (val instanceof Date) {
|
||||||
|
obj[key] = val.toISOString().split('.')[0] + 'Z';
|
||||||
|
} else if (val instanceof Object) {
|
||||||
|
allDatesToISOString(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { removeEmpty, allDatesToISOString };
|
|
@ -0,0 +1,32 @@
|
||||||
|
import Link from './link';
|
||||||
|
|
||||||
|
export default class Waypoint {
|
||||||
|
constructor(object) {
|
||||||
|
this.$ = {};
|
||||||
|
this.$.lat = object.$.lat === 0 || object.lat === 0 ? 0 : object.$.lat || object.lat || -1;
|
||||||
|
this.$.lon = object.$.lon === 0 || object.lon === 0 ? 0 : object.$.lon || object.lon || -1;
|
||||||
|
this.ele = object.ele;
|
||||||
|
this.time = object.time ? new Date(object.time) : new Date();
|
||||||
|
this.magvar = object.magvar;
|
||||||
|
this.geoidheight = object.geoidheight;
|
||||||
|
this.name = object.name;
|
||||||
|
this.cmt = object.cmt;
|
||||||
|
this.desc = object.desc;
|
||||||
|
this.src = object.src;
|
||||||
|
this.sym = object.sym;
|
||||||
|
this.type = object.type;
|
||||||
|
this.sat = object.sat;
|
||||||
|
this.hdop = object.hdop;
|
||||||
|
this.vdop = object.vdop;
|
||||||
|
this.pdop = object.pdop;
|
||||||
|
this.ageofdgpsdata = object.ageofdgpsdata;
|
||||||
|
this.dgpsid = object.dgpsid;
|
||||||
|
this.extensions = object.extensions;
|
||||||
|
if (object.link) {
|
||||||
|
if (!Array.isArray(object.link)) {
|
||||||
|
object.link = [object.link];
|
||||||
|
}
|
||||||
|
this.link = object.link.map(l => new Link(l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { assert } from 'console';
|
||||||
|
import { findStartTime } from './gpx';
|
||||||
|
|
||||||
|
describe('findStartTime', () => {
|
||||||
|
test('to be undefined for a string', () => {
|
||||||
|
const start = findStartTime('');
|
||||||
|
expect(start).toBeUndefined();
|
||||||
|
});
|
||||||
|
test('to be undefined for an object without time key', () => {
|
||||||
|
const start = findStartTime({ foo: 'foo', bar: 'bar' });
|
||||||
|
expect(start).toBeUndefined();
|
||||||
|
});
|
||||||
|
test('to be the time value for an object with a time key', () => {
|
||||||
|
const start = findStartTime({ foo: 'foo', time: 'bar' });
|
||||||
|
expect(start).toEqual('bar');
|
||||||
|
});
|
||||||
|
test('to be the lowest time value for an object with several time keys', () => {
|
||||||
|
const start = findStartTime({
|
||||||
|
foo: { time: 'foo' },
|
||||||
|
time: 'bar',
|
||||||
|
bar: { time: 'a' },
|
||||||
|
});
|
||||||
|
expect(start).toEqual('a');
|
||||||
|
});
|
||||||
|
test('to be the lowest time value for an array with several objects with time keys', () => {
|
||||||
|
const start = findStartTime({
|
||||||
|
foos: [{ time: 'foo' }, { time: '0' }],
|
||||||
|
time: 'bar',
|
||||||
|
bar: { time: 'a' },
|
||||||
|
});
|
||||||
|
expect(start).toEqual('0');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,19 @@
|
||||||
|
const min = (s1?: string, s2?: string) => {
|
||||||
|
return s1! < s2! ? s1 : s2;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findStartTime = (x: any, startTime?: string) => {
|
||||||
|
if (typeof x === 'object') {
|
||||||
|
let newStartTime = startTime;
|
||||||
|
|
||||||
|
for (const key in x) {
|
||||||
|
if (key === 'time') {
|
||||||
|
newStartTime = min(newStartTime, x[key]);
|
||||||
|
} else {
|
||||||
|
newStartTime = findStartTime(x[key], newStartTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newStartTime;
|
||||||
|
}
|
||||||
|
else return startTime;
|
||||||
|
};
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { route } from './docuri';
|
||||||
|
import uri from './ids';
|
||||||
|
|
||||||
|
describe('Checking some DocURI features', () => {
|
||||||
|
test(', basic route', () => {
|
||||||
|
const gpx = route('gpx/:id');
|
||||||
|
expect(gpx({ id: 10 })).toBe('gpx/10');
|
||||||
|
});
|
||||||
|
test(', basic route (vice-versa', () => {
|
||||||
|
const gpx = route('gpx/:id');
|
||||||
|
expect(gpx('gpx/10')).toMatchObject({ id: '10' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking a multilevel route', () => {
|
||||||
|
test(', using the two levels', () => {
|
||||||
|
const gpx = route('gpx/:gpx/3trk/:trk');
|
||||||
|
expect(gpx({ gpx: 10, trk: 0 })).toBe('gpx/10/3trk/0');
|
||||||
|
});
|
||||||
|
test(', using the two levels (vive-versa)', () => {
|
||||||
|
const gpx = route('gpx/:gpx/3trk/:trk');
|
||||||
|
expect(gpx('gpx/10/3trk/0')).toMatchObject({ gpx: '10', trk: '0' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking a multilevel route with optional part', () => {
|
||||||
|
test(', using the two levels', () => {
|
||||||
|
const gpx = route('gpx/:gpx(/3trk/:trk)');
|
||||||
|
expect(gpx({ gpx: 10, trk: 0 })).toBe('gpx/10/3trk/0');
|
||||||
|
});
|
||||||
|
test(', using the two levels (vive-versa)', () => {
|
||||||
|
const gpx = route('gpx/:gpx(/3trk/:trk)');
|
||||||
|
expect(gpx('gpx/10/3trk/0')).toMatchObject({ gpx: '10', trk: '0' });
|
||||||
|
});
|
||||||
|
test(', using only one level', () => {
|
||||||
|
const gpx = route('gpx/:gpx(/3trk/:trk)');
|
||||||
|
expect(gpx({ gpx: 10 })).toBe('gpx/10/3trk/'); //Unfortunately !
|
||||||
|
});
|
||||||
|
test(', using only one level (vive-versa)', () => {
|
||||||
|
const gpx = route('gpx/:gpx(/3trk/:trk)');
|
||||||
|
expect(gpx('gpx/10')).toMatchObject({ gpx: '10' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking gpx ids', () => {
|
||||||
|
const id = {
|
||||||
|
gpx: 1234567890123456,
|
||||||
|
};
|
||||||
|
const key = 'gpx/1234567890123456';
|
||||||
|
test(', vice', () => {
|
||||||
|
const gpx = uri('gpx', id);
|
||||||
|
expect(gpx).toBe(key);
|
||||||
|
});
|
||||||
|
test(', and versa', () => {
|
||||||
|
const gpx = uri('gpx', key);
|
||||||
|
expect(gpx).toMatchObject(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking trk ids', () => {
|
||||||
|
const id = {
|
||||||
|
gpx: 1234567890123456,
|
||||||
|
trk: 123456,
|
||||||
|
};
|
||||||
|
const key = 'gpx/1234567890123456/3trk/123456';
|
||||||
|
test(', vice', () => {
|
||||||
|
const rte = uri('trk', id);
|
||||||
|
expect(rte).toBe(key);
|
||||||
|
});
|
||||||
|
test(', and versa', () => {
|
||||||
|
const rte = uri('trk', key);
|
||||||
|
expect(rte).toMatchObject(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking trkseg ids', () => {
|
||||||
|
const id = {
|
||||||
|
gpx: 111,
|
||||||
|
trk: 0,
|
||||||
|
trkseg: 3,
|
||||||
|
};
|
||||||
|
const key = 'gpx/0000000000000111/3trk/000000/000003';
|
||||||
|
test(', vice', () => {
|
||||||
|
const rte = uri('trkseg', id);
|
||||||
|
expect(rte).toBe(key);
|
||||||
|
});
|
||||||
|
test(', and versa', () => {
|
||||||
|
const rte = uri('trkseg', key);
|
||||||
|
expect(rte).toMatchObject(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking trkpt ids', () => {
|
||||||
|
const id = {
|
||||||
|
gpx: 25,
|
||||||
|
trk: 8,
|
||||||
|
trkseg: 0,
|
||||||
|
trkpt: 155,
|
||||||
|
};
|
||||||
|
const key = 'gpx/0000000000000025/3trk/000008/000000/000155';
|
||||||
|
test(', vice', () => {
|
||||||
|
const rte = uri('trkpt', id);
|
||||||
|
expect(rte).toBe(key);
|
||||||
|
});
|
||||||
|
test(', and versa', () => {
|
||||||
|
const rte = uri('trkpt', key);
|
||||||
|
expect(rte).toMatchObject(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Checking settings id', () => {
|
||||||
|
test(', vice', () => {
|
||||||
|
const rte = uri('settings', {});
|
||||||
|
expect(rte).toBe('settings');
|
||||||
|
});
|
||||||
|
test(', and versa', () => {
|
||||||
|
const rte = uri('settings', 'settings');
|
||||||
|
expect(rte).toMatchObject({});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,51 @@
|
||||||
|
import route from './docuri';
|
||||||
|
|
||||||
|
const integerType = (n: number) => {
|
||||||
|
return {
|
||||||
|
encoder: (v: number) => v.toString().padStart(n, '0'),
|
||||||
|
decoder: parseInt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const bigIntType = (n: number) => {
|
||||||
|
return {
|
||||||
|
encoder: (v: number) => v.toString().padStart(n, '0'),
|
||||||
|
decoder: BigInt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const coding = {
|
||||||
|
gpx: integerType(16),
|
||||||
|
wpt: integerType(6),
|
||||||
|
rte: integerType(6),
|
||||||
|
rtept: integerType(6),
|
||||||
|
trk: integerType(6),
|
||||||
|
trkseg: integerType(6),
|
||||||
|
trkpt: integerType(6),
|
||||||
|
};
|
||||||
|
|
||||||
|
const routes = {
|
||||||
|
dbdef: route('dbdef', coding),
|
||||||
|
settings: route('settings', coding),
|
||||||
|
gpx: route('gpx/:gpx', coding),
|
||||||
|
wpt: route('gpx/:gpx/1wpt/:wpt', coding),
|
||||||
|
rte: route('gpx/:gpx/2rte/:rte', coding),
|
||||||
|
rtept: route('gpx/:gpx/2rte/:rte/:rtept', coding),
|
||||||
|
trk: route('gpx/:gpx/3trk/:trk', coding),
|
||||||
|
trkseg: route('gpx/:gpx/3trk/:trk/:trkseg', coding),
|
||||||
|
trkpt: route('gpx/:gpx/3trk/:trk/:trkseg/:trkpt', coding),
|
||||||
|
extensions: route('gpx/:gpx/4extensions', coding),
|
||||||
|
};
|
||||||
|
|
||||||
|
type RouteKey = keyof typeof routes;
|
||||||
|
|
||||||
|
const uri = (type: RouteKey, param: any) => {
|
||||||
|
return routes[type](param);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default uri;
|
||||||
|
|
||||||
|
const minDate = -8640000000000000;
|
||||||
|
const halfMinDate = minDate / 2;
|
||||||
|
|
||||||
|
export const intToGpxId = (i: number) => Math.round(i / 2) - halfMinDate;
|
|
@ -0,0 +1,34 @@
|
||||||
|
interface IdGpx {
|
||||||
|
gpx: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IdTrk {
|
||||||
|
gpx: number;
|
||||||
|
trk: number;
|
||||||
|
}
|
||||||
|
interface IdTrkseg {
|
||||||
|
gpx: number;
|
||||||
|
trk: number;
|
||||||
|
trkseg: number;
|
||||||
|
}
|
||||||
|
interface IdTrkpt {
|
||||||
|
gpx: number;
|
||||||
|
trk: number;
|
||||||
|
trkseg: number;
|
||||||
|
trkpt: number;
|
||||||
|
}
|
||||||
|
interface IdWpt {
|
||||||
|
gpx: number;
|
||||||
|
wpt: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IdRte {
|
||||||
|
gpx: number;
|
||||||
|
rte: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IdRtept {
|
||||||
|
gpx: number;
|
||||||
|
rte: number;
|
||||||
|
rtept: number;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
declare module 'docuri';
|
|
@ -0,0 +1 @@
|
||||||
|
declare module 'gpx-parser-builder';
|
|
@ -0,0 +1,37 @@
|
||||||
|
import dispatch, { init, worker } from './dispatcher-main';
|
||||||
|
|
||||||
|
jest.mock('./get-worker', () => ({
|
||||||
|
getWorker: () => ({
|
||||||
|
port: {
|
||||||
|
postMessage: jest.fn(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('The dispatcher-main', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
test('should create a new shared web worker', () => {
|
||||||
|
expect(worker).toBeDefined();
|
||||||
|
});
|
||||||
|
test('should create a onmessage function', () => {
|
||||||
|
expect(worker.port.onmessage).toBeDefined();
|
||||||
|
});
|
||||||
|
test('should return a promise if no callback is provided', () => {
|
||||||
|
expect(dispatch('ping')).toBeInstanceOf(Promise);
|
||||||
|
});
|
||||||
|
test('should forward the message', () => {
|
||||||
|
dispatch('ping');
|
||||||
|
expect(worker.port.postMessage).toBeCalledWith({ id: 0, payload: 'ping' });
|
||||||
|
});
|
||||||
|
test('should return the response', () => {
|
||||||
|
var response;
|
||||||
|
const callback = (error, success) => {
|
||||||
|
response = success;
|
||||||
|
};
|
||||||
|
dispatch('ping', callback);
|
||||||
|
worker.port.onmessage({ data: { id: 0, payload: 'pong' } });
|
||||||
|
expect(response).toEqual('pong');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { getWorker } from './get-worker';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var dispatcherQueue: { index: number; queue: Map<number, any> };
|
||||||
|
}
|
||||||
|
|
||||||
|
export var worker: any;
|
||||||
|
|
||||||
|
export const init = () => {
|
||||||
|
console.log({ caller: 'dispatcher-main / init' });
|
||||||
|
globalThis.dispatcherQueue = { index: 0, queue: new Map() };
|
||||||
|
console.log({ caller: 'dispatcher-main / init', globalThis });
|
||||||
|
|
||||||
|
worker = getWorker();
|
||||||
|
console.log({ caller: 'dispatcher-main / init', worker });
|
||||||
|
|
||||||
|
worker.onmessage = (event: any) => {
|
||||||
|
const { id, payload } = event.data;
|
||||||
|
dispatcherQueue.queue.get(id)(null, payload);
|
||||||
|
dispatcherQueue.queue.delete(id);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatch = (
|
||||||
|
payload: any,
|
||||||
|
callBack?: (error: any, result: any) => void
|
||||||
|
) => {
|
||||||
|
if (worker === undefined) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
if (callBack === undefined) {
|
||||||
|
/** If a callback function is not provided, return a promise */
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
dispatch(payload, (error, result) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** Otherwise, use the callback function */
|
||||||
|
dispatcherQueue.queue.set(dispatcherQueue.index, callBack);
|
||||||
|
const message = {
|
||||||
|
id: dispatcherQueue.index++,
|
||||||
|
payload: payload,
|
||||||
|
};
|
||||||
|
worker.postMessage(message);
|
||||||
|
console.log({ caller: 'dispatcher-main / dispatch', message });
|
||||||
|
};
|
||||||
|
|
||||||
|
export default dispatch;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import worker from './dispatcher-worker';
|
||||||
|
|
||||||
|
jest.mock('../db', () => ({
|
||||||
|
initDb: () => 'called initDb',
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('The dispatcher-worker ', () => {
|
||||||
|
let port;
|
||||||
|
beforeEach(() => {
|
||||||
|
port = {
|
||||||
|
postMessage: jest.fn(),
|
||||||
|
};
|
||||||
|
worker.onconnect({ ports: [port] });
|
||||||
|
});
|
||||||
|
test('creates a onmessage function', () => {
|
||||||
|
expect(port.onmessage).toBeDefined();
|
||||||
|
expect(port.postMessage).not.toBeCalled();
|
||||||
|
});
|
||||||
|
test('receives a ping and sends back an unknownAction', async () => {
|
||||||
|
await port.onmessage({ data: { id: 5, payload: { action: 'ping' } } });
|
||||||
|
expect(port.postMessage).toBeCalledWith({
|
||||||
|
id: 5,
|
||||||
|
payload: 'unknownAction',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('calls initDb when required', async () => {
|
||||||
|
await port.onmessage({ data: { id: 5, payload: { action: 'initDb' } } });
|
||||||
|
expect(port.postMessage).toBeCalledWith({
|
||||||
|
id: 5,
|
||||||
|
payload: 'called initDb',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
/// <reference lib="webworker" />
|
||||||
|
import { initDb } from '../db';
|
||||||
|
import {
|
||||||
|
putNewGpx,
|
||||||
|
existsGpx,
|
||||||
|
pruneAndSaveImportedGpx,
|
||||||
|
getGpxesForViewport,
|
||||||
|
getGpx,
|
||||||
|
} from '../db/gpx';
|
||||||
|
import { getTrk, putNewTrk } from '../db/trk';
|
||||||
|
import { getTrkseg } from '../db/trkseg';
|
||||||
|
|
||||||
|
const self = globalThis as unknown as WorkerGlobalScope;
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
initDb,
|
||||||
|
putNewGpx,
|
||||||
|
putNewTrk,
|
||||||
|
existsGpx,
|
||||||
|
pruneAndSaveImportedGpx,
|
||||||
|
getGpxesForViewport,
|
||||||
|
getGpx,
|
||||||
|
getTrk,
|
||||||
|
getTrkseg,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log({ caller: 'dispatcher-worker', actions });
|
||||||
|
|
||||||
|
onmessage = async function (e) {
|
||||||
|
console.log({ caller: 'dispatcher-worker / onmessage', e });
|
||||||
|
const { id, payload } = e.data;
|
||||||
|
console.log(`payload.action in actions: ${payload.action in actions}`);
|
||||||
|
var returnValue: any = 'unknownAction';
|
||||||
|
if (payload.action in actions) {
|
||||||
|
returnValue = await actions[<keyof typeof actions>payload.action](
|
||||||
|
payload.params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
postMessage({ id: id, payload: returnValue });
|
||||||
|
};
|
||||||
|
|
||||||
|
//export default self;
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const getWorker = () => {
|
||||||
|
console.log({ caller: 'getWorker' });
|
||||||
|
return new Worker(new URL('./dispatcher-worker', import.meta.url), {
|
||||||
|
type: 'module',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getWorker;
|
|
@ -4,7 +4,14 @@
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import solidPlugin from 'vite-plugin-solid';
|
import solidPlugin from 'vite-plugin-solid';
|
||||||
|
|
||||||
export default defineConfig({
|
// yarn add --dev @esbuild-plugins/node-globals-polyfill
|
||||||
|
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill';
|
||||||
|
// yarn add --dev @esbuild-plugins/node-modules-polyfill
|
||||||
|
import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfill';
|
||||||
|
// You don't need to add this to deps, it's included by @esbuild-plugins/node-modules-polyfill
|
||||||
|
import rollupNodePolyFill from 'rollup-plugin-node-polyfills';
|
||||||
|
|
||||||
|
export default defineConfig(({ command, mode, ssrBuild }) => ({
|
||||||
plugins: [solidPlugin()],
|
plugins: [solidPlugin()],
|
||||||
test: {
|
test: {
|
||||||
environment: 'jsdom',
|
environment: 'jsdom',
|
||||||
|
@ -29,5 +36,49 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
conditions: ['development', 'browser'],
|
conditions: ['development', 'browser'],
|
||||||
|
alias: {
|
||||||
|
// This Rollup aliases are extracted from @esbuild-plugins/node-modules-polyfill,
|
||||||
|
// see https://github.com/remorses/esbuild-plugins/blob/master/node-modules-polyfill/src/polyfills.ts
|
||||||
|
// process and buffer are excluded because already managed
|
||||||
|
// by node-globals-polyfill
|
||||||
|
util: 'rollup-plugin-node-polyfills/polyfills/util',
|
||||||
|
sys: 'util',
|
||||||
|
events: 'rollup-plugin-node-polyfills/polyfills/events',
|
||||||
|
stream: 'rollup-plugin-node-polyfills/polyfills/stream',
|
||||||
|
path: 'rollup-plugin-node-polyfills/polyfills/path',
|
||||||
|
querystring: 'rollup-plugin-node-polyfills/polyfills/qs',
|
||||||
|
punycode: 'rollup-plugin-node-polyfills/polyfills/punycode',
|
||||||
|
url: 'rollup-plugin-node-polyfills/polyfills/url',
|
||||||
|
string_decoder: 'rollup-plugin-node-polyfills/polyfills/string-decoder',
|
||||||
|
http: 'rollup-plugin-node-polyfills/polyfills/http',
|
||||||
|
https: 'rollup-plugin-node-polyfills/polyfills/http',
|
||||||
|
os: 'rollup-plugin-node-polyfills/polyfills/os',
|
||||||
|
assert: 'rollup-plugin-node-polyfills/polyfills/assert',
|
||||||
|
constants: 'rollup-plugin-node-polyfills/polyfills/constants',
|
||||||
|
_stream_duplex:
|
||||||
|
'rollup-plugin-node-polyfills/polyfills/readable-stream/duplex',
|
||||||
|
_stream_passthrough:
|
||||||
|
'rollup-plugin-node-polyfills/polyfills/readable-stream/passthrough',
|
||||||
|
_stream_readable:
|
||||||
|
'rollup-plugin-node-polyfills/polyfills/readable-stream/readable',
|
||||||
|
_stream_writable:
|
||||||
|
'rollup-plugin-node-polyfills/polyfills/readable-stream/writable',
|
||||||
|
_stream_transform:
|
||||||
|
'rollup-plugin-node-polyfills/polyfills/readable-stream/transform',
|
||||||
|
timers: 'rollup-plugin-node-polyfills/polyfills/timers',
|
||||||
|
console: 'rollup-plugin-node-polyfills/polyfills/console',
|
||||||
|
vm: 'rollup-plugin-node-polyfills/polyfills/vm',
|
||||||
|
zlib: 'rollup-plugin-node-polyfills/polyfills/zlib',
|
||||||
|
tty: 'rollup-plugin-node-polyfills/polyfills/tty',
|
||||||
|
domain: 'rollup-plugin-node-polyfills/polyfills/domain',
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
define:
|
||||||
|
command === 'serve'
|
||||||
|
? {
|
||||||
|
// By default, Vite doesn't include shims for NodeJS/
|
||||||
|
// necessary for segment analytics lib to work
|
||||||
|
global: {},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
}));
|
||||||
|
|
Loading…
Reference in New Issue