@etsoo/appscript 1.2.51 → 1.2.55

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.
@@ -7,6 +7,7 @@ const shared_1 = require("@etsoo/shared");
7
7
  const crypto_js_1 = require("crypto-js");
8
8
  const AddressRegion_1 = require("../address/AddressRegion");
9
9
  const AddressUtils_1 = require("../address/AddressUtils");
10
+ const BridgeUtils_1 = require("../bridges/BridgeUtils");
10
11
  const BusinessUtils_1 = require("../business/BusinessUtils");
11
12
  const ActionResultError_1 = require("../result/ActionResultError");
12
13
  /**
@@ -383,6 +384,7 @@ class CoreApp {
383
384
  * @param refreshToken Refresh token
384
385
  */
385
386
  authorize(token, refreshToken) {
387
+ var _a;
386
388
  // State, when token is null, means logout
387
389
  this.authorized = token != null;
388
390
  // Token
@@ -402,6 +404,8 @@ class CoreApp {
402
404
  this.cachedRefreshToken = undefined;
403
405
  this.refreshCountdownClear();
404
406
  }
407
+ // Host notice
408
+ (_a = BridgeUtils_1.BridgeUtils.host) === null || _a === void 0 ? void 0 : _a.userAuthorization(this.authorized);
405
409
  }
406
410
  /**
407
411
  * Change country or region
@@ -1,3 +1,4 @@
1
+ import { FlutterHost } from './FlutterHost';
1
2
  import { IBridgeHost } from './IBridgeHost';
2
3
  /**
3
4
  * Bridge utils
@@ -6,5 +7,5 @@ export declare namespace BridgeUtils {
6
7
  /**
7
8
  * Bridge host
8
9
  */
9
- const host: IBridgeHost | undefined;
10
+ const host: IBridgeHost | FlutterHost | undefined;
10
11
  }
@@ -7,13 +7,12 @@ const FlutterHost_1 = require("./FlutterHost");
7
7
  */
8
8
  var BridgeUtils;
