@powfix/core-js 0.9.22
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/browser.ts +73 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.js +58 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/dist/src/constants/COORDINATE.d.ts +7 -0
- package/dist/src/constants/COORDINATE.js +10 -0
- package/dist/src/constants/DISTANCE.d.ts +13 -0
- package/dist/src/constants/DISTANCE.js +18 -0
- package/dist/src/constants/DURATION.d.ts +16 -0
- package/dist/src/constants/DURATION.js +21 -0
- package/dist/src/interfaces/Coordinate.d.ts +8 -0
- package/dist/src/interfaces/Coordinate.js +2 -0
- package/dist/src/interfaces/Point2.d.ts +4 -0
- package/dist/src/interfaces/Point2.js +2 -0
- package/dist/src/interfaces/Point3.d.ts +4 -0
- package/dist/src/interfaces/Point3.js +2 -0
- package/dist/src/scripts/base64-polyfill.d.ts +1 -0
- package/dist/src/scripts/base64-polyfill.js +14 -0
- package/dist/src/services/Session.d.ts +27 -0
- package/dist/src/services/Session.js +140 -0
- package/dist/src/services/browser.d.ts +2 -0
- package/dist/src/services/browser.js +20 -0
- package/dist/src/services/index.d.ts +2 -0
- package/dist/src/services/index.js +19 -0
- package/dist/src/services/redis/RedisClient.d.ts +20 -0
- package/dist/src/services/redis/RedisClient.js +70 -0
- package/dist/src/services/redis/RedisPublisher.d.ts +13 -0
- package/dist/src/services/redis/RedisPublisher.js +61 -0
- package/dist/src/services/redis/RedisSubscriber.d.ts +11 -0
- package/dist/src/services/redis/RedisSubscriber.js +68 -0
- package/dist/src/services/redis/index.d.ts +3 -0
- package/dist/src/services/redis/index.js +20 -0
- package/dist/src/services/time/TimeService.d.ts +64 -0
- package/dist/src/services/time/TimeService.js +235 -0
- package/dist/src/services/time/index.d.ts +1 -0
- package/dist/src/services/time/index.js +17 -0
- package/dist/src/types/IntRage.d.ts +3 -0
- package/dist/src/types/IntRage.js +2 -0
- package/dist/src/utils/ArrayUtils.d.ts +10 -0
- package/dist/src/utils/ArrayUtils.js +20 -0
- package/dist/src/utils/BooleanUtils.d.ts +1 -0
- package/dist/src/utils/BooleanUtils.js +9 -0
- package/dist/src/utils/CoordinateUtils.d.ts +8 -0
- package/dist/src/utils/CoordinateUtils.js +42 -0
- package/dist/src/utils/DateUtils.d.ts +12 -0
- package/dist/src/utils/DateUtils.js +212 -0
- package/dist/src/utils/JuminNumberUtils.d.ts +4 -0
- package/dist/src/utils/JuminNumberUtils.js +50 -0
- package/dist/src/utils/NumberUtils.d.ts +4 -0
- package/dist/src/utils/NumberUtils.js +25 -0
- package/dist/src/utils/Point3Utils.d.ts +4 -0
- package/dist/src/utils/Point3Utils.js +12 -0
- package/dist/src/utils/RandomUtils.d.ts +8 -0
- package/dist/src/utils/RandomUtils.js +64 -0
- package/dist/src/utils/Sequencer.d.ts +39 -0
- package/dist/src/utils/Sequencer.js +148 -0
- package/dist/src/utils/StringUtils.d.ts +5 -0
- package/dist/src/utils/StringUtils.js +37 -0
- package/dist/src/utils/UuidUtils.d.ts +14 -0
- package/dist/src/utils/UuidUtils.js +49 -0
- package/dist/src/utils/Validator.d.ts +48 -0
- package/dist/src/utils/Validator.js +118 -0
- package/dist/src/utils/global/between.d.ts +1 -0
- package/dist/src/utils/global/between.js +7 -0
- package/dist/src/utils/global/sleep.d.ts +1 -0
- package/dist/src/utils/global/sleep.js +21 -0
- package/index.ts +5 -0
- package/package.json +42 -0
- package/src/constants/COORDINATE.ts +9 -0
- package/src/constants/DISTANCE.ts +15 -0
- package/src/constants/DURATION.ts +18 -0
- package/src/interfaces/Coordinate.ts +9 -0
- package/src/interfaces/Point2.ts +4 -0
- package/src/interfaces/Point3.ts +5 -0
- package/src/scripts/base64-polyfill.ts +13 -0
- package/src/services/Session.ts +141 -0
- package/src/services/browser.ts +5 -0
- package/src/services/index.ts +4 -0
- package/src/services/redis/RedisClient.ts +79 -0
- package/src/services/redis/RedisPublisher.ts +48 -0
- package/src/services/redis/RedisSubscriber.ts +49 -0
- package/src/services/redis/index.ts +4 -0
- package/src/services/time/TimeService.ts +304 -0
- package/src/services/time/index.ts +1 -0
- package/src/types/IntRage.ts +5 -0
- package/src/utils/ArrayUtils.ts +25 -0
- package/src/utils/BooleanUtils.ts +4 -0
- package/src/utils/CoordinateUtils.ts +50 -0
- package/src/utils/DateUtils.ts +213 -0
- package/src/utils/JuminNumberUtils.ts +47 -0
- package/src/utils/NumberUtils.ts +23 -0
- package/src/utils/Point3Utils.ts +11 -0
- package/src/utils/RandomUtils.ts +62 -0
- package/src/utils/Sequencer.ts +178 -0
- package/src/utils/StringUtils.ts +43 -0
- package/src/utils/UuidUtils.ts +46 -0
- package/src/utils/Validator.ts +162 -0
- package/src/utils/global/between.ts +3 -0
- package/src/utils/global/sleep.ts +6 -0
- package/tsconfig.json +103 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {RedisClient} from "./RedisClient";
|
|
2
|
+
import {PubSubListener} from "@redis/client/dist/lib/client/pub-sub";
|
|
3
|
+
|
|
4
|
+
export class RedisSubscriber extends RedisClient {
|
|
5
|
+
public constructor(options?: RedisClient.RedisClientOptions) {
|
|
6
|
+
super(options);
|
|
7
|
+
console.log(Date.now(), 'Subscriber', 'initialized');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public subscribe = async <T extends boolean = false>(channels: string | string[], listener: PubSubListener<T>, bufferMode?: T | undefined) => {
|
|
11
|
+
for (const channel of Array.isArray(channels) ? channels : [channels]) {
|
|
12
|
+
if ((/\*/g).test(channel)) {
|
|
13
|
+
await this.client.pSubscribe(channel, listener, bufferMode);
|
|
14
|
+
} else {
|
|
15
|
+
await this.client.subscribe(channel, listener, bufferMode);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
public unsubscribe = async <T extends boolean = false>(channels: string | string[], listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined): Promise<void> => {
|
|
21
|
+
for (const channel of Array.isArray(channels) ? channels : [channels]) {
|
|
22
|
+
if ((/\*/g).test(channel)) {
|
|
23
|
+
await this.client.pUnsubscribe(channel, listener, bufferMode);
|
|
24
|
+
} else {
|
|
25
|
+
await this.client.unsubscribe(channel, listener, bufferMode);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
public async start(): Promise<RedisClient.Status> {
|
|
31
|
+
const status = await super.start();
|
|
32
|
+
await this.registerListeners();
|
|
33
|
+
return status;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async stop(): Promise<RedisClient.Status> {
|
|
37
|
+
const status = await super.stop();
|
|
38
|
+
await this.unregisterListeners();
|
|
39
|
+
return status;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected async registerListeners() {
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected async unregisterListeners() {
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import EventEmitter from "events";
|
|
2
|
+
|
|
3
|
+
const LOG_TAG: string = 'TimeService';
|
|
4
|
+
|
|
5
|
+
export class TimeService {
|
|
6
|
+
protected status: TimeService.Status = TimeService.Status.STOPPED;
|
|
7
|
+
private offset?: TimeService.Offset | undefined;
|
|
8
|
+
private option: TimeService.Option;
|
|
9
|
+
private syncedAt?: TimeService.TimeStamp | undefined;
|
|
10
|
+
|
|
11
|
+
// Emitter
|
|
12
|
+
private emitter = new EventEmitter({});
|
|
13
|
+
public readonly on = this.emitter.on;
|
|
14
|
+
public readonly off = this.emitter.off;
|
|
15
|
+
private readonly emit = this.emitter.emit;
|
|
16
|
+
|
|
17
|
+
public static calculateNTPResultOffset(ntpResult: TimeService.NTPResult): TimeService.Offset {
|
|
18
|
+
const {t1, t2, t3, t4} = ntpResult;
|
|
19
|
+
return ((t2 - t1) + (t3 - t4)) / 2;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
constructor(option: TimeService.Option) {
|
|
23
|
+
this.option = option;
|
|
24
|
+
|
|
25
|
+
if (option.autoStart) {
|
|
26
|
+
this.start();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Binding
|
|
30
|
+
this.start = this.start.bind(this);
|
|
31
|
+
this.stop = this.stop.bind(this);
|
|
32
|
+
this.sync = this.sync.bind(this);
|
|
33
|
+
this.getOption = this.getOption.bind(this);
|
|
34
|
+
this.setOption = this.setOption.bind(this);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public getOption(): TimeService.Option {
|
|
38
|
+
return this.option;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public setOption(option: TimeService.Option): TimeService.Option {
|
|
42
|
+
return this.option = option;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public getOffset(defaultValue: TimeService.Offset): TimeService.Offset;
|
|
46
|
+
public getOffset(): TimeService.Offset | undefined;
|
|
47
|
+
public getOffset(defaultValue?: TimeService.Offset): TimeService.Offset | undefined {
|
|
48
|
+
if (this.offset !== undefined) {
|
|
49
|
+
return this.offset;
|
|
50
|
+
}
|
|
51
|
+
if (defaultValue !== undefined) {
|
|
52
|
+
return defaultValue;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public setOffset(offset: TimeService.Offset): TimeService.Offset
|
|
58
|
+
public setOffset(offset: TimeService.Offset | undefined): TimeService.Offset
|
|
59
|
+
public setOffset(offset?: TimeService.Offset): TimeService.Offset | undefined {
|
|
60
|
+
return this.offset = offset;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public getSyncedAt(): TimeService.TimeStamp | undefined {
|
|
64
|
+
return this.syncedAt;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private setSyncedAt(syncedAt: TimeService.TimeStamp | undefined): TimeService.TimeStamp | undefined {
|
|
68
|
+
this.syncedAt = syncedAt;
|
|
69
|
+
|
|
70
|
+
// Emit
|
|
71
|
+
this.emit(TimeService.EVENT.SYNCED, syncedAt);
|
|
72
|
+
|
|
73
|
+
return syncedAt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public getSyncInterval(): number | null {
|
|
77
|
+
if (this.option.syncInterval === undefined) {
|
|
78
|
+
// If option is undefined using default value
|
|
79
|
+
return TimeService.DEFAULT_SYNC_INTERVAL;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this.option.syncInterval === null || this.option.syncInterval === -1) {
|
|
83
|
+
// If option is null, do not sync automatically
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return this.option.syncInterval;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public setSyncInterval(interval: TimeService.Option['syncInterval']) {
|
|
91
|
+
this.option.syncInterval = interval;
|
|
92
|
+
|
|
93
|
+
// Emit
|
|
94
|
+
this.emit(TimeService.EVENT.SYNC_INTERVAL_CHANGED, interval);
|
|
95
|
+
|
|
96
|
+
if (this.status === TimeService.Status.RUNNING) {
|
|
97
|
+
if (this.syncHandler !== undefined) {
|
|
98
|
+
this.stopSync();
|
|
99
|
+
this.startSync();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public getClientTime(defaultValue: TimeService.TimeStamp = Date.now()): TimeService.TimeStamp {
|
|
105
|
+
try {
|
|
106
|
+
if (typeof this.option.clientTimeProvider === 'function') {
|
|
107
|
+
return this.option.clientTimeProvider();
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error(e);
|
|
111
|
+
}
|
|
112
|
+
return defaultValue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public getServerTime(): TimeService.TimeStamp | null {
|
|
116
|
+
const offset = this.getOffset();
|
|
117
|
+
if (offset === undefined) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const clientTime = this.getClientTime();
|
|
122
|
+
return clientTime + offset;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public getTime(): number {
|
|
126
|
+
return this.getServerTime() || this.getClientTime();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private readonly fetchServerNTPResult = async (t1: TimeService.NTPResult['t1']): Promise<TimeService.ServerNTPResult | null> => {
|
|
130
|
+
try {
|
|
131
|
+
if (typeof this.option.serverTimeProvider === 'function') {
|
|
132
|
+
return await this.option.serverTimeProvider(t1);
|
|
133
|
+
}
|
|
134
|
+
} catch (e) {
|
|
135
|
+
console.error(e);
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public getStatus(): TimeService.Status {
|
|
141
|
+
return this.status;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public start() {
|
|
145
|
+
if (this.status !== TimeService.Status.STOPPED) {
|
|
146
|
+
console.warn(LOG_TAG, 'service is not stopped');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Change status
|
|
151
|
+
this.status = TimeService.Status.RUNNING;
|
|
152
|
+
|
|
153
|
+
// Sync immediately
|
|
154
|
+
this.sync().finally(() => {});
|
|
155
|
+
|
|
156
|
+
// Start sync
|
|
157
|
+
this.startSync();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public stop() {
|
|
161
|
+
if (this.status !== TimeService.Status.RUNNING) {
|
|
162
|
+
console.warn(LOG_TAG, 'service is not running');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Change status
|
|
167
|
+
this.status = TimeService.Status.RUNNING;
|
|
168
|
+
|
|
169
|
+
// Stop sync
|
|
170
|
+
this.stopSync();
|
|
171
|
+
|
|
172
|
+
// Reset offset
|
|
173
|
+
this.setOffset(undefined);
|
|
174
|
+
|
|
175
|
+
// Reset synced at
|
|
176
|
+
this.setSyncedAt(undefined);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public async sync(): Promise<TimeService.Offset | null> {
|
|
180
|
+
try {
|
|
181
|
+
// T1 (Client Request Time)
|
|
182
|
+
const requestedAt: number = Date.now();
|
|
183
|
+
|
|
184
|
+
// Fetch server time from server
|
|
185
|
+
const serverNtpResult = await this.fetchServerNTPResult(requestedAt);
|
|
186
|
+
|
|
187
|
+
// Check is null
|
|
188
|
+
if (serverNtpResult === null) {
|
|
189
|
+
console.warn(LOG_TAG, 'Failed to get server time');
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// T2 (Server Receive Time)
|
|
194
|
+
const {t2} = serverNtpResult;
|
|
195
|
+
|
|
196
|
+
// Check is not a number
|
|
197
|
+
if (isNaN(Number(t2))) {
|
|
198
|
+
// Not a Number
|
|
199
|
+
console.error(LOG_TAG, 'invalid server time(t2), not a number', t2);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// T3 (Server Transmit Time)
|
|
204
|
+
const {t3} = serverNtpResult;
|
|
205
|
+
|
|
206
|
+
// Check is not a number
|
|
207
|
+
if (isNaN(Number(t3))) {
|
|
208
|
+
// Not a Number
|
|
209
|
+
console.error(LOG_TAG, 'invalid server time(t2), not a number', t2);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// T4 (Client Receive Time)
|
|
214
|
+
const receivedAt: number = Date.now();
|
|
215
|
+
|
|
216
|
+
const ntpResult: TimeService.NTPResult = {
|
|
217
|
+
t1: requestedAt,
|
|
218
|
+
t2: t2,
|
|
219
|
+
t3: t3,
|
|
220
|
+
t4: receivedAt,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Calculate offset
|
|
224
|
+
const offset = TimeService.calculateNTPResultOffset(ntpResult);
|
|
225
|
+
|
|
226
|
+
// Save calculated offset
|
|
227
|
+
this.setOffset(offset);
|
|
228
|
+
|
|
229
|
+
// Mark synced timestamp
|
|
230
|
+
this.setSyncedAt(Date.now());
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.error(e);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private syncHandler?: ReturnType<typeof setInterval> | undefined;
|
|
239
|
+
private startSync() {
|
|
240
|
+
if (this.syncHandler !== undefined) {
|
|
241
|
+
console.warn('sync handler is not undefined', this.syncHandler);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const syncInterval = this.getSyncInterval();
|
|
246
|
+
if (syncInterval === null) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.syncHandler = setInterval(this.sync, syncInterval);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
private stopSync() {
|
|
254
|
+
if (this.syncHandler === undefined) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
clearInterval(this.syncHandler);
|
|
258
|
+
this.syncHandler = undefined;
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export namespace TimeService {
|
|
263
|
+
export const DEFAULT_SYNC_INTERVAL: number = 60000;
|
|
264
|
+
|
|
265
|
+
export enum Status {
|
|
266
|
+
STOPPED = 0,
|
|
267
|
+
RUNNING = 1,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export type Offset = number;
|
|
271
|
+
export type TimeStamp = number;
|
|
272
|
+
|
|
273
|
+
export interface NTPResult {
|
|
274
|
+
// T1 (Client Request Time)
|
|
275
|
+
t1: TimeStamp;
|
|
276
|
+
|
|
277
|
+
// T2 (Server Receive Time)
|
|
278
|
+
t2: TimeStamp;
|
|
279
|
+
|
|
280
|
+
// T3 (Server Transmit Time)
|
|
281
|
+
t3: TimeStamp;
|
|
282
|
+
|
|
283
|
+
// T4 (Client Receive Time)
|
|
284
|
+
t4: TimeStamp;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface ServerNTPResult extends Pick<NTPResult, 't2' | 't3'> {}
|
|
288
|
+
|
|
289
|
+
export type ClientTimeProvider = () => TimeStamp;
|
|
290
|
+
// export type ServerTimeProvider = ((t1: NTPResult['t1']) => ServerNTPResult) | ((t1: NTPResult['t1']) => Promise<ServerNTPResult>);
|
|
291
|
+
export type ServerTimeProvider = (t1: NTPResult['t1']) => (ServerNTPResult | null) | (Promise<ServerNTPResult | null>);
|
|
292
|
+
|
|
293
|
+
export interface Option {
|
|
294
|
+
autoStart?: boolean;
|
|
295
|
+
syncInterval?: number | null | undefined; // Sync interval in milliseconds
|
|
296
|
+
clientTimeProvider?: ClientTimeProvider | undefined;
|
|
297
|
+
serverTimeProvider?: ServerTimeProvider | undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export enum EVENT {
|
|
301
|
+
SYNCED = 'SYNCED',
|
|
302
|
+
SYNC_INTERVAL_CHANGED = 'SYNC_INTERVAL_CHANGED',
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./TimeService";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class ArrayUtils {
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 객체를 요소로 가지는 배열에서 가장 큰 값(Property)를 가지는 객체를 반환한다
|
|
5
|
+
* @param e 객체 배열
|
|
6
|
+
* @param key 값을 비교할 Property 키
|
|
7
|
+
*/
|
|
8
|
+
static getGreatestObject(e: any[], key: string) {
|
|
9
|
+
return e.reduce((prev, current) => (
|
|
10
|
+
(prev[key] > current[key] ? prev : current)
|
|
11
|
+
));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static removeDuplicate<T>(arr: T[]): T[] {
|
|
15
|
+
return [...new Set(arr)]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static removeObjectDuplicate(arr: any[], key: string) {
|
|
19
|
+
return arr.filter((v, i, self) => (
|
|
20
|
+
i === self.findIndex(e => (
|
|
21
|
+
e[key] === v[key]
|
|
22
|
+
))
|
|
23
|
+
))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {Coordinate} from "../interfaces/Coordinate";
|
|
2
|
+
|
|
3
|
+
export class CoordinateUtils {
|
|
4
|
+
public static isValidLatitude(latitude: Coordinate['latitude'] | string) {
|
|
5
|
+
return /^-?([0-8]?[0-9]|90)(\.[0-9]{1,15})$/.test(latitude.toString());
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
public static isValidLongitude(longitude: Coordinate['longitude'] | string) {
|
|
9
|
+
return /^-?([0-9]{1,2}|1[0-7][0-9]|180)(\.[0-9]{1,14})$/.test(longitude.toString());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static isValidLatitudeLongitude(latitude: Coordinate['latitude'] | string, longitude: Coordinate['longitude'] | string) {
|
|
13
|
+
return CoordinateUtils.isValidLatitude(latitude) && CoordinateUtils.isValidLongitude(longitude);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public static isValidCoordinate(coordinate: Coordinate): boolean {
|
|
17
|
+
return this.isValidLatitudeLongitude(coordinate.latitude, coordinate.longitude);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public static crowDistance(...coordinates: Coordinate[]): number {
|
|
21
|
+
if (coordinates.length <= 2) {
|
|
22
|
+
const toRad = (value: number) => value * Math.PI / 180;
|
|
23
|
+
|
|
24
|
+
const c1 = coordinates[0];
|
|
25
|
+
const c2 = coordinates[1];
|
|
26
|
+
if (!c1 || !c2) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const R = 6371e3;
|
|
31
|
+
const dLat = toRad(c2.latitude - c1.latitude);
|
|
32
|
+
const dLon = toRad(c2.longitude - c1.longitude);
|
|
33
|
+
const lat1 = toRad(c1.latitude);
|
|
34
|
+
const lat2 = toRad(c2.latitude);
|
|
35
|
+
|
|
36
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
37
|
+
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
|
|
38
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
39
|
+
|
|
40
|
+
return R * c;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let totalDistance: number = 0;
|
|
44
|
+
for (let i = 0; i < coordinates.length - 1; ++i) {
|
|
45
|
+
totalDistance += this.crowDistance(coordinates[i], coordinates[i + 1]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return totalDistance;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import moment, {Moment, MomentInput, RelativeTimeSpec} from "moment";
|
|
2
|
+
|
|
3
|
+
export class DateUtils {
|
|
4
|
+
public static relativeDate = (input: MomentInput, from: MomentInput = Date.now()): string => {
|
|
5
|
+
// Create new moment instance to apply new locale
|
|
6
|
+
const datetime = moment.isMoment(input) ? moment(input.toDate()) : moment(input);
|
|
7
|
+
|
|
8
|
+
if (!datetime.isValid()) {
|
|
9
|
+
console.error('core-js:DateUtils.relativeDate() datetime is invalid', datetime);
|
|
10
|
+
return '<INVALID DATE>';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return datetime.from(from);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
public static setLocale = (language: DateUtils.Locale) => {
|
|
17
|
+
const relativeTime = DateUtils.getRelativeTimeSpec(language);
|
|
18
|
+
if (relativeTime !== undefined) {
|
|
19
|
+
moment.updateLocale(language, {relativeTime});
|
|
20
|
+
}
|
|
21
|
+
moment.locale(language);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
private static getRelativeTimeSpec = (locale: DateUtils.Locale): RelativeTimeSpec | undefined => {
|
|
25
|
+
if (locale in DateUtils.relativeTimeSpecs) {
|
|
26
|
+
return DateUtils.relativeTimeSpecs[locale];
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export namespace DateUtils {
|
|
33
|
+
export type Locale = string;
|
|
34
|
+
|
|
35
|
+
export const relativeTimeSpecs: {[key: Locale]: RelativeTimeSpec} = {
|
|
36
|
+
ko: {
|
|
37
|
+
future: "%s 후",
|
|
38
|
+
past: "%s 전",
|
|
39
|
+
s: number => `${number}초`,
|
|
40
|
+
ss: number => `${number}초`,
|
|
41
|
+
m: number => `${number}분`,
|
|
42
|
+
mm: number => `${number}분`,
|
|
43
|
+
h: number => `${number}시간`,
|
|
44
|
+
hh: number => `${number}시간`,
|
|
45
|
+
d: number => `${number}일`,
|
|
46
|
+
dd: number => `${number}일`,
|
|
47
|
+
M: number => `${number}개월`,
|
|
48
|
+
MM: number => `${number}개월`,
|
|
49
|
+
y: number => `${number}년`,
|
|
50
|
+
yy: number => `${number}년`
|
|
51
|
+
} as RelativeTimeSpec,
|
|
52
|
+
en: {
|
|
53
|
+
future: "in %s",
|
|
54
|
+
past: "%s ago",
|
|
55
|
+
s: number => number === 1 ? `${number} second`: `${number} seconds`,
|
|
56
|
+
ss: number => `${number} seconds`,
|
|
57
|
+
m: number => number === 1 ? `${number} minute`: `${number} minutes`,
|
|
58
|
+
mm: number => `${number} minutes`,
|
|
59
|
+
h: number => number === 1 ? `${number} hour`: `${number} hours`,
|
|
60
|
+
hh: number => `${number} hours`,
|
|
61
|
+
d: number => number === 1 ? `${number} day`: `${number} days`,
|
|
62
|
+
dd: number => `${number} days`,
|
|
63
|
+
M: number => number === 1 ? `${number} month`: `${number} months`,
|
|
64
|
+
MM: number => `${number} months`,
|
|
65
|
+
y: number => number === 1 ? `${number} year`: `${number} years`,
|
|
66
|
+
yy: number => `${number} years`
|
|
67
|
+
} as RelativeTimeSpec,
|
|
68
|
+
ja: {
|
|
69
|
+
future: "%s後",
|
|
70
|
+
past: "%s前",
|
|
71
|
+
s: number => `${number}秒`,
|
|
72
|
+
ss: number => `${number}秒`,
|
|
73
|
+
m: number => `${number}分`,
|
|
74
|
+
mm: number => `${number}分`,
|
|
75
|
+
h: number => `${number}時`,
|
|
76
|
+
hh: number => `${number}時`,
|
|
77
|
+
d: number => `${number}日`,
|
|
78
|
+
dd: number => `${number}日`,
|
|
79
|
+
M: number => `${number}月`,
|
|
80
|
+
MM: number => `${number}月`,
|
|
81
|
+
y: number => `${number}年`,
|
|
82
|
+
yy: number => `${number}年`
|
|
83
|
+
} as RelativeTimeSpec,
|
|
84
|
+
zh: {
|
|
85
|
+
future: "%s后",
|
|
86
|
+
past: "%s前",
|
|
87
|
+
s: number => `${number}秒`,
|
|
88
|
+
ss: number => `${number}秒`,
|
|
89
|
+
m: number => `${number}分钟`,
|
|
90
|
+
mm: number => `${number}分钟`,
|
|
91
|
+
h: number => `${number}个小时`,
|
|
92
|
+
hh: number => `${number}个小时`,
|
|
93
|
+
d: number => `${number}天`,
|
|
94
|
+
dd: number => `${number}天`,
|
|
95
|
+
M: number => `${number}个月`,
|
|
96
|
+
MM: number => `${number}个月`,
|
|
97
|
+
y: number => `${number}年`,
|
|
98
|
+
yy: number => `${number}年`
|
|
99
|
+
} as RelativeTimeSpec,
|
|
100
|
+
ru: {
|
|
101
|
+
future: "через %s",
|
|
102
|
+
past: "%s назад",
|
|
103
|
+
s: number => `${number} секунд`,
|
|
104
|
+
ss: number => `${number} секунд`,
|
|
105
|
+
m: number => `${number} минута`,
|
|
106
|
+
mm: number => `${number} минут`,
|
|
107
|
+
h: number => `${number} час`,
|
|
108
|
+
hh: number => `${number} часов`,
|
|
109
|
+
d: number => `${number} день`,
|
|
110
|
+
dd: number => `${number} дней`,
|
|
111
|
+
M: number => `${number} месяц`,
|
|
112
|
+
MM: number => `${number} месяцев`,
|
|
113
|
+
y: number => `${number} год`,
|
|
114
|
+
yy: number => `${number} лет`
|
|
115
|
+
} as RelativeTimeSpec,
|
|
116
|
+
uz: {
|
|
117
|
+
future: "%sdan keyin",
|
|
118
|
+
past: "%s avval",
|
|
119
|
+
s: number => `${number} soniya`,
|
|
120
|
+
ss: number => `${number} soniya`,
|
|
121
|
+
m: number => `${number} daqiqa`,
|
|
122
|
+
mm: number => `${number} daqiqa`,
|
|
123
|
+
h: number => `${number} soat`,
|
|
124
|
+
hh: number => `${number} soat`,
|
|
125
|
+
d: number => `${number} kun`,
|
|
126
|
+
dd: number => `${number} kun`,
|
|
127
|
+
M: number => `${number} oy`,
|
|
128
|
+
MM: number => `${number} oy`,
|
|
129
|
+
y: number => `${number} yil`,
|
|
130
|
+
yy: number => `${number} yil`
|
|
131
|
+
} as RelativeTimeSpec,
|
|
132
|
+
de: {
|
|
133
|
+
future: "in %s",
|
|
134
|
+
past: "vor %s",
|
|
135
|
+
s: number => `${number === 1 ? "einer" : number} Sekunde${number === 1 ? "" : "n"}`,
|
|
136
|
+
ss: number => `${number} Sekunde${number === 1 ? "" : "n"}`,
|
|
137
|
+
m: number => `${number === 1 ? "einer" : number} Minute${number === 1 ? "" : "n"}`,
|
|
138
|
+
mm: number => `${number} Minute${number === 1 ? "" : "n"}`,
|
|
139
|
+
h: number => `${number === 1 ? "einer" : number} Stunde${number === 1 ? "" : "n"}`,
|
|
140
|
+
hh: number => `${number} Stunde${number === 1 ? "" : "n"}`,
|
|
141
|
+
d: number => `${number === 1 ? "einem" : number} Tag${number === 1 ? "" : "en"}`,
|
|
142
|
+
dd: number => `${number} Tag${number === 1 ? "" : "en"}`,
|
|
143
|
+
M: number => `${number === 1 ? "einem" : number} Monat${number === 1 ? "" : "en"}`,
|
|
144
|
+
MM: number => `${number} Monat${number === 1 ? "" : "en"}`,
|
|
145
|
+
y: number => `${number === 1 ? "einem" : number} Jahr${number === 1 ? "" : "en"}`,
|
|
146
|
+
yy: number => `${number} Jahr${number === 1 ? "" : "en"}`
|
|
147
|
+
} as RelativeTimeSpec,
|
|
148
|
+
fr: {
|
|
149
|
+
future: "dans %s",
|
|
150
|
+
past: "il y a %s",
|
|
151
|
+
s: number => `quelques secondes`,
|
|
152
|
+
ss: number => `${number} secondes`,
|
|
153
|
+
m: number => `une minute`,
|
|
154
|
+
mm: number => `${number} minutes`,
|
|
155
|
+
h: number => `une heure`,
|
|
156
|
+
hh: number => `${number} heures`,
|
|
157
|
+
d: number => `un jour`,
|
|
158
|
+
dd: number => `${number} jours`,
|
|
159
|
+
M: number => `un mois`,
|
|
160
|
+
MM: number => `${number} mois`,
|
|
161
|
+
y: number => `un an`,
|
|
162
|
+
yy: number => `${number} ans`
|
|
163
|
+
} as RelativeTimeSpec,
|
|
164
|
+
vi: {
|
|
165
|
+
future: "sau %s",
|
|
166
|
+
past: "%s trước",
|
|
167
|
+
s: number => `${number} giây`,
|
|
168
|
+
ss: number => `${number} giây`,
|
|
169
|
+
m: number => `${number} phút`,
|
|
170
|
+
mm: number => `${number} phút`,
|
|
171
|
+
h: number => `${number} giờ`,
|
|
172
|
+
hh: number => `${number} giờ`,
|
|
173
|
+
d: number => `${number} ngày`,
|
|
174
|
+
dd: number => `${number} ngày`,
|
|
175
|
+
M: number => `${number} tháng`,
|
|
176
|
+
MM: number => `${number} tháng`,
|
|
177
|
+
y: number => `${number} năm`,
|
|
178
|
+
yy: number => `${number} năm`
|
|
179
|
+
} as RelativeTimeSpec,
|
|
180
|
+
th: {
|
|
181
|
+
future: "ใน %s",
|
|
182
|
+
past: "%s ที่แล้ว",
|
|
183
|
+
s: number => `${number} วินาที`,
|
|
184
|
+
ss: number => `${number} วินาที`,
|
|
185
|
+
m: number => `${number} นาที`,
|
|
186
|
+
mm: number => `${number} นาที`,
|
|
187
|
+
h: number => `${number} ชั่วโมง`,
|
|
188
|
+
hh: number => `${number} ชั่วโมง`,
|
|
189
|
+
d: number => `${number} วัน`,
|
|
190
|
+
dd: number => `${number} วัน`,
|
|
191
|
+
M: number => `${number} เดือน`,
|
|
192
|
+
MM: number => `${number} เดือน`,
|
|
193
|
+
y: number => `${number} ปี`,
|
|
194
|
+
yy: number => `${number} ปี`
|
|
195
|
+
} as RelativeTimeSpec,
|
|
196
|
+
fil: {
|
|
197
|
+
future: "sa loob ng %s",
|
|
198
|
+
past: "%s ang nakalipas",
|
|
199
|
+
s: number => `${number} segundo`,
|
|
200
|
+
ss: number => `${number} segundo`,
|
|
201
|
+
m: number => `${number} minuto`,
|
|
202
|
+
mm: number => `${number} minuto`,
|
|
203
|
+
h: number => `${number} oras`,
|
|
204
|
+
hh: number => `${number} oras`,
|
|
205
|
+
d: number => `${number} araw`,
|
|
206
|
+
dd: number => `${number} araw`,
|
|
207
|
+
M: number => `${number} buwan`,
|
|
208
|
+
MM: number => `${number} buwan`,
|
|
209
|
+
y: number => `${number} taon`,
|
|
210
|
+
yy: number => `${number} taon`
|
|
211
|
+
} as RelativeTimeSpec,
|
|
212
|
+
};
|
|
213
|
+
}
|