Convert facilmap-client to TypeScript

pull/147/head
Candid Dauth 2020-03-15 01:57:41 +01:00
rodzic df1fd8cfcc
commit 61c731788b
24 zmienionych plików z 1600 dodań i 1740 usunięć

2
.gitignore vendored
Wyświetl plik

@ -3,4 +3,4 @@
node_modules
bower_components
frontend/build
client/build
client/dist

Wyświetl plik

@ -438,7 +438,7 @@ Get a fake line object for a line with the given type. This can be used so that
that line already has the right style.
* `data` (`{typeId: <typeId>}`): An object containing the type ID
* _returns_ (Promise<[line](#line-1)): A fake line object with the styles of this type
* _returns_ (Promise<[line](#line-1)>): A fake line object with the styles of this type
### `addLine(data)`
@ -626,7 +626,7 @@ their `idx` property.
* `id` (number): The ID of this history entry
* `time` (Date): The time when the modification was done
* `type` (string): The type of object that was modified, one of `Marker`, `Line`, `View`, `Type`, `Pad`
* `action` (string): The action that was done, one of `create`, `update, `delete`
* `action` (string): The action that was done, one of `create`, `update`, `delete`
* `objectId` (number): The ID of the object that was modified (null if the object was the map itself)
* `objectBefore` (object): The object before the modification (null if `action` is `create`)
* `objectAfter` (object): The object after the modification (null if `action` is `delete`)
@ -644,7 +644,7 @@ their `idx` property.
* `name` (string): The name of the field. This is at the same time the key in the `data` properties of markers and lines
* `oldName` (string): When renaming a field (using [`editType(data)`](#edittypedata)), specify the former name here
* `type` (string): The type of field, one of `textarea`, `dropdown`, `checkbox`, `input`
* `controlColour`, `controlSize`, `controlSymbol`, `controlWidth` (boolean): If this field is a dropdown, whether
* `controlColour`, `controlSize`, `controlSymbol`, `controlShape`, `controlWidth` (boolean): If this field is a dropdown, whether
the different options set a specific property on the object
* `default` (string/boolean): The default value of this field
* `options` ([object]): If this field is a dropdown, an array of objects with the following properties:

Wyświetl plik

@ -1,399 +0,0 @@
const io = require("socket.io-client");
const Promise = require("es6-promise").Promise;
class Socket {
constructor(server, padId) {
this._init(server, padId);
}
_init(server, padId) {
// Needs to be in a separate method so that we can merge this class with a scope object in the frontend.
this.server = server;
this.padId = padId;
this.socket = io.connect(server, { 'force new connection': true });
this.padData = null;
this.readonly = null;
this.writable = null;
this.deleted = false;
this.markers = { };
this.lines = { };
this.views = { };
this.types = { };
this.history = { };
this.route = null;
this._listeners = [ ];
for(let i in this._handlers) {
this.on(i, this._handlers[i].bind(this));
}
setTimeout(() => {
this._simulateEvent("loadStart");
}, 0);
this.once("connect", () => {
this._simulateEvent("loadEnd");
});
}
on(eventName, fn) {
if(typeof this._listeners[eventName] != "object") {
this._listeners[eventName] = [ ];
this.socket.on(eventName, this._simulateEvent.bind(this, eventName));
}
this._listeners[eventName].push(fn);
}
once(eventName, fn) {
let handler = (data) => {
this.removeListener(eventName, handler);
fn(data);
};
this.on(eventName, handler);
}
removeListener(eventName, fn) {
if(typeof this._listeners[eventName] == "object") {
this._listeners[eventName] = this._listeners[eventName].filter((listener) => (listener !== fn));
}
}
_emit(eventName, data) {
return new Promise((resolve, reject) => {
this._simulateEvent("loadStart");
this.socket.emit(eventName, data, (err, data) => {
this._simulateEvent("loadEnd");
if(err)
reject(err);
else
resolve(data);
});
});
}
setPadId(padId) {
if(this.padId != null)
throw new Error("Pad ID already set.");
return this._setPadId(padId);
}
updateBbox(bbox) {
this.bbox = bbox;
return this._emit("updateBbox", bbox).then((obj) => {
this._receiveMultiple(obj);
});
}
createPad(data) {
return this._emit("createPad", data).then((obj) => {
this.readonly = false;
this.writable = 2;
this._receiveMultiple(obj);
});
}
editPad(data) {
return this._emit("editPad", data);
}
deletePad() {
return this._emit("deletePad");
}
listenToHistory() {
return this._emit("listenToHistory").then((obj) => {
this._listeningToHistory = true;
this._receiveMultiple(obj);
});
}
stopListeningToHistory() {
this._listeningToHistory = false;
return this._emit("stopListeningToHistory");
}
revertHistoryEntry(data) {
return this._emit("revertHistoryEntry", data).then((obj) => {
this.history = { };
this._receiveMultiple(obj);
});
}
async getMarker(data) {
let marker = await this._emit("getMarker", data);
this.markers[marker.id] = marker;
return marker;
}
addMarker(data) {
return this._emit("addMarker", data);
}
editMarker(data) {
return this._emit("editMarker", data);
}
deleteMarker(data) {
return this._emit("deleteMarker", data);
}
getLineTemplate(data) {
return this._emit("getLineTemplate", data);
}
addLine(data) {
return this._emit("addLine", data);
}
editLine(data) {
return this._emit("editLine", data);
}
deleteLine(data) {
return this._emit("deleteLine", data);
}
exportLine(data) {
return this._emit("exportLine", data);
}
find(data) {
return this._emit("find", data);
}
findOnMap(data) {
return this._emit("findOnMap", data);
}
getRoute(data) {
return this._emit("getRoute", data);
}
setRoute(data) {
return this._emit("setRoute", data).then((route) => {
if(route) { // If unset, a newer submitted route has returned in the meantime
this.route = route;
this.route.trackPoints = this._mergeTrackPoints({}, route.trackPoints);
}
return this.route;
});
}
clearRoute() {
this.route = null;
return this._emit("clearRoute");
}
lineToRoute(data) {
return this._emit("lineToRoute", data).then((route) => {
this.route = route;
this.route.trackPoints = this._mergeTrackPoints({}, route.trackPoints);
return this.route;
});
}
exportRoute(data) {
return this._emit("exportRoute", data);
}
addType(data) {
return this._emit("addType", data);
}
editType(data) {
return this._emit("editType", data);
}
deleteType(data) {
return this._emit("deleteType", data);
}
addView(data) {
return this._emit("addView", data);
}
editView(data) {
return this._emit("editView", data);
}
deleteView(data) {
return this._emit("deleteView", data);
}
geoip() {
return this._emit("geoip");
}
disconnect() {
this.socket.removeAllListeners();
this.socket.disconnect();
}
_setPadId(padId) {
this.padId = padId;
return this._emit("setPadId", padId).then((obj) => {
this.disconnected = false;
this._receiveMultiple(obj);
}).catch((err) => {
this.serverError = err;
throw err;
});
}
_receiveMultiple(obj) {
for(let i in obj || { })
obj[i].forEach((it) => { this._simulateEvent(i, it); });
}
_simulateEvent(eventName, data) {
if(typeof this._listeners[eventName] == "object") {
this._listeners[eventName].forEach(function(listener) {
listener(data);
});
}
}
_mergeTrackPoints(existingTrackPoints, newTrackPoints) {
let ret = existingTrackPoints ? Object.assign({}, existingTrackPoints) : {};
for(let i=0; i<newTrackPoints.length; i++) {
ret[newTrackPoints[i].idx] = newTrackPoints[i];
}
ret.length = 0;
for(let i in ret) {
if(i != "length" && i >= ret.length)
ret.length = 1*i+1;
}
return ret;
}
}
Socket.prototype._handlers = {
padData(data) {
this.padData = data;
if(data.writable != null) {
this.readonly = (data.writable == 0);
this.writable = data.writable;
}
let id = this.writable == 2 ? data.adminId : this.writable == 1 ? data.writeId : data.id;
if(id != null)
this.padId = id;
},
deletePad() {
this.readonly = true;
this.writable = 0;
this.deleted = true;
},
marker(data) {
if(this.markers[data.id] == null)
this.markers[data.id] = { };
this.markers[data.id] = data;
},
deleteMarker(data) {
delete this.markers[data.id];
},
line(data) {
if(this.lines[data.id])
data.trackPoints = this.lines[data.id].trackPoints;
else
this.lines[data.id] = { };
this.lines[data.id] = data;
},
deleteLine(data) {
delete this.lines[data.id];
},
linePoints(data) {
let line = this.lines[data.id];
if(line == null)
return console.error("Received line points for non-existing line "+data.id+".");
line.trackPoints = this._mergeTrackPoints(data.reset ? {} : line.trackPoints, data.trackPoints);
},
routePoints(data) {
if(!this.route) {
console.error("Received route points for non-existing route.");
return;
}
this.route.trackPoints = this._mergeTrackPoints(this.route.trackPoints, data);
},
view(data) {
if(this.views[data.id] == null)
this.views[data.id] = { };
this.views[data.id] = data;
},
deleteView(data) {
delete this.views[data.id];
if(this.padData.defaultViewId == data.id)
this.padData.defaultViewId = null;
},
type(data) {
if(this.types[data.id] == null)
this.types[data.id] = { };
this.types[data.id] = data;
},
deleteType(data) {
delete this.types[data.id];
},
disconnect() {
this.disconnected = true;
this.markers = { };
this.lines = { };
this.views = { };
this.history = { };
},
connect() {
if(this.padId)
this._setPadId(this.padId);
else
this.disconnected = false; // Otherwise it gets set when padData arrives
if(this.bbox)
this.updateBbox(this.bbox);
if(this._listeningToHistory) // TODO: Execute after setPadId() returns
this.listenToHistory().catch(function(err) { console.error("Error listening to history", err); });
if(this.route)
this.setRoute(this.route);
},
history(data) {
this.history[data.id] = data;
// TODO: Limit to 50 entries
}
};
module.exports = Socket;

1922
client/package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -14,27 +14,30 @@
},
"license": "AGPL-3.0",
"author": "Candid Dauth <cdauth@cdauth.eu>",
"main": "./client.js",
"main": "./dist/client.js",
"repository": {
"type": "git",
"url": "https://github.com/FacilMap/facilmap2.git"
},
"scripts": {
"deps": "npm install",
"build": "webpack",
"clean": "rimraf ./build"
"watch": "webpack --watch",
"clean": "rimraf dist",
"dev-server": "webpack-dev-server --mode development"
},
"dependencies": {
"@types/geojson": "^7946.0.7",
"es6-promise": "^4.2.5",
"facilmap-types": "2.7.0",
"socket.io-client": "^2.3.0"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"babel-loader": "^8.1.0",
"expose-loader": "^0.7.5",
"@types/socket.io-client": "^1.4.32",
"rimraf": "^3.0.2",
"ts-loader": "^6.2.1",
"typescript": "^3.8.3",
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.6.1",
"webpack-cli": "^3.3.11"
}
}

Wyświetl plik

@ -0,0 +1,460 @@
import io from "socket.io-client";
import { Promise as PromiseShim } from "es6-promise";
import {
Bbox, EventData, EventDataParams, EventHandler, EventName, FindOnMapQuery, FindQuery, HistoryEntry, ID, Line, LineCreate,
LineExportRequest, LineTemplateRequest, LineUpdate, Marker, MarkerCreate, MarkerUpdate, MultipleEvents, ObjectWithId,
PadData, PadDataCreate, PadDataUpdate, PadId, RequestData, RequestName, ResponseData, Route, RouteCreate, RouteExportRequest,
TrackPoint, Type, TypeCreate, TypeUpdate, View, ViewCreate, ViewUpdate, Writable
} from "facilmap-types";
declare module "facilmap-types/src/events" {
interface EventMap {
connect: void,
connect_error: Error;
connect_timeout: void;
reconnect: number;
reconnect_attempt: number;
reconnecting: number;
reconnect_error: Error;
reconnect_failed: void;
disconnect: void,
loadStart: void,
loadEnd: void
}
}
export interface TrackPoints {
[idx: number]: TrackPoint;
length: number
}
export interface LineWithTrackPoints extends Line {
trackPoints: TrackPoints;
}
export interface RouteWithTrackPoints extends Omit<Route, "trackPoints"> {
trackPoints: TrackPoints;
}
export default class Socket {
disconnected!: boolean;
server!: string;
padId!: string;
bbox!: Bbox | null;
socket!: SocketIOClient.Socket;
padData!: PadData | null;
readonly!: boolean | null;
writable!: Writable | null;
deleted!: boolean;
markers!: Record<ID, Marker>;
lines!: Record<ID, LineWithTrackPoints>;
views!: Record<ID, View>;
types!: Record<ID, Type>;
history!: Record<ID, HistoryEntry>;
route!: RouteWithTrackPoints | null;
serverError!: Error | null;
_listeners!: {
[E in EventName]?: Array<EventHandler<E>>
};
_listeningToHistory!: boolean;
constructor(server: string, padId: string) {
this._init(server, padId);
}
_init(server: string, padId: string) {
// Needs to be in a separate method so that we can merge this class with a scope object in the frontend.
this.server = server;
this.padId = padId;
this.bbox = null;
this.serverError = null;
this.disconnected = true;
this.socket = io.connect(server, { forceNew: true });
this.padData = null;
this.readonly = null;
this.writable = null;
this.deleted = false;
this.markers = { };
this.lines = { };
this.views = { };
this.types = { };
this.history = { };
this.route = null;
this._listeners = { };
this._listeningToHistory = false;
for(let i of Object.keys(this._handlers) as EventName[]) {
this.on(i, this._handlers[i] as EventHandler<typeof i>);
}
setTimeout(() => {
this._simulateEvent("loadStart");
}, 0);
this.once("connect", () => {
this._simulateEvent("loadEnd");
});
}
on<E extends EventName>(eventName: E, fn: EventHandler<E>) {
let listeners = this._listeners[eventName] as Array<EventHandler<E>> | undefined;
if(!listeners) {
listeners = this._listeners[eventName] = [ ];
this.socket.on(eventName, (data: EventData<E>) => { this._simulateEvent(eventName as any, data); });
}
listeners.push(fn);
}
once<E extends EventName>(eventName: E, fn: EventHandler<E>) {
let handler: EventHandler<E> = (data) => {
this.removeListener(eventName, handler);
fn(data);
};
this.on(eventName, handler);
}
removeListener<E extends EventName>(eventName: E, fn: EventHandler<E>) {
const listeners = this._listeners[eventName] as Array<EventHandler<E>> | undefined;
if(listeners) {
this._listeners[eventName] = listeners.filter((listener) => (listener !== fn)) as any;
}
}
_emit<R extends RequestName>(eventName: R, ...[data]: RequestData<R> extends void ? [ ] : [ RequestData<R> ]): PromiseShim<ResponseData<R>> {
return new PromiseShim((resolve, reject) => {
this._simulateEvent("loadStart");
this.socket.emit(eventName, data, (err: Error, data: ResponseData<R>) => {
this._simulateEvent("loadEnd");
if(err)
reject(err);
else
resolve(data);
});
});
}
_handlers: {
[E in EventName]?: EventHandler<E>
} = {
padData: (data) => {
this.padData = data;
if(data.writable != null) {
this.readonly = (data.writable == 0);
this.writable = data.writable;
}
let id = this.writable == 2 ? data.adminId : this.writable == 1 ? data.writeId : data.id;
if(id != null)
this.padId = id;
},
deletePad: () => {
this.readonly = true;
this.writable = 0;
this.deleted = true;
},
marker: (data) => {
this.markers[data.id] = data;
},
deleteMarker: (data) => {
delete this.markers[data.id];
},
line: (data) => {
this.lines[data.id] = {
...data,
trackPoints: this.lines[data.id]?.trackPoints || { }
};
},
deleteLine: (data) => {
delete this.lines[data.id];
},
linePoints: (data) => {
let line = this.lines[data.id];
if(line == null)
return console.error("Received line points for non-existing line "+data.id+".");
line.trackPoints = this._mergeTrackPoints(data.reset ? {} : line.trackPoints, data.trackPoints);
},
routePoints: (data) => {
if(!this.route) {
console.error("Received route points for non-existing route.");
return;
}
this.route.trackPoints = this._mergeTrackPoints(this.route.trackPoints, data);
},
view: (data) => {
this.views[data.id] = data;
},
deleteView: (data) => {
delete this.views[data.id];
if (this.padData) {
if(this.padData.defaultViewId == data.id)
this.padData.defaultViewId = null;
}
},
type: (data) => {
this.types[data.id] = data;
},
deleteType: (data) => {
delete this.types[data.id];
},
disconnect: () => {
this.disconnected = true;
this.markers = { };
this.lines = { };
this.views = { };
this.history = { };
},
connect: () => {
if(this.padId)
this._setPadId(this.padId);
else
this.disconnected = false; // Otherwise it gets set when padData arrives
if(this.bbox)
this.updateBbox(this.bbox);
if(this._listeningToHistory) // TODO: Execute after setPadId() returns
this.listenToHistory().catch(function(err) { console.error("Error listening to history", err); });
if(this.route)
this.setRoute(this.route);
},
history: (data) => {
this.history[data.id] = data;
// TODO: Limit to 50 entries
}
};
setPadId(padId: PadId) {
if(this.padId != null)
throw new Error("Pad ID already set.");
return this._setPadId(padId);
}
updateBbox(bbox: Bbox) {
this.bbox = bbox;
return this._emit("updateBbox", bbox).then((obj) => {
this._receiveMultiple(obj);
});
}
createPad(data: PadDataCreate) {
return this._emit("createPad", data).then((obj) => {
this.readonly = false;
this.writable = 2;
this._receiveMultiple(obj);
});
}
editPad(data: PadDataUpdate) {
return this._emit("editPad", data);
}
deletePad() {
return this._emit("deletePad");
}
listenToHistory() {
return this._emit("listenToHistory").then((obj) => {
this._listeningToHistory = true;
this._receiveMultiple(obj);
});
}
stopListeningToHistory() {
this._listeningToHistory = false;
return this._emit("stopListeningToHistory");
}
revertHistoryEntry(data: ObjectWithId) {
return this._emit("revertHistoryEntry", data).then((obj) => {
this.history = { };
this._receiveMultiple(obj);
});
}
async getMarker(data: ObjectWithId) {
let marker = await this._emit("getMarker", data);
this.markers[marker.id] = marker;
return marker;
}
addMarker(data: MarkerCreate) {
return this._emit("addMarker", data);
}
editMarker(data: MarkerUpdate) {
return this._emit("editMarker", data);
}
deleteMarker(data: ObjectWithId) {
return this._emit("deleteMarker", data);
}
getLineTemplate(data: LineTemplateRequest) {
return this._emit("getLineTemplate", data);
}
addLine(data: LineCreate) {
return this._emit("addLine", data);
}
editLine(data: LineUpdate) {
return this._emit("editLine", data);
}
deleteLine(data: ObjectWithId) {
return this._emit("deleteLine", data);
}
exportLine(data: LineExportRequest) {
return this._emit("exportLine", data);
}
find(data: FindQuery) {
return this._emit("find", data);
}
findOnMap(data: FindOnMapQuery) {
return this._emit("findOnMap", data);
}
getRoute(data: RouteCreate) {
return this._emit("getRoute", data);
}
setRoute(data: RouteCreate) {
return this._emit("setRoute", data).then((route) => {
if(route) { // If unset, a newer submitted route has returned in the meantime
this.route = {
...route,
trackPoints: this._mergeTrackPoints({}, route.trackPoints)
};
}
return this.route;
});
}
clearRoute() {
this.route = null;
return this._emit("clearRoute");
}
lineToRoute(data: ObjectWithId) {
return this._emit("lineToRoute", data).then((route) => {
this.route = {
...route,
trackPoints: this._mergeTrackPoints({}, route.trackPoints)
};
return this.route;
});
}
exportRoute(data: RouteExportRequest) {
return this._emit("exportRoute", data);
}
addType(data: TypeCreate) {
return this._emit("addType", data);
}
editType(data: TypeUpdate) {
return this._emit("editType", data);
}
deleteType(data: ObjectWithId) {
return this._emit("deleteType", data);
}
addView(data: ViewCreate) {
return this._emit("addView", data);
}
editView(data: ViewUpdate) {
return this._emit("editView", data);
}
deleteView(data: ObjectWithId) {
return this._emit("deleteView", data);
}
geoip() {
return this._emit("geoip");
}
disconnect() {
this.socket.removeAllListeners();
this.socket.disconnect();
}
_setPadId(padId: string) {
this.padId = padId;
return this._emit("setPadId", padId).then((obj) => {
this.disconnected = false;
this._receiveMultiple(obj);
}).catch((err) => {
this.serverError = err;
throw err;
});
}
_receiveMultiple(obj?: MultipleEvents) {
if (obj) {
for(const i of Object.keys(obj) as EventName[])
(obj[i] as Array<EventData<typeof i>>).forEach((it) => { this._simulateEvent(i, it); });
}
}
_simulateEvent<E extends EventName>(eventName: E, ...[data]: EventDataParams<E>) {
const listeners = this._listeners[eventName] as Array<EventHandler<E>> | undefined;
if(listeners) {
listeners.forEach(function(listener: EventHandler<E>) {
listener(data as EventData<E>);
});
}
}
_mergeTrackPoints(existingTrackPoints: Record<number, TrackPoint> | null, newTrackPoints: TrackPoint[]) {
let ret = { ...(existingTrackPoints || { }) } as TrackPoints;
for(let i=0; i<newTrackPoints.length; i++) {
ret[newTrackPoints[i].idx] = newTrackPoints[i];
}
ret.length = 0;
for(let i in ret) {
if(i != "length")
ret.length = Math.max(ret.length, parseInt(i) + 1);
}
return ret;
}
}

Wyświetl plik

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es3",
"esModuleInterop": true,
"strict": true,
"sourceMap": true,
"declaration": true,
"outDir": "dist",
"moduleResolution": "node"
}
}

Wyświetl plik

@ -1,24 +1,40 @@
const webpack = require("webpack");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
entry: `expose-loader?FacilMap.Client!${__dirname}/client.js`,
output: {
filename: "client.js",
path: __dirname + "/build/"
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: [ "@babel/preset-env" ]
}
module.exports = (env, argv) => {
const isDev = argv.mode == "development";
return {
entry: `${__dirname}/src/client.ts`,
output: {
filename: "client.js",
path: __dirname + "/dist/",
library: "FacilMap.Client",
libraryTarget: "umd"
},
resolve: {
extensions: [ ".js", ".ts" ]
},
mode: isDev ? "development" : "production",
devtool: isDev ? "cheap-eval-source-map" : "source-map",
module: {
rules: [
{
resource: { and: [ /\.ts/, [
__dirname + "/src/"
] ] },
loader: 'ts-loader'
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
}
]
},
mode: "production",
devtool: "source-map"
]
},
plugins: [
//new BundleAnalyzerPlugin()
],
devServer: {
publicPath: "/dist"
}
};
};

Wyświetl plik

@ -96,8 +96,5 @@
"webpack-bundle-analyzer": "^3.7.0",
"webpack-cli": "^3.3.11",
"webpack-hot-middleware": "^2.24.3"
},
"resolutions": {
"**/leaflet": "1.6.0"
}
}

13
types/package-lock.json wygenerowano 100644
Wyświetl plik

@ -0,0 +1,13 @@
{
"name": "facilmap-types",
"version": "2.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/geojson": {
"version": "7946.0.7",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ=="
}
}
}

19
types/package.json 100644
Wyświetl plik

@ -0,0 +1,19 @@
{
"name": "facilmap-types",
"version": "2.7.0",
"description": "Typescript typings for the FacilMap communication between client and server.",
"homepage": "https://github.com/FacilMap/facilmap2",
"bugs": {
"url": "https://github.com/FacilMap/facilmap2/issues"
},
"license": "AGPL-3.0",
"author": "Candid Dauth <cdauth@cdauth.eu>",
"main": "./src/index.ts",
"repository": {
"type": "git",
"url": "https://github.com/FacilMap/facilmap2.git"
},
"dependencies": {
"@types/geojson": "^7946.0.7"
}
}

32
types/src/base.ts 100644
Wyświetl plik

@ -0,0 +1,32 @@
export type Latitude = number;
export type Longitude = number;
export type ZoomLevel = number;
export type Colour = string;
export type Symbol = string;
export type Shape = "" | "drop" | "circle";
export type ID = number;
export type RouteMode = string;
export type Layer = string;
export type ExportFormat = "gpx-trk" | "gpx-rte";
export interface Point {
lat: Latitude;
lon: Longitude;
}
export interface Bbox {
top: Latitude;
bottom: Latitude;
left: Longitude;
right: Longitude;
}
export interface BboxWithZoom extends Bbox {
zoom: ZoomLevel;
}
export type ObjectWithId = {
id: ID;
}
export type AllOptionalExceptId<T extends ObjectWithId> = Pick<T, "id"> & Partial<Omit<T, "id">>;

Wyświetl plik

@ -0,0 +1,43 @@
import { ID, ObjectWithId } from "./base";
import { Type } from "./type";
import { HistoryEntry } from "./historyEntry";
import { View } from "./view";
import { Line, TrackPoint } from "./line";
import { Marker } from "./marker";
import { PadData } from "./padData";
export interface LinePointsEvent {
id: ID;
reset: boolean;
trackPoints: TrackPoint[];
}
export interface EventMap {
padData: PadData;
deletePad: void;
marker: Marker;
deleteMarker: ObjectWithId;
line: Line;
deleteLine: ObjectWithId;
linePoints: LinePointsEvent;
routePoints: TrackPoint[];
view: View;
deleteView: ObjectWithId;
type: Type;
deleteType: ObjectWithId;
history: HistoryEntry;
}
export type EventName = keyof EventMap;
export type EventData<E extends EventName> = EventMap[E];
export type EventHandler<E extends EventName> = EventData<E> extends void ? () => void : (data: EventData<E>) => void;
export type EventDataParams<E extends EventName> = Array<EventData<E>> & (EventData<E> extends void ? {
0?: undefined
} : {
0: EventData<E>
});
export type MultipleEvents = {
[E in EventName]?: Array<EventData<E>> | undefined
};

Wyświetl plik

@ -0,0 +1,41 @@
import { ID } from "./base";
import { Marker } from "./marker";
import { Line } from "./line";
import { View } from "./view";
import { PadData } from "./padData";
import { Type } from "./type";
type HistoryEntryType = "Marker" | "Line" | "View" | "Type" | "Pad";
type HistoryEntryAction = "create" | "update" | "delete";
type HistoryEntryObject<T extends HistoryEntryType> =
T extends "Marker" ? Marker :
T extends "Line" ? Line :
T extends "View" ? View :
T extends "Type" ? Type :
PadData;
type HistoryEntryBefore<T extends HistoryEntryType, A extends HistoryEntryAction> = A extends "create" ? null : HistoryEntryObject<T>;
type HistoryEntryAfter<T extends HistoryEntryType, A extends HistoryEntryAction> = A extends "delete" ? null : HistoryEntryObject<T>;
interface HistoryEntryBase<T extends HistoryEntryType, A extends HistoryEntryAction> {
id: ID;
time: Date;
objectId: ID;
action: A;
objectBefore: HistoryEntryBefore<T, A>;
objectAfter: HistoryEntryAfter<T, A>;
}
type HistoryEntryBase2<T extends HistoryEntryType> =
HistoryEntryBase<T, "create">
| HistoryEntryBase<T, "update">
| HistoryEntryBase<T, "delete">
export type HistoryEntry =
HistoryEntryBase2<"Marker">
| HistoryEntryBase2<"Line">
| HistoryEntryBase2<"View">
| HistoryEntryBase2<"Type">
| HistoryEntryBase2<"Pad">;

11
types/src/index.ts 100644
Wyświetl plik

@ -0,0 +1,11 @@
export * from './base';
export * from './events';
export * from './historyEntry';
export * from './line';
export * from './marker';
export * from './padData';
export * from './route';
export * from './searchResult';
export * from './socket';
export * from './type';
export * from './view';

33
types/src/line.ts 100644
Wyświetl plik

@ -0,0 +1,33 @@
import { AllOptionalExceptId, Bbox, Colour, ID, Point, RouteMode, ZoomLevel } from "./base";
interface LineBase {
id: ID;
routePoints: Point[];
mode: RouteMode;
colour: Colour;
width: number;
name: string;
typeId: ID;
data: Record<string, string>;
}
export interface Line extends LineBase, Bbox {
distance: number;
ascent: number;
descent: number;
time: number;
}
export interface TrackPoint extends Point {
idx: number;
zoom: ZoomLevel;
ele?: number;
}
interface TrackLineCreate extends LineBase {
mode: "track";
trackPoints: TrackPoint[];
}
export type LineCreate = LineBase | TrackLineCreate;
export type LineUpdate = AllOptionalExceptId<LineCreate>;

Wyświetl plik

@ -0,0 +1,16 @@
import { AllOptionalExceptId, Colour, ID, Point, Shape, Symbol } from "./base";
export interface Marker extends Point {
id: ID;
name: string;
colour: Colour;
size: number;
symbol: Symbol;
shape: Shape;
elevation: number;
typeId: ID;
data: Record<string, string>;
}
export type MarkerCreate = Marker;
export type MarkerUpdate = AllOptionalExceptId<MarkerCreate>;

Wyświetl plik

@ -0,0 +1,31 @@
import { View } from "./view";
import { ID } from "./base";
export type PadId = string;
export interface PadDataBase {
id: PadId;
writeId: PadId;
adminId: PadId;
name: string;
searchEngines: boolean;
description: string;
clusterMarkers: boolean;
legend1: string;
legend2: string;
defaultViewId: ID | null;
}
export enum Writable {
READ = 0,
WRITE = 1,
ADMIN = 2
}
export type PadData = Omit<PadDataBase, "writeId" | "adminId"> & Partial<Pick<PadDataBase, "writeId" | "adminId">> & {
writable: Writable;
defaultView?: View;
}
export type PadDataCreate = PadDataBase;
export type PadDataUpdate = Partial<PadDataCreate>;

18
types/src/route.ts 100644
Wyświetl plik

@ -0,0 +1,18 @@
import { Point, RouteMode } from "./base";
import { TrackPoint } from "./line";
interface RouteBase {
routePoints: Point[];
mode: RouteMode;
}
export interface RouteCreate extends RouteBase {
}
export interface Route extends RouteBase {
trackPoints: TrackPoint[],
distance: number;
time?: number;
ascent?: number;
descent?: number;
}

Wyświetl plik

@ -0,0 +1,18 @@
import { Bbox, Point, ZoomLevel } from "./base";
import { GeoJSON } from "geojson";
export type SearchResultType = string;
export interface SearchResult extends Point {
short_name: string;
display_name: string;
address: string;
boundingbox: Bbox | null;
zoom: ZoomLevel | null;
extratags: Record<string, string>;
geojson: GeoJSON | null;
icon: Symbol;
type: SearchResultType;
id: string;
ele?: number;
}

102
types/src/socket.ts 100644
Wyświetl plik

@ -0,0 +1,102 @@
import { Bbox, ExportFormat, ID, ObjectWithId } from "./base";
import { PadData, PadDataCreate, PadDataUpdate } from "./padData";
import { Marker, MarkerCreate, MarkerUpdate } from "./marker";
import { Line, LineCreate, LineUpdate } from "./line";
import { Route, RouteCreate } from "./route";
import { Type, TypeCreate, TypeUpdate } from "./type";
import { View, ViewCreate, ViewUpdate } from "./view";
import { MultipleEvents } from "./events";
import { SearchResult } from "./searchResult";
export interface LineTemplateRequest {
typeId: ID
}
export interface LineExportRequest {
id: ID;
format: ExportFormat
}
export interface RouteExportRequest {
format: ExportFormat
}
export interface FindQuery {
query: string;
loadUrls?: boolean;
elevation?: boolean;
}
export interface FindOnMapQuery {
query: string;
}
export interface RequestDataMap {
updateBbox: Bbox;
createPad: PadDataCreate;
editPad: PadDataUpdate;
listenToHistory: void;
stopListeningToHistory: void;
revertHistoryEntry: ObjectWithId;
getMarker: ObjectWithId;
addMarker: MarkerCreate;
editMarker: MarkerUpdate;
deleteMarker: ObjectWithId;
getLineTemplate: LineTemplateRequest;
addLine: LineCreate;
editLine: LineUpdate;
deleteLine: ObjectWithId;
exportLine: LineExportRequest;
find: FindQuery;
findOnMap: FindOnMapQuery;
getRoute: RouteCreate;
setRoute: RouteCreate;
clearRoute: void;
lineToRoute: ObjectWithId;
exportRoute: RouteExportRequest;
addType: TypeCreate;
editType: TypeUpdate;
deleteType: ObjectWithId;
addView: ViewCreate;
editView: ViewUpdate;
deleteView: ObjectWithId;
geoip: void;
setPadId: string;
}
export interface ResponseDataMap {
updateBbox: MultipleEvents;
createPad: MultipleEvents;
editPad: PadData;
listenToHistory: MultipleEvents;
stopListeningToHistory: void;
revertHistoryEntry: MultipleEvents;
getMarker: Marker;
addMarker: Marker;
editMarker: Marker;
deleteMarker: Marker;
getLineTemplate: Line;
addLine: Line;
editLine: Line;
deleteLine: Line;
exportLine: string;
find: string | SearchResult[];
findOnMap: Array<Pick<Marker, "id" | "name" | "typeId" | "lat" | "lon"> | Pick<Line, "id" | "name" | "typeId" | "left" | "top" | "right" | "bottom">>;
getRoute: Route;
setRoute: Route;
clearRoute: void;
lineToRoute: Route;
exportRoute: string;
addType: Type;
editType: Type;
deleteType: Type;
addView: View;
editView: View;
deleteView: View;
geoip: Bbox | null;
setPadId: MultipleEvents;
}
export type RequestName = keyof RequestDataMap;
export type RequestData<E extends RequestName> = RequestDataMap[E];
export type ResponseData<E extends RequestName> = ResponseDataMap[E];

62
types/src/type.ts 100644
Wyświetl plik

@ -0,0 +1,62 @@
import { AllOptionalExceptId, Colour, ID, RouteMode, Shape } from "./base";
type ObjectType = "marker" | "line";
type FieldType = "textarea" | "dropdown" | "checkbox" | "input";
type FieldValue<F extends FieldType> = F extends "checkbox" ? boolean : string;
type OptionValue<F extends FieldType> = F extends "checkbox" ? "1" | "0" : string;
type FieldBase<F extends FieldType, O extends ObjectType, isUpdate extends boolean> = {
name: string;
type: F;
controlColour: boolean;
default: FieldValue<F>;
} & (O extends "marker" ? {
controlSize: boolean;
controlSymbol: boolean;
controlShape: boolean;
} : {
controlWidth: boolean;
}) & (F extends "dropdown" | "checkbox" ? {
options: Array<FieldOption<F, O, isUpdate>>
} : { }) & (isUpdate extends true ? {
oldName: string;
} : { });
type FieldOption<F extends FieldType, O extends ObjectType, isUpdate extends boolean> = {
value: OptionValue<F>;
colour?: Colour;
} & (O extends "marker" ? {
size: number;
symbol: Symbol;
shape: Shape;
} : {
width: number;
}) & (isUpdate extends true ? {
oldValue?: OptionValue<F>
} : { });
type Field<O extends ObjectType, isUpdate extends boolean> =
FieldBase<"textarea", O, isUpdate>
| FieldBase<"dropdown", O, isUpdate>
| FieldBase<"checkbox", O, isUpdate>
| FieldBase<"input", O, isUpdate>;
type TypeBase<O extends ObjectType, isUpdate extends boolean> = {
id: ID;
name: string;
defaultColour: Colour | null;
fields: Array<Field<O, isUpdate>>
} & (isUpdate extends false ? {
type: O
} : {}) & (O extends "marker" ? {
defaultSize?: number;
defaultSymbol?: Symbol;
defaultShape?: Shape;
} : {
defaultWidth: number;
defaultMode: RouteMode;
});
export type Type = TypeBase<"marker", false> | TypeBase<"line", false>;
export type TypeCreate = Type;
export type TypeUpdate = AllOptionalExceptId<TypeBase<"marker", true> | TypeBase<"line", true>>;

12
types/src/view.ts 100644
Wyświetl plik

@ -0,0 +1,12 @@
import { AllOptionalExceptId, Bbox, ID, Layer } from "./base";
export interface View extends Bbox {
id: ID;
name: string;
baseLayer: Layer;
layers: Layer[];
filter?: string;
}
export type ViewCreate = View;
export type ViewUpdate = AllOptionalExceptId<View>;

Wyświetl plik

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es3",
"esModuleInterop": true,
"strict": true,
"sourceMap": true,
"declaration": true,
"outDir": "dist",
"moduleResolution": "node"
}
}