9
9
  (function (BridgeUtils) {
10
- var _a;
11
10
  const g = globalThis;
12
11
  /**
13
12
  * Bridge host
14
13
  */
15
- BridgeUtils.host = typeof ((_a = g.flutter_inappwebview) === null || _a === void 0 ? void 0 : _a.callHandler) === 'function'
16
- ? new FlutterHost_1.FlutterHost(g.flutter_inappwebview.callHandler)
14
+ BridgeUtils.host = typeof g.flutter_inappwebview === 'object'
15
+ ? new FlutterHost_1.FlutterHost(g.flutter_inappwebview)
17
16
  : typeof g.electron === 'object'
18
17
  ? g.electron
19
18
  : undefined;
@@ -1,21 +1,33 @@
1
1
  import { IBridgeHost } from './IBridgeHost';
2
+ declare type CallHandlerType = (name: string, ...args: unknown[]) => PromiseLike<Record<string, unknown> | void>;
2
3
  /**
3
4
  * Flutter JavaScript Host
4
5
  * https://inappwebview.dev/docs/javascript/communication/
5
6
  */
6
7
  export declare class FlutterHost implements IBridgeHost {
7
- callHandler: (name: string, ...args: unknown[]) => PromiseLike<{} | void>;
8
+ private host;
8
9
  /**
9
10
  * Start Url
10
11
  */
11
12
  private startUrl;
13
+ /**
14
+ * Cached commands
15
+ */
16
+ private cachedCommands;
12
17
  /**
13
18
  * Constructor
14
19
  * @param callHandler Call handler
15
20
  */
16
- constructor(callHandler: (name: string, ...args: unknown[]) => PromiseLike<{} | void>);
21
+ constructor(host: {
22
+ callHandler?: CallHandlerType;
23
+ });
24
+ cacheCommand(name: string, ...args: unknown[]): void;
17
25
  changeCulture(locale: string): void;
26
+ closable(): boolean;
18
27
  exit(): void;
28
+ getLabels<T extends string>(...keys: T[]): Promise<any>;
19
29
  getStartUrl(): string | null | undefined;
20
30
  loadApp(name: string, startUrl?: string): void;
31
+ userAuthorization(authorized: boolean): void;
21
32
  }
33
+ export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlutterHost = void 0;
4
+ const shared_1 = require("@etsoo/shared");
4
5
  /**
5
6
  * Flutter JavaScript Host
6
7
  * https://inappwebview.dev/docs/javascript/communication/
@@ -10,21 +11,80 @@ class FlutterHost {
10
11
  * Constructor
11
12
  * @param callHandler Call handler
12
13
  */
13
- constructor(callHandler) {
14
- this.callHandler = callHandler;
14
+ constructor(host) {
15
+ this.host = host;
16
+ /**
17
+ * Cached commands
18
+ */
19
+ this.cachedCommands = {};
20
+ window.addEventListener('flutterInAppWebViewPlatformReady', (_event) => {
21
+ if (this.host.callHandler == null)
22
+ return;
23
+ for (const key in this.cachedCommands) {
24
+ // Args
25
+ const args = this.cachedCommands[key];
26
+ // Execute
27
+ this.host.callHandler(key, ...args);
28
+ // Remove the key
29
+ delete this.cachedCommands[key];
30
+ }
31
+ });
32
+ }
33
+ cacheCommand(name, ...args) {
34
+ this.cachedCommands[name] = args;
15
35
  }
16
36
  changeCulture(locale) {
17
- this.callHandler('changeCulture', locale);
37
+ if (this.host.callHandler)
38
+ this.host.callHandler('changeCulture', locale);
39
+ else
40
+ this.cacheCommand('changeCulture', locale);
41
+ }
42
+ closable() {
43
+ return false;
18
44
  }
19
45
  exit() {
20
- this.callHandler('exit');
46
+ if (this.host.callHandler)
47
+ this.host.callHandler('exit');
48
+ else
49
+ this.cacheCommand('exit');
50
+ }
51
+ async getLabels(...keys) {
52
+ var _a;
53
+ // Try 500 miliseconds
54
+ let count = 5;
55
+ while (this.host.callHandler == null) {
56
+ count--;
57
+ await shared_1.ExtendUtils.sleep(100);
58
+ if (count === 0)
59
+ break;
60
+ }
61
+ const init = {};
62
+ if (this.host.callHandler == null)
63
+ return init;
64
+ const result = (_a = (await this.host.callHandler('getLabels'))) !== null && _a !== void 0 ? _a : {};
65
+ return keys.reduce((a, v) => {
66
+ var _a;
67
+ return ({
68
+ ...a,
69
+ [v]: (_a = result[v]) !== null && _a !== void 0 ? _a : ''
70
+ });
71
+ }, init);
21
72
  }
22
73
  getStartUrl() {
23
74
  return this.startUrl;
24
75
  }
25
76
  loadApp(name, startUrl) {
26
77
  this.startUrl = startUrl;
27
- this.callHandler('loadApp', name, startUrl);
78
+ if (this.host.callHandler)
79
+ this.host.callHandler('loadApp', name, startUrl);
80
+ else
81
+ this.cacheCommand('loadApp', name, startUrl);
82
+ }
83
+ userAuthorization(authorized) {
84
+ if (this.host.callHandler)
85
+ this.host.callHandler('userAuthorization', authorized);
86
+ else
87
+ this.cacheCommand('userAuthorization', authorized);
28
88
  }
29
89
  }
30
90
  exports.FlutterHost = FlutterHost;
@@ -7,10 +7,21 @@ export interface IBridgeHost {
7
7
  * @param locale Locale
8
8
  */
9
9
  changeCulture(locale: string): void;
10
+ /**
11
+ * Closable from client
12
+ */
13
+ closable(): boolean;
10
14
  /**
11
15
  * Exit the application
12
16
  */
13
17
  exit(): void;
18
+ /**
19
+ * Get multiple culture labels
20
+ * @param keys Keys
21
+ */
22
+ getLabels<T extends string>(...keys: T[]): PromiseLike<{
23
+ [K in T]: string;
24
+ }>;
14
25
  /**
15
26
  * Get app start Url / router Url
16
27
  */
@@ -21,4 +32,9 @@ export interface IBridgeHost {
21
32
  * @param startUrl Start Url / router Url
22
33
  */
23
34
  loadApp(name: string, startUrl?: string): void;
35
+ /**
36
+ * User authorization notice
37
+ * @param authorized Authorized or not
38
+ */
39
+ userAuthorization(authorized: boolean): void;
24
40
  }
@@ -4,6 +4,7 @@ import { DateUtils, DomUtils, NumberUtils, Utils } from '@etsoo/shared';
4
4
  import { AES, algo, enc, HmacSHA512, lib, mode, pad, PBKDF2, SHA3 } from 'crypto-js';
5
5
  import { AddressRegion } from '../address/AddressRegion';
6
6
  import { AddressUtils } from '../address/AddressUtils';
7
+ import { BridgeUtils } from '../bridges/BridgeUtils';
7
8
  import { BusinessUtils } from '../business/BusinessUtils';
8
9
  import { ActionResultError } from '../result/ActionResultError';
9
10
  /**
@@ -380,6 +381,7 @@ export class CoreApp {
380
381
  * @param refreshToken Refresh token
381
382
  */
382
383
  authorize(token, refreshToken) {
384
+ var _a;
383
385
  // State, when token is null, means logout
384
386
  this.authorized = token != null;
385
387
  // Token
@@ -399,6 +401,8 @@ export class CoreApp {
399
401
  this.cachedRefreshToken = undefined;
400
402
  this.refreshCountdownClear();
401
403
  }
404
+ // Host notice
405
+ (_a = BridgeUtils.host) === null || _a === void 0 ? void 0 : _a.userAuthorization(this.authorized);
402
406
  }
403
407
  /**
404
408
  * Change country or region
@@ -1,3 +1,4 @@
1
+ import { FlutterHost } from './FlutterHost';
1
2
  import { IBridgeHost } from './IBridgeHost';
2
3
  /**
3
4
  * Bridge utils
@@ -6,5 +7,5 @@ export declare namespace BridgeUtils {
6
7
  /**
7
8
  * Bridge host
8
9
  */
9
- const host: IBridgeHost | undefined;
10
+ const host: IBridgeHost | FlutterHost | undefined;
10
11
  }
@@ -4,13 +4,12 @@ import { FlutterHost } from './FlutterHost';
4
4
  */
5
5
  export var BridgeUtils;
6
6
  (function (BridgeUtils) {
7
- var _a;
8
7
  const g = globalThis;
9
8
  /**
10
9
  * Bridge host
11
10
  */
12
- BridgeUtils.host = typeof ((_a = g.flutter_inappwebview) === null || _a === void 0 ? void 0 : _a.callHandler) === 'function'
13
- ? new FlutterHost(g.flutter_inappwebview.callHandler)
11
+ BridgeUtils.host = typeof g.flutter_inappwebview === 'object'
12
+ ? new FlutterHost(g.flutter_inappwebview)
14
13
  : typeof g.electron === 'object'
15
14
  ? g.electron
16
15
  : undefined;
@@ -1,21 +1,33 @@
1
1
  import { IBridgeHost } from './IBridgeHost';
2
+ declare type CallHandlerType = (name: string, ...args: unknown[]) => PromiseLike<Record<string, unknown> | void>;
2
3
  /**
3
4
  * Flutter JavaScript Host
4
5
  * https://inappwebview.dev/docs/javascript/communication/
5
6
  */
6
7
  export declare class FlutterHost implements IBridgeHost {
7
- callHandler: (name: string, ...args: unknown[]) => PromiseLike<{} | void>;
8
+ private host;
8
9
  /**
9
10
  * Start Url
10
11
  */
11
12
  private startUrl;
13
+ /**
14
+ * Cached commands
15
+ */
16
+ private cachedCommands;
12
17
  /**
13
18
  * Constructor
14
19
  * @param callHandler Call handler
15
20
  */
16
- constructor(callHandler: (name: string, ...args: unknown[]) => PromiseLike<{} | void>);
21
+ constructor(host: {
22
+ callHandler?: CallHandlerType;
23
+ });
24
+ cacheCommand(name: string, ...args: unknown[]): void;
17
25
  changeCulture(locale: string): void;
26
+ closable(): boolean;
18
27
  exit(): void;
28
+ getLabels<T extends string>(...keys: T[]): Promise<any>;
19
29
  getStartUrl(): string | null | undefined;
20
30
  loadApp(name: string, startUrl?: string): void;
31
+ userAuthorization(authorized: boolean): void;
21
32
  }
33
+ export {};
@@ -1,3 +1,4 @@
1
+ import { ExtendUtils } from '@etsoo/shared';
1
2
  /**
2
3
  * Flutter JavaScript Host
3
4
  * https://inappwebview.dev/docs/javascript/communication/
@@ -7,20 +8,79 @@ export class FlutterHost {
7
8
  * Constructor
8
9
  * @param callHandler Call handler
9
10
  */
10
- constructor(callHandler) {
11
- this.callHandler = callHandler;
11
+ constructor(host) {
12
+ this.host = host;
13
+ /**
14
+ * Cached commands
15
+ */
16
+ this.cachedCommands = {};
17
+ window.addEventListener('flutterInAppWebViewPlatformReady', (_event) => {
18
+ if (this.host.callHandler == null)
19
+ return;
20
+ for (const key in this.cachedCommands) {
21
+ // Args
22
+ const args = this.cachedCommands[key];
23
+ // Execute
24
+ this.host.callHandler(key, ...args);
25
+ // Remove the key
26
+ delete this.cachedCommands[key];
27
+ }
28
+ });
29
+ }
30
+ cacheCommand(name, ...args) {
31
+ this.cachedCommands[name] = args;
12
32
  }
13
33
  changeCulture(locale) {
14
- this.callHandler('changeCulture', locale);
34
+ if (this.host.callHandler)
35
+ this.host.callHandler('changeCulture', locale);
36
+ else
37
+ this.cacheCommand('changeCulture', locale);
38
+ }
39
+ closable() {
40
+ return false;
15
41
  }
16
42
  exit() {
17
- this.callHandler('exit');
43
+ if (this.host.callHandler)
44
+ this.host.callHandler('exit');
45
+ else
46
+ this.cacheCommand('exit');
47
+ }
48
+ async getLabels(...keys) {
49
+ var _a;
50
+ // Try 500 miliseconds
51
+ let count = 5;
52
+ while (this.host.callHandler == null) {
53
+ count--;
54
+ await ExtendUtils.sleep(100);
55
+ if (count === 0)
56
+ break;
57
+ }
58
+ const init = {};
59
+ if (this.host.callHandler == null)
60
+ return init;
61
+ const result = (_a = (await this.host.callHandler('getLabels'))) !== null && _a !== void 0 ? _a : {};
62
+ return keys.reduce((a, v) => {
63
+ var _a;
64
+ return ({
65
+ ...a,
66
+ [v]: (_a = result[v]) !== null && _a !== void 0 ? _a : ''
67
+ });
68
+ }, init);
18
69
  }
19
70
  getStartUrl() {
20
71
  return this.startUrl;
21
72
  }
22
73
  loadApp(name, startUrl) {
23
74
  this.startUrl = startUrl;
24
- this.callHandler('loadApp', name, startUrl);
75
+ if (this.host.callHandler)
76
+ this.host.callHandler('loadApp', name, startUrl);
77
+ else
78
+ this.cacheCommand('loadApp', name, startUrl);
79
+ }
80
+ userAuthorization(authorized) {
81
+ if (this.host.callHandler)
82
+ this.host.callHandler('userAuthorization', authorized);
83
+ else
84
+ this.cacheCommand('userAuthorization', authorized);
25
85
  }
26
86
  }
@@ -7,10 +7,21 @@ export interface IBridgeHost {
7
7
  * @param locale Locale
8
8
  */
9
9
  changeCulture(locale: string): void;
10
+ /**
11
+ * Closable from client
12
+ */
13
+ closable(): boolean;
10
14
  /**
11
15
  * Exit the application
12
16
  */
13
17
  exit(): void;
18
+ /**
19
+ * Get multiple culture labels
20
+ * @param keys Keys
21
+ */
22
+ getLabels<T extends string>(...keys: T[]): PromiseLike<{
23
+ [K in T]: string;
24
+ }>;
14
25
  /**
15
26
  * Get app start Url / router Url
16
27
  */
@@ -21,4 +32,9 @@ export interface IBridgeHost {
21
32
  * @param startUrl Start Url / router Url
22
33
  */
23
34
  loadApp(name: string, startUrl?: string): void;
35
+ /**
36
+ * User authorization notice
37
+ * @param authorized Authorized or not
38
+ */
39
+ userAuthorization(authorized: boolean): void;
24
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/appscript",
3
- "version": "1.2.51",
3
+ "version": "1.2.55",
4
4
  "description": "Applications shared TypeScript framework",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -55,19 +55,19 @@
55
55
  "@etsoo/notificationbase": "^1.1.1",
56
56
  "@etsoo/restclient": "^1.0.64",
57
57
  "@etsoo/shared": "^1.1.11",
58
- "@types/crypto-js": "^4.1.0",
58
+ "@types/crypto-js": "^4.1.1",
59
59
  "crypto-js": "^4.1.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@babel/cli": "^7.17.0",
63
- "@babel/core": "^7.17.2",
62
+ "@babel/cli": "^7.17.6",
63
+ "@babel/core": "^7.17.5",
64
64
  "@babel/plugin-transform-runtime": "^7.17.0",
65
65
  "@babel/preset-env": "^7.16.11",
66
66
  "@babel/runtime-corejs3": "^7.17.2",
67
67
  "@types/jest": "^27.4.0",
68
- "@typescript-eslint/eslint-plugin": "^5.11.0",
69
- "@typescript-eslint/parser": "^5.11.0",
70
- "eslint": "^8.8.0",
68
+ "@typescript-eslint/eslint-plugin": "^5.12.1",
69
+ "@typescript-eslint/parser": "^5.12.1",
70
+ "eslint": "^8.9.0",
71
71
  "eslint-config-airbnb-base": "^15.0.0",
72
72
  "eslint-plugin-import": "^2.25.4",
73
73
  "jest": "^27.5.1",
@@ -29,6 +29,7 @@ import {
29
29
  } from 'crypto-js';
30
30
  import { AddressRegion } from '../address/AddressRegion';
31
31
  import { AddressUtils } from '../address/AddressUtils';
32
+ import { BridgeUtils } from '../bridges/BridgeUtils';
32
33
  import { BusinessUtils } from '../business/BusinessUtils';
33
34
  import { ProductUnit } from '../business/ProductUnit';
34
35
  import { IdLabelDto } from '../dto/IdLabelDto';
@@ -1041,6 +1042,9 @@ export abstract class CoreApp<
1041
1042
  this.cachedRefreshToken = undefined;
1042
1043
  this.refreshCountdownClear();
1043
1044
  }
1045
+
1046
+ // Host notice
1047
+ BridgeUtils.host?.userAuthorization(this.authorized);
1044
1048
  }
1045
1049
 
1046
1050
  /**
@@ -11,8 +11,8 @@ export namespace BridgeUtils {
11
11
  * Bridge host
12
12
  */
13
13
  export const host =
14
- typeof g.flutter_inappwebview?.callHandler === 'function'
15
- ? new FlutterHost(g.flutter_inappwebview.callHandler)
14
+ typeof g.flutter_inappwebview === 'object'
15
+ ? new FlutterHost(g.flutter_inappwebview)
16
16
  : typeof g.electron === 'object'
17
17
  ? (g.electron as IBridgeHost)
18
18
  : undefined;
@@ -1,5 +1,12 @@
1
+ import { ExtendUtils } from '@etsoo/shared';
1
2
  import { IBridgeHost } from './IBridgeHost';
2
3
 
4
+ // Call handler type
5
+ type CallHandlerType = (
6
+ name: string,
7
+ ...args: unknown[]
8
+ ) => PromiseLike<Record<string, unknown> | void>;
9
+
3
10
  /**
4
11
  * Flutter JavaScript Host
5
12
  * https://inappwebview.dev/docs/javascript/communication/
@@ -10,23 +17,77 @@ export class FlutterHost implements IBridgeHost {
10
17
  */
11
18
  private startUrl: string | null | undefined;
12
19
 
20
+ /**
21
+ * Cached commands
22
+ */
23
+ private cachedCommands: Record<string, unknown[]> = {};
24
+
13
25
  /**
14
26
  * Constructor
15
27
  * @param callHandler Call handler
16
28
  */
17
- constructor(
18
- public callHandler: (
19
- name: string,
20
- ...args: unknown[]
21
- ) => PromiseLike<{} | void>
22
- ) {}
29
+ constructor(private host: { callHandler?: CallHandlerType }) {
30
+ window.addEventListener(
31
+ 'flutterInAppWebViewPlatformReady',
32
+ (_event) => {
33
+ if (this.host.callHandler == null) return;
34
+
35
+ for (const key in this.cachedCommands) {
36
+ // Args
37
+ const args = this.cachedCommands[key];
38
+
39
+ // Execute
40
+ this.host.callHandler(key, ...args);
41
+
42
+ // Remove the key
43
+ delete this.cachedCommands[key];
44
+ }
45
+ }
46
+ );
47
+ }
48
+
49
+ cacheCommand(name: string, ...args: unknown[]): void {
50
+ this.cachedCommands[name] = args;
51
+ }
23
52
 
24
53
  changeCulture(locale: string): void {
25
- this.callHandler('changeCulture', locale);
54
+ if (this.host.callHandler)
55
+ this.host.callHandler('changeCulture', locale);
56
+ else this.cacheCommand('changeCulture', locale);
57
+ }
58
+
59
+ closable(): boolean {
60
+ return false;
26
61
  }
27
62
 
28
63
  exit(): void {
29
- this.callHandler('exit');
64
+ if (this.host.callHandler) this.host.callHandler('exit');
65
+ else this.cacheCommand('exit');
66
+ }
67
+
68
+ async getLabels<T extends string>(...keys: T[]) {
69
+ // Try 500 miliseconds
70
+ let count = 5;
71
+ while (this.host.callHandler == null) {
72
+ count--;
73
+ await ExtendUtils.sleep(100);
74
+
75
+ if (count === 0) break;
76
+ }
77
+
78
+ const init: any = {};
79
+
80
+ if (this.host.callHandler == null) return init;
81
+
82
+ const result = (await this.host.callHandler('getLabels')) ?? {};
83
+
84
+ return keys.reduce(
85
+ (a, v) => ({
86
+ ...a,
87
+ [v]: result[v] ?? ''
88
+ }),
89
+ init
90
+ );
30
91
  }
31
92
 
32
93
  getStartUrl(): string | null | undefined {
@@ -35,6 +96,14 @@ export class FlutterHost implements IBridgeHost {
35
96
 
36
97
  loadApp(name: string, startUrl?: string): void {
37
98
  this.startUrl = startUrl;
38
- this.callHandler('loadApp', name, startUrl);
99
+ if (this.host.callHandler)
100
+ this.host.callHandler('loadApp', name, startUrl);
101
+ else this.cacheCommand('loadApp', name, startUrl);
102
+ }
103
+
104
+ userAuthorization(authorized: boolean): void {
105
+ if (this.host.callHandler)
106
+ this.host.callHandler('userAuthorization', authorized);
107
+ else this.cacheCommand('userAuthorization', authorized);
39
108
  }
40
109
  }
@@ -8,11 +8,24 @@ export interface IBridgeHost {
8
8
  */
9
9
  changeCulture(locale: string): void;
10
10
 
11
+ /**
12
+ * Closable from client
13
+ */
14
+ closable(): boolean;
15
+
11
16
  /**
12
17
  * Exit the application
13
18
  */
14
19
  exit(): void;
15
20
 
21
+ /**
22
+ * Get multiple culture labels
23
+ * @param keys Keys
24
+ */
25
+ getLabels<T extends string>(
26
+ ...keys: T[]
27
+ ): PromiseLike<{ [K in T]: string }>;
28
+
16
29
  /**
17
30
  * Get app start Url / router Url
18
31
  */
@@ -24,4 +37,10 @@ export interface IBridgeHost {
24
37
  * @param startUrl Start Url / router Url
25
38
  */
26
39
  loadApp(name: string, startUrl?: string): void;
40
+
41
+ /**
42
+ * User authorization notice
43
+ * @param authorized Authorized or not
44
+ */
45
+ userAuthorization(authorized: boolean): void;
27
46
  }