incyclist-services 1.0.62 → 1.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/repository/types.d.ts +1 -1
- package/lib/devices/access/service.js +1 -1
- package/lib/devices/pairing/model.d.ts +1 -0
- package/lib/devices/pairing/service.d.ts +5 -0
- package/lib/devices/pairing/service.js +65 -18
- package/lib/routes/base/parsers/incyclist-xml.d.ts +5 -0
- package/lib/routes/base/parsers/incyclist-xml.js +27 -0
- package/lib/routes/base/parsers/incyclistxml.d.ts +0 -0
- package/lib/routes/base/parsers/incyclistxml.js +0 -0
- package/lib/routes/base/parsers/index.js +3 -1
- package/lib/routes/base/parsers/multixml.d.ts +12 -0
- package/lib/routes/base/parsers/multixml.js +37 -0
- package/lib/routes/base/parsers/utils.d.ts +1 -3
- package/lib/routes/base/parsers/utils.js +6 -5
- package/lib/routes/base/parsers/xml.d.ts +19 -4
- package/lib/routes/base/parsers/xml.js +190 -160
- package/lib/routes/base/types/index.d.ts +2 -1
- package/lib/routes/list/service.d.ts +2 -1
- package/lib/routes/list/service.js +43 -23
- package/lib/routes/list/types.d.ts +1 -0
- package/package.json +2 -2
|
@@ -175,7 +175,6 @@ class DeviceAccessService extends events_1.default {
|
|
|
175
175
|
this.connect(name); });
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
console.log('~~~ DEBUG:connect if', ifaceName, this.interfaces[ifaceName]);
|
|
179
178
|
if (((_a = this.interfaces[ifaceName]) === null || _a === void 0 ? void 0 : _a.enabled) === false)
|
|
180
179
|
return;
|
|
181
180
|
const impl = this.getInterface(ifaceName);
|
|
@@ -189,6 +188,7 @@ class DeviceAccessService extends events_1.default {
|
|
|
189
188
|
return true;
|
|
190
189
|
this.interfaces[ifaceName].state = 'connecting';
|
|
191
190
|
this.emit('interface-changed', ifaceName, Object.assign(Object.assign({}, this.interfaces[ifaceName]), { state: 'connecting' }));
|
|
191
|
+
yield impl.disconnect();
|
|
192
192
|
const connected = yield impl.connect();
|
|
193
193
|
const state = connected ? 'connected' : 'disconnected';
|
|
194
194
|
this.interfaces[ifaceName].state = state;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { DeviceAccessService } from "../access/service";
|
|
2
3
|
import { AdapterInfo, CapabilityInformation, DeviceConfigurationInfo, DeviceConfigurationService, IncyclistDeviceSettings, InterfaceSetting } from "../configuration";
|
|
3
4
|
import { CapabilityData, DevicePairingData, DevicePairingStatus, DeviceSelectState, InternalPairingState, PairingProps, PairingSettings, PairingState } from "./model";
|
|
@@ -32,6 +33,7 @@ export declare class DevicePairingService extends IncyclistService {
|
|
|
32
33
|
static getInstance(): DevicePairingService;
|
|
33
34
|
constructor(services?: Services);
|
|
34
35
|
start(onStateChanged: (newState: PairingState) => void): Promise<void>;
|
|
36
|
+
protected loadConfiguration(): Promise<void>;
|
|
35
37
|
protected initConfigHandlers(): void;
|
|
36
38
|
protected removeConfigHandlers(): void;
|
|
37
39
|
stop(): Promise<void>;
|
|
@@ -41,6 +43,7 @@ export declare class DevicePairingService extends IncyclistService {
|
|
|
41
43
|
deleteDevice(capability: IncyclistCapability, udid: string, deleteAll?: boolean): Promise<void>;
|
|
42
44
|
unselectDevices(capability: IncyclistCapability): Promise<void>;
|
|
43
45
|
changeInterfaceSettings(name: string, settings: InterfaceSetting): Promise<void>;
|
|
46
|
+
protected restartPair(): Promise<void>;
|
|
44
47
|
protected restart(): Promise<void>;
|
|
45
48
|
protected _stop(): Promise<void>;
|
|
46
49
|
protected getCapability(capability: IncyclistCapability | CapabilityData): CapabilityData;
|
|
@@ -77,6 +80,7 @@ export declare class DevicePairingService extends IncyclistService {
|
|
|
77
80
|
private stopPairing;
|
|
78
81
|
protected run(props?: PairingProps): Promise<void>;
|
|
79
82
|
private startPairing;
|
|
83
|
+
private isReadyToPair;
|
|
80
84
|
private processConnectedDevices;
|
|
81
85
|
private startScanning;
|
|
82
86
|
protected deregisterScanningDataHandlers(): void;
|
|
@@ -105,6 +109,7 @@ export declare class DevicePairingService extends IncyclistService {
|
|
|
105
109
|
protected deleteCapabilityDevice(capability: IncyclistCapability, udid: string, shouldEmit?: boolean): void;
|
|
106
110
|
protected isScanning(): boolean;
|
|
107
111
|
protected isPairing(): boolean;
|
|
112
|
+
protected isPairingWaiting(): NodeJS.Timeout;
|
|
108
113
|
protected _stopDeviceSelection(changed: boolean): Promise<void>;
|
|
109
114
|
private numberOfSelectedCababilities;
|
|
110
115
|
protected addToDeletedList(capability: IncyclistCapability | CapabilityData, udid: string): void;
|
|
@@ -69,16 +69,7 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
69
69
|
this.emitStateChange(this.state);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
yield this.
|
|
73
|
-
const { capabilities, interfaces } = this.configuration.load();
|
|
74
|
-
const state = Object.assign({}, this.state);
|
|
75
|
-
delete state.adapters;
|
|
76
|
-
this.state.capabilities = this.mappedCapabilities(capabilities);
|
|
77
|
-
this.state.interfaces = this.access.enrichWithAccessState(interfaces);
|
|
78
|
-
this.state.canStartRide = this.configuration.canStartRide();
|
|
79
|
-
this.state.stopRequested = false;
|
|
80
|
-
this.state.stopped = false;
|
|
81
|
-
this.logCapabilities();
|
|
72
|
+
yield this.loadConfiguration();
|
|
82
73
|
this.state.interfaces.forEach(i => {
|
|
83
74
|
if (!this.isInterfaceEnabled(i.name))
|
|
84
75
|
this.unselectOnInterface(i.name);
|
|
@@ -93,6 +84,20 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
93
84
|
}
|
|
94
85
|
});
|
|
95
86
|
}
|
|
87
|
+
loadConfiguration() {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
yield this.waitForInit();
|
|
90
|
+
const { capabilities, interfaces } = this.configuration.load();
|
|
91
|
+
const state = Object.assign({}, this.state);
|
|
92
|
+
delete state.adapters;
|
|
93
|
+
this.state.capabilities = this.mappedCapabilities(capabilities);
|
|
94
|
+
this.state.interfaces = this.access.enrichWithAccessState(interfaces);
|
|
95
|
+
this.state.canStartRide = this.configuration.canStartRide();
|
|
96
|
+
this.state.stopRequested = false;
|
|
97
|
+
this.state.stopped = false;
|
|
98
|
+
this.logCapabilities();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
96
101
|
initConfigHandlers() {
|
|
97
102
|
this.configuration.on('interface-changed', this.onInterfaceConfigChangedHandler);
|
|
98
103
|
this.configuration.on('capability-changed', this.onConfigurationUpdateHandler);
|
|
@@ -242,6 +247,17 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
242
247
|
}
|
|
243
248
|
});
|
|
244
249
|
}
|
|
250
|
+
restartPair() {
|
|
251
|
+
var _a, _b;
|
|
252
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
253
|
+
if (!this.isPairing())
|
|
254
|
+
return;
|
|
255
|
+
if ((_a = this.state.check) === null || _a === void 0 ? void 0 : _a.to)
|
|
256
|
+
clearTimeout((_b = this.state.check) === null || _b === void 0 ? void 0 : _b.to);
|
|
257
|
+
delete this.state.check;
|
|
258
|
+
this.run();
|
|
259
|
+
});
|
|
260
|
+
}
|
|
245
261
|
restart() {
|
|
246
262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
247
263
|
const wasActive = this.isPairing() || this.isScanning();
|
|
@@ -437,6 +453,7 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
437
453
|
this.logEvent({ message: 'interface state changed', interface: ifName, state: ifDetails.state });
|
|
438
454
|
try {
|
|
439
455
|
let restartScan = false;
|
|
456
|
+
let restartPair = false;
|
|
440
457
|
if (interfacesNew) {
|
|
441
458
|
const getData = (i) => ({ name: i.name, enabled: i.enabled, state: i.state });
|
|
442
459
|
if (!prev)
|
|
@@ -449,6 +466,20 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
449
466
|
const changedIdx = prev.findIndex(i => i.name === ifName);
|
|
450
467
|
if (ifDetails.state === 'disconnected' && current.state !== 'disconnected') {
|
|
451
468
|
this.failAdaptersOnInterface(ifName);
|
|
469
|
+
if (this.isPairing()) {
|
|
470
|
+
const pairing = this.getPairingInterfaces();
|
|
471
|
+
if (pairing.includes(ifName)) {
|
|
472
|
+
try {
|
|
473
|
+
current.state = 'disconnected';
|
|
474
|
+
this.emitStateChange({ interfaces: this.state.interfaces });
|
|
475
|
+
yield (0, utils_1.sleep)(1000);
|
|
476
|
+
this.access.connect(ifName);
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
this.logError(err, 'reconnect');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
452
483
|
}
|
|
453
484
|
else if (ifDetails.state === 'unavailable' && current.state !== 'unavailable') {
|
|
454
485
|
this.disableAdaptersOnInterface(ifName);
|
|
@@ -460,12 +491,18 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
460
491
|
else if (ifDetails.state === 'connected' && current.state !== 'connected' && !this.isPairing()) {
|
|
461
492
|
restartScan = true;
|
|
462
493
|
}
|
|
494
|
+
else if (ifDetails.state === 'connected' && current.state !== 'connected' && this.isPairingWaiting()) {
|
|
495
|
+
restartPair = true;
|
|
496
|
+
}
|
|
463
497
|
if (changedIdx !== -1) {
|
|
464
498
|
prev[changedIdx].isScanning = ifDetails.isScanning;
|
|
465
499
|
prev[changedIdx].state = ifDetails.state;
|
|
466
500
|
}
|
|
467
501
|
if (restartScan)
|
|
468
502
|
this.restart();
|
|
503
|
+
if (restartPair) {
|
|
504
|
+
this.restartPair();
|
|
505
|
+
}
|
|
469
506
|
this.emitStateChange({ interfaces: this.state.interfaces });
|
|
470
507
|
}
|
|
471
508
|
}
|
|
@@ -770,16 +807,15 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
770
807
|
const preparing = DevicePairingService.checkCounter++;
|
|
771
808
|
this.state.check = { preparing };
|
|
772
809
|
this.emit('pairing-start');
|
|
773
|
-
const
|
|
774
|
-
const busyRequired = this.state.interfaces
|
|
775
|
-
.filter(i => requiredInterfaces.includes(i.name))
|
|
776
|
-
.find(i => i.enabled && i.state !== 'connected' && i.state !== 'unavailable');
|
|
777
|
-
const isReady = busyRequired === undefined;
|
|
810
|
+
const { isReady, busyRequired } = this.isReadyToPair();
|
|
778
811
|
if (!isReady) {
|
|
779
|
-
|
|
812
|
+
this.logEvent({ message: 'Pairing: waiting for interfaces', interfaces: busyRequired === null || busyRequired === void 0 ? void 0 : busyRequired.name });
|
|
813
|
+
this.state.check.to = setTimeout(() => {
|
|
780
814
|
if ((!this.isPairing() || this.state.check.preparing === preparing) && !this.isScanning()) {
|
|
781
|
-
|
|
782
|
-
|
|
815
|
+
const { isReady } = this.isReadyToPair();
|
|
816
|
+
if (isReady) {
|
|
817
|
+
delete this.state.check;
|
|
818
|
+
}
|
|
783
819
|
}
|
|
784
820
|
}, 1000);
|
|
785
821
|
return;
|
|
@@ -810,6 +846,14 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
810
846
|
}
|
|
811
847
|
});
|
|
812
848
|
}
|
|
849
|
+
isReadyToPair() {
|
|
850
|
+
const requiredInterfaces = this.getPairingInterfaces();
|
|
851
|
+
const busyRequired = this.state.interfaces
|
|
852
|
+
.filter(i => requiredInterfaces.includes(i.name))
|
|
853
|
+
.find(i => i.enabled && i.state !== 'connected' && i.state !== 'unavailable');
|
|
854
|
+
const isReady = busyRequired === undefined;
|
|
855
|
+
return { isReady, busyRequired };
|
|
856
|
+
}
|
|
813
857
|
processConnectedDevices(adapters) {
|
|
814
858
|
const started = adapters.filter(ai => ai.adapter.isStarted());
|
|
815
859
|
started.forEach(ai => {
|
|
@@ -1105,6 +1149,9 @@ class DevicePairingService extends service_2.IncyclistService {
|
|
|
1105
1149
|
var _a;
|
|
1106
1150
|
return this.state.check !== undefined && ((_a = this.state) === null || _a === void 0 ? void 0 : _a.check) !== null;
|
|
1107
1151
|
}
|
|
1152
|
+
isPairingWaiting() {
|
|
1153
|
+
return this.isPairing() && this.state.check.to;
|
|
1154
|
+
}
|
|
1108
1155
|
_stopDeviceSelection(changed) {
|
|
1109
1156
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1110
1157
|
this.deviceSelectState = null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.IncyclistXMLParser = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const xml_1 = require("./xml");
|
|
18
|
+
class IncyclistXMLParser extends xml_1.XMLParser {
|
|
19
|
+
getGPXFileContent(url) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const res = yield axios_1.default.get(url);
|
|
22
|
+
return res.data;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.IncyclistXMLParser = IncyclistXMLParser;
|
|
27
|
+
IncyclistXMLParser.SCHEME = 'gpx-import';
|
|
File without changes
|
|
File without changes
|
|
@@ -17,11 +17,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.useParsers = void 0;
|
|
18
18
|
__exportStar(require("./kwt"), exports);
|
|
19
19
|
const factory_1 = require("./factory");
|
|
20
|
+
const incyclist_xml_1 = require("./incyclist-xml");
|
|
20
21
|
const kwt_1 = require("./kwt");
|
|
22
|
+
const multixml_1 = require("./multixml");
|
|
21
23
|
const useParsers = () => {
|
|
22
24
|
const parsers = factory_1.ParserFactory.getInstance();
|
|
23
25
|
if (!parsers.isInitialized()) {
|
|
24
|
-
parsers.add(new kwt_1.KWTParser
|
|
26
|
+
parsers.add(new multixml_1.MultipleXMLParser([kwt_1.KWTParser, incyclist_xml_1.IncyclistXMLParser]));
|
|
25
27
|
parsers.setInitialized(true);
|
|
26
28
|
}
|
|
27
29
|
return parsers;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { FileInfo, IFileLoader } from "../../../api";
|
|
3
|
+
import { RouteApiDetail } from "../api/types";
|
|
4
|
+
import { ParseResult, Parser } from "../types";
|
|
5
|
+
import { XMLParser } from "./xml";
|
|
6
|
+
export declare class MultipleXMLParser implements Parser<string | Buffer, RouteApiDetail> {
|
|
7
|
+
protected parsers: any;
|
|
8
|
+
constructor(classes: Array<typeof XMLParser>);
|
|
9
|
+
import(file: FileInfo, data: string | Buffer, loader?: IFileLoader): Promise<ParseResult<RouteApiDetail>>;
|
|
10
|
+
supportsExtension(extension: string): boolean;
|
|
11
|
+
supportsContent(): boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MultipleXMLParser = void 0;
|
|
13
|
+
const xml_1 = require("../utils/xml");
|
|
14
|
+
class MultipleXMLParser {
|
|
15
|
+
constructor(classes) {
|
|
16
|
+
this.parsers = [];
|
|
17
|
+
classes.forEach(C => {
|
|
18
|
+
const parser = new C();
|
|
19
|
+
this.parsers.push(parser);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
import(file, data, loader) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const str = Buffer.isBuffer(data) ? data.toString() : data;
|
|
25
|
+
const xmlJson = yield (0, xml_1.parseXml)(str);
|
|
26
|
+
const parser = this.parsers.find(p => p.supportsContent(xmlJson));
|
|
27
|
+
return yield parser.import(file, xmlJson, loader);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
supportsExtension(extension) {
|
|
31
|
+
return (extension === null || extension === void 0 ? void 0 : extension.toLowerCase()) === 'xml';
|
|
32
|
+
}
|
|
33
|
+
supportsContent() {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.MultipleXMLParser = MultipleXMLParser;
|
|
@@ -4,6 +4,4 @@ export declare const addVideoSpeed: (points: Array<RoutePoint>, video: any) => A
|
|
|
4
4
|
export declare const getReferencedFileInfo: (info: FileInfo, referenced: {
|
|
5
5
|
file?: string;
|
|
6
6
|
url?: string;
|
|
7
|
-
}, scheme?: string) =>
|
|
8
|
-
url: string;
|
|
9
|
-
};
|
|
7
|
+
}, scheme?: string) => string;
|
|
@@ -35,23 +35,23 @@ const addVideoSpeed = (points, video) => {
|
|
|
35
35
|
exports.addVideoSpeed = addVideoSpeed;
|
|
36
36
|
const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
|
|
37
37
|
if (info.type !== 'url')
|
|
38
|
-
return;
|
|
38
|
+
return info.url;
|
|
39
39
|
const target = {};
|
|
40
40
|
let fileName = referenced.file;
|
|
41
41
|
if (referenced.url) {
|
|
42
|
-
return
|
|
42
|
+
return referenced.url;
|
|
43
43
|
}
|
|
44
44
|
if (fileName) {
|
|
45
45
|
const inputUrl = info.url;
|
|
46
46
|
const regex = /(\\|\/)/g;
|
|
47
47
|
if (fileName.startsWith('http://') || fileName.startsWith('https://')) {
|
|
48
|
-
return
|
|
48
|
+
return fileName;
|
|
49
49
|
}
|
|
50
50
|
else if (fileName.search(regex) === -1) {
|
|
51
51
|
if (inputUrl.startsWith('incyclist:') || inputUrl.startsWith('file:')) {
|
|
52
52
|
const parts = inputUrl.split('://');
|
|
53
53
|
const targetPath = parts[1].replace(info.name, fileName);
|
|
54
|
-
return
|
|
54
|
+
return `${scheme}://${targetPath}`;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
else {
|
|
@@ -61,8 +61,9 @@ const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
|
|
|
61
61
|
target.url = `${scheme}:///${fileName}`;
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
|
-
|
|
64
|
+
target.url = `${scheme}:///${fileName}`;
|
|
65
65
|
}
|
|
66
|
+
return target.url;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
};
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { XmlJSON } from '../utils/xml';
|
|
2
2
|
import { RouteApiDetail } from '../api/types';
|
|
3
|
-
import { ParseResult, Parser } from '../types';
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { ParseResult, Parser, RouteInfo } from '../types';
|
|
4
|
+
import { FileInfo, IFileLoader, JSONObject } from '../../../api';
|
|
5
|
+
export type XmlParserContext = {
|
|
6
|
+
fileInfo: FileInfo;
|
|
7
|
+
data: JSONObject;
|
|
8
|
+
route?: RouteApiDetail;
|
|
9
|
+
};
|
|
10
|
+
export declare class XMLParser implements Parser<XmlJSON, RouteApiDetail> {
|
|
11
|
+
protected loader: IFileLoader;
|
|
12
|
+
import(file: FileInfo, xml: XmlJSON, loader?: IFileLoader): Promise<ParseResult<RouteApiDetail>>;
|
|
6
13
|
getSupportedSheme(): string;
|
|
7
14
|
supportsExtension(extension: string): boolean;
|
|
8
15
|
supportsContent(xmljson: XmlJSON): boolean;
|
|
9
|
-
protected
|
|
16
|
+
protected loadDescription(context: XmlParserContext): Promise<void>;
|
|
17
|
+
protected parse(file: FileInfo, xmljson: XmlJSON): Promise<ParseResult<RouteApiDetail>>;
|
|
18
|
+
protected buildInfo(context: XmlParserContext): Promise<RouteInfo>;
|
|
19
|
+
parseVideo(context: XmlParserContext): Promise<void>;
|
|
20
|
+
loadElevationFromAltitudes(context: XmlParserContext, tagName?: string): Promise<void>;
|
|
21
|
+
loadElevationFromPositions(context: XmlParserContext, tags?: {
|
|
22
|
+
altitudes?: string;
|
|
23
|
+
positions?: string;
|
|
24
|
+
}): Promise<void>;
|
|
10
25
|
}
|
|
@@ -13,12 +13,14 @@ exports.XMLParser = void 0;
|
|
|
13
13
|
const gd_eventlog_1 = require("gd-eventlog");
|
|
14
14
|
const xml_1 = require("../utils/xml");
|
|
15
15
|
const route_1 = require("../utils/route");
|
|
16
|
+
const utils_1 = require("./utils");
|
|
16
17
|
let _logger;
|
|
17
18
|
class XMLParser {
|
|
18
|
-
import(xml) {
|
|
19
|
+
import(file, xml, loader) {
|
|
19
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
21
|
xml.expectScheme(this.getSupportedSheme());
|
|
21
|
-
|
|
22
|
+
this.loader = loader;
|
|
23
|
+
return yield this.parse(file, xml);
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
getSupportedSheme() {
|
|
@@ -30,174 +32,202 @@ class XMLParser {
|
|
|
30
32
|
}
|
|
31
33
|
supportsContent(xmljson) {
|
|
32
34
|
const json = xmljson.json;
|
|
33
|
-
|
|
35
|
+
const scheme = this.getSupportedSheme();
|
|
36
|
+
return json[scheme] !== undefined && json[scheme] !== null;
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
loadElevationFromAltitudes(data, route);
|
|
55
|
-
}
|
|
56
|
-
parseVideo(data, route);
|
|
57
|
-
const res = {
|
|
58
|
-
data: buildInfo(route),
|
|
59
|
-
details: route
|
|
60
|
-
};
|
|
61
|
-
return res;
|
|
38
|
+
loadDescription(context) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const data = context.data;
|
|
41
|
+
context.route = {
|
|
42
|
+
title: data['name'],
|
|
43
|
+
localizedTitle: data['title'] || data['name'],
|
|
44
|
+
country: data['country'],
|
|
45
|
+
id: data['id'],
|
|
46
|
+
previewUrl: data['previewURL'],
|
|
47
|
+
distance: 0,
|
|
48
|
+
elevation: 0,
|
|
49
|
+
points: [],
|
|
50
|
+
description: data['description1']
|
|
51
|
+
};
|
|
52
|
+
if (typeof context.route.localizedTitle === 'string') {
|
|
53
|
+
const lt = context.route.localizedTitle;
|
|
54
|
+
context.route.localizedTitle = { en: lt };
|
|
55
|
+
}
|
|
56
|
+
});
|
|
62
57
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (prev.dpf !== undefined && mapping.frame !== undefined)
|
|
123
|
-
return prev.dpf * (mapping.frame - prevFrame) + prevDist;
|
|
124
|
-
throw new Error(`mapping #${idx || 'total'}: one of [distance], [dpf or frame] is missing: <mapping ${(0, xml_1.toXml)(mapping)}/>`);
|
|
58
|
+
parse(file, xmljson) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
const data = xmljson.json;
|
|
61
|
+
const context = { fileInfo: file, data };
|
|
62
|
+
yield this.loadDescription(context);
|
|
63
|
+
const positions = data.positions;
|
|
64
|
+
if ((positions === null || positions === void 0 ? void 0 : positions.length) > 0) {
|
|
65
|
+
yield this.loadElevationFromPositions(context);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
yield this.loadElevationFromAltitudes(context);
|
|
69
|
+
}
|
|
70
|
+
yield this.parseVideo(context);
|
|
71
|
+
const res = {
|
|
72
|
+
data: yield this.buildInfo(context),
|
|
73
|
+
details: context.route
|
|
74
|
+
};
|
|
75
|
+
return res;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
buildInfo(context) {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const { fileInfo, route } = context;
|
|
82
|
+
const info = {
|
|
83
|
+
id: route.id,
|
|
84
|
+
title: route.title,
|
|
85
|
+
localizedTitle: route.localizedTitle,
|
|
86
|
+
category: route.category,
|
|
87
|
+
country: route.country,
|
|
88
|
+
distance: route.distance,
|
|
89
|
+
elevation: route.elevation,
|
|
90
|
+
points: route.points,
|
|
91
|
+
segments: (_a = route.video) === null || _a === void 0 ? void 0 : _a.selectableSegments,
|
|
92
|
+
requiresDownload: false,
|
|
93
|
+
hasGpx: ((_b = route.points) === null || _b === void 0 ? void 0 : _b.length) > 0,
|
|
94
|
+
hasVideo: true,
|
|
95
|
+
isDemo: false,
|
|
96
|
+
isLocal: true,
|
|
97
|
+
isLoop: (0, route_1.checkIsLoop)(route.points),
|
|
98
|
+
videoFormat: route.video.format,
|
|
99
|
+
videoUrl: getVideoUrl(fileInfo, route),
|
|
100
|
+
previewUrl: getPreviewUrl(fileInfo, route)
|
|
101
|
+
};
|
|
102
|
+
return info;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
parseVideo(context) {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
const { data, route } = context;
|
|
108
|
+
route.video = {
|
|
109
|
+
file: data['video-file-path'],
|
|
110
|
+
url: undefined,
|
|
111
|
+
framerate: parseFloat(data['framerate']),
|
|
112
|
+
next: data['next-video'],
|
|
113
|
+
mappings: [],
|
|
114
|
+
format: undefined,
|
|
115
|
+
selectableSegments: data['segments'],
|
|
116
|
+
infoTexts: data['informations'],
|
|
125
117
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
118
|
+
console.log(route.video);
|
|
119
|
+
const fileParts = route.video.file.split('.');
|
|
120
|
+
const extension = fileParts[fileParts.length - 1];
|
|
121
|
+
route.video.format = extension.toLowerCase();
|
|
122
|
+
const mappings = data['mappings'];
|
|
123
|
+
if (mappings) {
|
|
124
|
+
let prev;
|
|
125
|
+
let prevTime = 0;
|
|
126
|
+
const startFrame = parseInt(data['start-frame'] || 0);
|
|
127
|
+
const endFrame = data['end-frame'] ? parseInt(data['end-frame']) : undefined;
|
|
128
|
+
try {
|
|
129
|
+
const getDistance = (mapping, prevMapping, idx) => {
|
|
130
|
+
const prevDist = prevMapping.distance || 0;
|
|
131
|
+
const prevFrame = prevMapping.frame || startFrame;
|
|
132
|
+
if (mapping.distance !== undefined)
|
|
133
|
+
return mapping.distance;
|
|
134
|
+
if (prev.dpf !== undefined && mapping.frame !== undefined)
|
|
135
|
+
return prev.dpf * (mapping.frame - prevFrame) + prevDist;
|
|
136
|
+
throw new Error(`mapping #${idx || 'total'}: one of [distance], [dpf or frame] is missing: <mapping ${(0, xml_1.toXml)(mapping)}/>`);
|
|
137
|
+
};
|
|
138
|
+
mappings.forEach((mapping, idx) => {
|
|
139
|
+
if (idx !== 0) {
|
|
140
|
+
mapping.distance = (mapping.distance !== undefined && mapping.distance !== null) ? parseInt(mapping.distance) : undefined;
|
|
141
|
+
mapping.dpf = (mapping.dpf !== undefined && mapping.dpf !== null) ? parseFloat(mapping.dpf) : undefined;
|
|
142
|
+
mapping.frame = (mapping.frame !== undefined && mapping.frame !== null) ? parseInt(mapping.frame) : undefined;
|
|
143
|
+
const distance = getDistance(mapping, prev, idx);
|
|
144
|
+
route.distance = mapping.distance = distance;
|
|
145
|
+
const frames = mapping.frame - (prev.frame || startFrame);
|
|
146
|
+
const videoSpeed = (prev.distance === undefined && mapping.dpf !== undefined) ? 3.6 * mapping.dpf * route.video.framerate : 3.6 * (distance - prev.distance) / frames * route.video.framerate;
|
|
147
|
+
const time = prevTime + frames / route.video.framerate;
|
|
148
|
+
route.video.mappings.push(Object.assign({ videoSpeed, time: prevTime }, prev));
|
|
149
|
+
prev = mapping;
|
|
150
|
+
prevTime = time;
|
|
151
|
+
if (idx === mappings.length - 1) {
|
|
152
|
+
route.video.mappings.push(Object.assign({ videoSpeed, time }, mapping));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
mapping.distance = (mapping.distance !== undefined && mapping.distance !== null) ? parseInt(mapping.distance) : undefined;
|
|
157
|
+
mapping.dpf = (mapping.dpf !== undefined && mapping.dpf !== null) ? parseFloat(mapping.dpf) : undefined;
|
|
158
|
+
mapping.frame = (mapping.frame !== undefined && mapping.frame !== null) ? parseInt(mapping.frame) : undefined;
|
|
159
|
+
prev = mapping;
|
|
160
|
+
prevTime = 0;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
if (endFrame && prev.dpf !== undefined) {
|
|
164
|
+
const mapping = { frame: endFrame, dpf: prev.dpf };
|
|
165
|
+
route.distance = getDistance(mapping, prev);
|
|
141
166
|
}
|
|
142
167
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
prevTime = 0;
|
|
168
|
+
catch (err) {
|
|
169
|
+
if (!_logger)
|
|
170
|
+
_logger = new gd_eventlog_1.EventLogger('XmlParser');
|
|
171
|
+
_logger.logEvent({ message: 'xml details', error: err.message, mappings });
|
|
172
|
+
throw new Error('Could not parse XML File');
|
|
149
173
|
}
|
|
150
|
-
});
|
|
151
|
-
if (endFrame && prev.dpf !== undefined) {
|
|
152
|
-
const mapping = { frame: endFrame, dpf: prev.dpf };
|
|
153
|
-
route.distance = getDistance(mapping, prev);
|
|
154
174
|
}
|
|
155
|
-
}
|
|
156
|
-
catch (err) {
|
|
157
|
-
if (!_logger)
|
|
158
|
-
_logger = new gd_eventlog_1.EventLogger('XmlParser');
|
|
159
|
-
_logger.logEvent({ message: 'xml details', error: err.message, mappings });
|
|
160
|
-
throw new Error('Could not parse XML File');
|
|
161
|
-
}
|
|
175
|
+
});
|
|
162
176
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
loadElevationFromAltitudes(context, tagName = 'altitudes') {
|
|
178
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
179
|
+
const { data, route } = context;
|
|
180
|
+
const altitudes = data[tagName];
|
|
181
|
+
if (!altitudes)
|
|
182
|
+
return;
|
|
183
|
+
route.points = altitudes.map(a => ({
|
|
184
|
+
routeDistance: Number(a.distance),
|
|
185
|
+
elevation: Number(a.height),
|
|
186
|
+
}));
|
|
187
|
+
const points = route.points;
|
|
188
|
+
if ((points === null || points === void 0 ? void 0 : points.length) > 0) {
|
|
189
|
+
route.distance = points[points.length - 1].routeDistance;
|
|
190
|
+
}
|
|
191
|
+
(0, route_1.updateSlopes)(points);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
loadElevationFromPositions(context, tags) {
|
|
195
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
const { data, route } = context;
|
|
197
|
+
const altitudes = data[(tags === null || tags === void 0 ? void 0 : tags.altitudes) || 'altitudes'];
|
|
198
|
+
const positions = data[(tags === null || tags === void 0 ? void 0 : tags.positions) || 'positions'];
|
|
199
|
+
if (!positions)
|
|
200
|
+
return;
|
|
201
|
+
let prevAltitude = undefined;
|
|
202
|
+
let prevDistance = 0;
|
|
203
|
+
route.elevation = 0;
|
|
204
|
+
const points = [];
|
|
205
|
+
positions.forEach((pos, i) => {
|
|
206
|
+
const altitude = getAltitude(altitudes, positions, i, prevAltitude);
|
|
207
|
+
const elevationGain = altitude - prevAltitude;
|
|
208
|
+
const pi = createPoint(pos, altitude, prevDistance);
|
|
209
|
+
points.push(pi.point);
|
|
210
|
+
route.distance = pi.point.routeDistance;
|
|
211
|
+
if (elevationGain > 0)
|
|
212
|
+
route.elevation += elevationGain;
|
|
213
|
+
prevDistance = pi.prevDistance;
|
|
214
|
+
prevAltitude = altitude;
|
|
215
|
+
});
|
|
216
|
+
route.points = points;
|
|
217
|
+
route.distance = prevDistance;
|
|
218
|
+
(0, route_1.updateSlopes)(route.points);
|
|
219
|
+
});
|
|
175
220
|
}
|
|
176
|
-
|
|
221
|
+
}
|
|
222
|
+
exports.XMLParser = XMLParser;
|
|
223
|
+
const getVideoUrl = (info, route) => {
|
|
224
|
+
const { file, url } = (route === null || route === void 0 ? void 0 : route.video) || {};
|
|
225
|
+
return (0, utils_1.getReferencedFileInfo)(info, { file, url }, 'video');
|
|
177
226
|
};
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
return;
|
|
183
|
-
let prevAltitude = undefined;
|
|
184
|
-
let prevDistance = 0;
|
|
185
|
-
route.elevation = 0;
|
|
186
|
-
const points = [];
|
|
187
|
-
positions.forEach((pos, i) => {
|
|
188
|
-
const altitude = getAltitude(altitudes, positions, i, prevAltitude);
|
|
189
|
-
const elevationGain = altitude - prevAltitude;
|
|
190
|
-
const pi = createPoint(pos, altitude, prevDistance);
|
|
191
|
-
points.push(pi.point);
|
|
192
|
-
route.distance = pi.point.routeDistance;
|
|
193
|
-
if (elevationGain > 0)
|
|
194
|
-
route.elevation += elevationGain;
|
|
195
|
-
prevDistance = pi.prevDistance;
|
|
196
|
-
prevAltitude = altitude;
|
|
197
|
-
});
|
|
198
|
-
route.points = points;
|
|
199
|
-
route.distance = prevDistance;
|
|
200
|
-
(0, route_1.updateSlopes)(route.points);
|
|
227
|
+
const getPreviewUrl = (info, route) => {
|
|
228
|
+
const url = route === null || route === void 0 ? void 0 : route.previewUrl;
|
|
229
|
+
const file = route === null || route === void 0 ? void 0 : route.previewUrlLocal;
|
|
230
|
+
return (0, utils_1.getReferencedFileInfo)(info, { file, url }, 'file');
|
|
201
231
|
};
|
|
202
232
|
function createPoint(pos, altitude, prevDistance) {
|
|
203
233
|
const point = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FileInfo, IFileLoader } from "../../../api";
|
|
1
2
|
export type RouteType = 'gpx' | 'video';
|
|
2
3
|
export type RouteCategory = 'Free' | 'Demo' | 'personal';
|
|
3
4
|
export type RouteState = 'prepared' | 'loading' | 'loaded' | 'error';
|
|
@@ -79,7 +80,7 @@ export interface ParseResult<T extends RouteBase> {
|
|
|
79
80
|
details: T;
|
|
80
81
|
}
|
|
81
82
|
export interface Parser<In, Out extends RouteBase> {
|
|
82
|
-
import(data: In): Promise<ParseResult<Out>>;
|
|
83
|
+
import(file: FileInfo, data: In, loader?: IFileLoader): Promise<ParseResult<Out>>;
|
|
83
84
|
supportsExtension(extension: string): boolean;
|
|
84
85
|
supportsContent(data: In): boolean;
|
|
85
86
|
}
|
|
@@ -72,7 +72,7 @@ export declare class RouteListService extends IncyclistService {
|
|
|
72
72
|
protected initRouteLists(): void;
|
|
73
73
|
protected getPageState(pageId: string, lang?: string): RouteListData;
|
|
74
74
|
protected registerPage(pageId: string, props: RouteListStartProps): Page;
|
|
75
|
-
protected sort(routes: RouteInfo[] | Route[]): void;
|
|
75
|
+
protected sort(list: List, routes: RouteInfo[] | Route[]): void;
|
|
76
76
|
protected getRouteListDataEntry(list: List, entry: Array<Route>, language: string, listHeader: string): {
|
|
77
77
|
list: List;
|
|
78
78
|
listHeader: string;
|
|
@@ -96,6 +96,7 @@ export declare class RouteListService extends IncyclistService {
|
|
|
96
96
|
protected addDescriptionsFromDB(descriptions: RoutesDB): void;
|
|
97
97
|
protected addDescriptionsFromRepo(descriptions: Array<RouteApiDescription>, repo?: JsonRepository): void;
|
|
98
98
|
protected addDescriptionsFromServer(descriptions: Array<RouteApiDescription>): void;
|
|
99
|
+
protected addDescriptionFromFile(data: RouteInfo, details: RouteApiDetail): void;
|
|
99
100
|
protected updateRouteTitle(data: RouteInfo, route: {
|
|
100
101
|
descr?: RouteApiDescription;
|
|
101
102
|
}): void;
|
|
@@ -216,30 +216,23 @@ class RouteListService extends service_1.IncyclistService {
|
|
|
216
216
|
const parsers = (0, parsers_1.useParsers)();
|
|
217
217
|
files.forEach((file) => __awaiter(this, void 0, void 0, function* () {
|
|
218
218
|
try {
|
|
219
|
-
const { data, error } = yield this.loader.open(file);
|
|
219
|
+
const { data: content, error } = yield this.loader.open(file);
|
|
220
220
|
if (error) {
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
|
-
console.log('~~~ DATA',
|
|
224
|
-
const parser = parsers.findMatching(file.ext,
|
|
225
|
-
const
|
|
226
|
-
|
|
223
|
+
console.log('~~~ DATA', content);
|
|
224
|
+
const parser = parsers.findMatching(file.ext, content);
|
|
225
|
+
const res = yield parser.import(file, content);
|
|
226
|
+
const data = res.data;
|
|
227
|
+
data.state = 'loaded';
|
|
228
|
+
const details = res.data;
|
|
229
|
+
this.addDescriptionFromFile(data, details);
|
|
230
|
+
console.log('~~~ ROUTES', this.getRouteList('myRoutes'));
|
|
227
231
|
}
|
|
228
232
|
catch (err) {
|
|
229
233
|
console.log(err);
|
|
230
234
|
}
|
|
231
235
|
}));
|
|
232
|
-
const route = {
|
|
233
|
-
id: Date.now().toString(),
|
|
234
|
-
data: {
|
|
235
|
-
type: 'route',
|
|
236
|
-
id: Date.now().toString(),
|
|
237
|
-
state: 'prepared',
|
|
238
|
-
title: 'Test'
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
this.addRouteToList('myRoutes', route);
|
|
242
|
-
this.emitPageUpdate();
|
|
243
236
|
}
|
|
244
237
|
setCardUpdateHandler(pageId, r, list, idx, onRouteStateChanged, onCarouselStateChanged) {
|
|
245
238
|
const route = this.getRouteFromStartProps(r);
|
|
@@ -362,7 +355,6 @@ class RouteListService extends service_1.IncyclistService {
|
|
|
362
355
|
const promises = [];
|
|
363
356
|
const rle = this.lists.find(l => l.list === list);
|
|
364
357
|
if (((_a = rle.routes) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
365
|
-
this.sort(rle.routes);
|
|
366
358
|
let cntApi = 0;
|
|
367
359
|
rle.routes.forEach((r) => {
|
|
368
360
|
if (!r.data.isLocal) {
|
|
@@ -491,27 +483,36 @@ class RouteListService extends service_1.IncyclistService {
|
|
|
491
483
|
this.state.pages.push(page);
|
|
492
484
|
return page;
|
|
493
485
|
}
|
|
494
|
-
sort(routes) {
|
|
486
|
+
sort(list, routes) {
|
|
495
487
|
const isRouteArray = (a) => a[0].data !== undefined;
|
|
496
488
|
let val = (route) => route;
|
|
497
489
|
if (isRouteArray(routes)) {
|
|
498
|
-
val = (route) => route.data;
|
|
490
|
+
val = (route) => (Object.assign(Object.assign({}, route.data), { type: route.type, created: route.created }));
|
|
499
491
|
}
|
|
500
492
|
const score = (route) => {
|
|
493
|
+
if (route.type === 'import')
|
|
494
|
+
return 1000;
|
|
495
|
+
if (route.type === 'free-ride')
|
|
496
|
+
return 1.2;
|
|
501
497
|
let val = 0;
|
|
498
|
+
if (route.created) {
|
|
499
|
+
const tDelta = (Date.now() - route.created) / 1000;
|
|
500
|
+
const DAY = 60 * 60 * 24;
|
|
501
|
+
val += Math.min((1 - (DAY - tDelta) / DAY), 1);
|
|
502
|
+
}
|
|
502
503
|
if (route.hasVideo)
|
|
503
|
-
val +=
|
|
504
|
+
val += 1.000;
|
|
504
505
|
if (route.hasGpx)
|
|
505
|
-
val += 100;
|
|
506
|
+
val += 0.100;
|
|
506
507
|
if (route.isDemo)
|
|
507
|
-
val -= 150;
|
|
508
|
+
val -= 0.150;
|
|
508
509
|
return val;
|
|
509
510
|
};
|
|
510
511
|
routes.sort((a, b) => score(val(b)) - score(val(a)));
|
|
511
512
|
}
|
|
512
513
|
getRouteListDataEntry(list, entry, language, listHeader) {
|
|
513
514
|
const routes = entry.map(r => (0, localization_1.getLocalizedData)(r.data, language));
|
|
514
|
-
this.sort(routes);
|
|
515
|
+
this.sort(list, routes);
|
|
515
516
|
return { list, listHeader, routes };
|
|
516
517
|
}
|
|
517
518
|
getPage(pageId, byIndex) {
|
|
@@ -702,6 +703,25 @@ class RouteListService extends service_1.IncyclistService {
|
|
|
702
703
|
}
|
|
703
704
|
});
|
|
704
705
|
}
|
|
706
|
+
addDescriptionFromFile(data, details) {
|
|
707
|
+
const existing = this.getRoute(data.id);
|
|
708
|
+
if (existing) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const list = 'myRoutes';
|
|
712
|
+
const local = this.getRouteDescription(list, data.id);
|
|
713
|
+
if (local) {
|
|
714
|
+
this.mergeDescription(local, data);
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
data.state = 'loaded';
|
|
718
|
+
data.isLocal = true;
|
|
719
|
+
const item = { id: data.id, data, details, created: Date.now() };
|
|
720
|
+
this.addRouteToList(list, item);
|
|
721
|
+
this.routeDescriptions[data.id] = Object.assign(Object.assign({}, data), { list });
|
|
722
|
+
this.emitPageUpdate();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
705
725
|
updateRouteTitle(data, route) {
|
|
706
726
|
const { descr } = route;
|
|
707
727
|
if (descr && !data.title) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incyclist-services",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.63",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"gd-eventlog": "^0.1.26"
|
|
6
6
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"axios": "^1.6.1",
|
|
42
|
-
"incyclist-devices": "
|
|
42
|
+
"incyclist-devices": "file:../devices",
|
|
43
43
|
"uuid": "^9.0.0",
|
|
44
44
|
"xml2js": "^0.6.2"
|
|
45
45
|
}
|