@adsgram/react 1.0.0
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/README.md +99 -0
- package/dist/components/adsgramTask.d.ts +70 -0
- package/dist/components/adsgramTask.js +88 -0
- package/dist/hooks/useAdsgram.d.ts +45 -0
- package/dist/hooks/useAdsgram.js +99 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/types/task.d.ts +15 -0
- package/dist/types/task.js +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @adsgram/react
|
|
2
|
+
|
|
3
|
+
React bindings for Adsgram SDK. Provides components and hooks for ad tasks and fullscreen banners.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @adsgram/react
|
|
9
|
+
# or
|
|
10
|
+
yarn add @adsgram/react
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @adsgram/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Components
|
|
16
|
+
|
|
17
|
+
### `<AdsgramTask />`
|
|
18
|
+
|
|
19
|
+
React component for the `<adsgram-task>` custom element.
|
|
20
|
+
|
|
21
|
+
#### Props
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Description |
|
|
24
|
+
|--------------------|----------------|-----------------------------------------|
|
|
25
|
+
| `blockId` | `string` | Unique task block ID (required) |
|
|
26
|
+
| `debug` | `boolean` | Enable debug mode (optional) |
|
|
27
|
+
| `className` | `string` | Optional CSS class name (optional) |
|
|
28
|
+
| `onReward` | `EventHandler` | Reward event callback (optional) |
|
|
29
|
+
| `onError` | `EventHandler` | Error event callback (optional) |
|
|
30
|
+
| `onBannerNotFound` | `EventHandler` | No banner available callback (optional) |
|
|
31
|
+
| `onTooLongSession` | `EventHandler` | Long session callback (optional) |
|
|
32
|
+
|
|
33
|
+
#### Example
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { AdsgramTask } from '@adsgram/react';
|
|
37
|
+
|
|
38
|
+
const handleReward = () => {
|
|
39
|
+
// your code to reward user
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleError = () => {
|
|
43
|
+
// do nothing or handle error
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function MyTask() {
|
|
47
|
+
return (
|
|
48
|
+
<AdsgramTask blockId="task-123" className={styles.task} onReward={handleReward} onError={handleError}>
|
|
49
|
+
<span slot="reward" className={styles.task__reward}>1000 coins</span>
|
|
50
|
+
<div slot="button" className={styles.task__button}>go</div>
|
|
51
|
+
<div slot="claim" className={stylestask__button_claim}>claim</div>
|
|
52
|
+
<div slot="done" className={stylestask__button_done}>done</div>
|
|
53
|
+
</AdsgramTask>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Hook
|
|
59
|
+
|
|
60
|
+
### `useAdsgram`
|
|
61
|
+
|
|
62
|
+
#### Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Description |
|
|
65
|
+
| ------------------ | ------------------------------ | ------------------------------------------------- |
|
|
66
|
+
| `blockId` | `string` | Unique ad block ID (required) |
|
|
67
|
+
| `debug` | `boolean` | Enable debug mode (optional) |
|
|
68
|
+
| `debugBannerType` | `string` | Forced banner type in debug mode (optional) |
|
|
69
|
+
| `onReward` | `(event: CustomEvent) => void` | Handler for the `reward` event (optional) |
|
|
70
|
+
| `onError` | `(event: CustomEvent) => void` | Handler for the `error` event (optional) |
|
|
71
|
+
|
|
72
|
+
#### Returns
|
|
73
|
+
An object with methods to control the Adsgram ad:
|
|
74
|
+
- `show(): Promise<ShowPromiseResult>` — Shows the ad and resolves with the result.
|
|
75
|
+
- `addEventListener(event: string, handler: (event: CustomEvent) => void): void` — Add event listener.
|
|
76
|
+
- `removeEventListener(event: string, handler: (event: CustomEvent) => void): void` — Remove event listener.
|
|
77
|
+
- `destroy(): void` — Destroys the ad controller.
|
|
78
|
+
|
|
79
|
+
#### Example
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { useAdsgram } from '@adsgram/react';
|
|
83
|
+
|
|
84
|
+
function MyAdButton() {
|
|
85
|
+
const handleReward = () => {
|
|
86
|
+
// user watch ad till the end
|
|
87
|
+
// your code to reward user for rewarded format
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const handleError = () => {
|
|
91
|
+
// user get error during playing ad
|
|
92
|
+
// do nothing or whatever you want
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
const { show } = useAdsgram({ blockId: 'task-123', onReward: handleReward, onError: handleError });
|
|
96
|
+
|
|
97
|
+
return <button onClick={show}>Show Ad</button>;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BlockId, EventHandler } from "@adsgram/common";
|
|
2
|
+
import React, { JSX } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the AdsgramTask component
|
|
5
|
+
* @interface TaskProps
|
|
6
|
+
* @property {BlockId} blockId - The unique identifier for the task block
|
|
7
|
+
* @property {boolean} [debug] - Whether to enable debug mode
|
|
8
|
+
* @property {string} [className] - Additional CSS class names
|
|
9
|
+
* @property {React.ReactNode} [children] - Child elements to render inside the task component
|
|
10
|
+
* @property {EventHandler} [onReward] - Callback for reward events
|
|
11
|
+
* @property {EventHandler} [onBannerNotFound] - Callback for banner not found events
|
|
12
|
+
* @property {EventHandler} [onTooLongSession] - Callback for too long session events
|
|
13
|
+
* @property {EventHandler} [onError] - Callback for error events
|
|
14
|
+
*/
|
|
15
|
+
export interface TaskProps {
|
|
16
|
+
blockId: BlockId;
|
|
17
|
+
debug?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
onReward?: EventHandler;
|
|
21
|
+
onBannerNotFound?: EventHandler;
|
|
22
|
+
onTooLongSession?: EventHandler;
|
|
23
|
+
onError?: EventHandler;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A React component wrapper for the Adsgram Task custom element.
|
|
27
|
+
* Handles initialization and event binding for the Adsgram task functionality.
|
|
28
|
+
*
|
|
29
|
+
* @component
|
|
30
|
+
* @param {TaskProps} props - Component properties
|
|
31
|
+
* @returns {JSX.Element | null} The rendered task element or null if not ready
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* import { AdsgramTask } from "@adsgram/react";
|
|
36
|
+
*
|
|
37
|
+
* function Task() {
|
|
38
|
+
* const handleReward = (event: CustomEvent<string>) => {
|
|
39
|
+
* console.log("User earned reward:", event.detail);
|
|
40
|
+
* };
|
|
41
|
+
*
|
|
42
|
+
* const handleError = (event: CustomEvent<string>) => {
|
|
43
|
+
* console.error("Task error:", event.detail);
|
|
44
|
+
* };
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <AdsgramTask
|
|
48
|
+
* blockId="task-123"
|
|
49
|
+
* className={styles.task}
|
|
50
|
+
* onReward={handleReward}
|
|
51
|
+
* onError={handleError}
|
|
52
|
+
* >
|
|
53
|
+
* <span slot="reward" className={styles.task__reward}>
|
|
54
|
+
* 1000 coins
|
|
55
|
+
* </span>
|
|
56
|
+
* <div slot="button" className={styles.task__button}>
|
|
57
|
+
* go
|
|
58
|
+
* </div>
|
|
59
|
+
* <div slot="claim" className={styles.button_claim}>
|
|
60
|
+
* claim
|
|
61
|
+
* </div>
|
|
62
|
+
* <div slot="done" className={styles.task__button_done}>
|
|
63
|
+
* done
|
|
64
|
+
* </div>
|
|
65
|
+
* </AdsgramTask>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare const AdsgramTask: ({ blockId, debug, className, onReward, onBannerNotFound, onTooLongSession, onError, children, }: TaskProps) => JSX.Element | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getObjectKeys, } from "@adsgram/common";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* A React component wrapper for the Adsgram Task custom element.
|
|
6
|
+
* Handles initialization and event binding for the Adsgram task functionality.
|
|
7
|
+
*
|
|
8
|
+
* @component
|
|
9
|
+
* @param {TaskProps} props - Component properties
|
|
10
|
+
* @returns {JSX.Element | null} The rendered task element or null if not ready
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { AdsgramTask } from "@adsgram/react";
|
|
15
|
+
*
|
|
16
|
+
* function Task() {
|
|
17
|
+
* const handleReward = (event: CustomEvent<string>) => {
|
|
18
|
+
* console.log("User earned reward:", event.detail);
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* const handleError = (event: CustomEvent<string>) => {
|
|
22
|
+
* console.error("Task error:", event.detail);
|
|
23
|
+
* };
|
|
24
|
+
*
|
|
25
|
+
* return (
|
|
26
|
+
* <AdsgramTask
|
|
27
|
+
* blockId="task-123"
|
|
28
|
+
* className={styles.task}
|
|
29
|
+
* onReward={handleReward}
|
|
30
|
+
* onError={handleError}
|
|
31
|
+
* >
|
|
32
|
+
* <span slot="reward" className={styles.task__reward}>
|
|
33
|
+
* 1000 coins
|
|
34
|
+
* </span>
|
|
35
|
+
* <div slot="button" className={styles.task__button}>
|
|
36
|
+
* go
|
|
37
|
+
* </div>
|
|
38
|
+
* <div slot="claim" className={styles.button_claim}>
|
|
39
|
+
* claim
|
|
40
|
+
* </div>
|
|
41
|
+
* <div slot="done" className={styles.task__button_done}>
|
|
42
|
+
* done
|
|
43
|
+
* </div>
|
|
44
|
+
* </AdsgramTask>
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const AdsgramTask = ({ blockId, debug, className, onReward, onBannerNotFound, onTooLongSession, onError, children, }) => {
|
|
50
|
+
const taskRef = useRef(null);
|
|
51
|
+
const [ready, setReady] = useState(false);
|
|
52
|
+
// Wait for custom element to be defined
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
customElements.whenDefined("adsgram-task").then(() => {
|
|
55
|
+
setReady(true);
|
|
56
|
+
});
|
|
57
|
+
}, []);
|
|
58
|
+
// Setup and cleanup event listeners
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
const task = taskRef.current;
|
|
61
|
+
if (!task || !ready)
|
|
62
|
+
return;
|
|
63
|
+
const eventMap = {
|
|
64
|
+
reward: onReward,
|
|
65
|
+
onBannerNotFound: onBannerNotFound,
|
|
66
|
+
onTooLongSession: onTooLongSession,
|
|
67
|
+
onError: onError,
|
|
68
|
+
};
|
|
69
|
+
getObjectKeys(eventMap).forEach((eventName) => {
|
|
70
|
+
const handler = eventMap[eventName];
|
|
71
|
+
if (handler) {
|
|
72
|
+
task.addEventListener(eventName, handler);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return () => {
|
|
76
|
+
getObjectKeys(eventMap).forEach((eventName) => {
|
|
77
|
+
const handler = eventMap[eventName];
|
|
78
|
+
if (handler) {
|
|
79
|
+
task.removeEventListener(eventName, handler);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
}, [onReward, onBannerNotFound, onTooLongSession, onError, ready]);
|
|
84
|
+
// Don't render until custom element is ready
|
|
85
|
+
if (!ready)
|
|
86
|
+
return null;
|
|
87
|
+
return (_jsx("adsgram-task", { ref: taskRef, "data-debug": debug ?? false, "data-block-id": blockId, className: className, children: children }));
|
|
88
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A custom React hook for integrating with Adsgram ad service.
|
|
3
|
+
* Provides methods to show ads, manage event listeners, and clean up resources.
|
|
4
|
+
*
|
|
5
|
+
* @module useAdsgram
|
|
6
|
+
* @param {AdsgramProps} props - Initialization parameters for Adsgram
|
|
7
|
+
* @returns {AdsgramMethods} An object containing methods to interact with Adsgram
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
import type { AdsgramMethods, AdsgramProps } from "@adsgram/common";
|
|
11
|
+
/**
|
|
12
|
+
* React hook for managing Adsgram ad integration
|
|
13
|
+
* @param {AdsgramProps} props - Configuration parameters for Adsgram initialization
|
|
14
|
+
* @returns {AdsgramMethods} Methods to interact with the Adsgram sdk
|
|
15
|
+
* @param {string} params.blockId - Unique identifier for the ad block
|
|
16
|
+
* @param {boolean} [params.debug=false] - Enable debug mode
|
|
17
|
+
* @param {BannerType} [params.debugBannerType] - Forced banner type in debug mode
|
|
18
|
+
* @param {HandlerType} [params.onReward] - Callback when user completes a rewarded ad
|
|
19
|
+
* @param {HandlerType} [params.onError] - Callback when ad playback fails
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* basic usage example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { useAdsgram } from "@adsgram/react";
|
|
25
|
+
*
|
|
26
|
+
* function MyComponent() {
|
|
27
|
+
* const { show } = useAdsgram({
|
|
28
|
+
* blockId: "123",
|
|
29
|
+
* onReward: () => {
|
|
30
|
+
* // user watch ad till the end
|
|
31
|
+
* // your code to reward user for rewarded format
|
|
32
|
+
* },
|
|
33
|
+
* onError: () => {
|
|
34
|
+
* // user get error during playing ad
|
|
35
|
+
* // do nothing or whatever you want
|
|
36
|
+
* },
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* return (
|
|
40
|
+
* <button onClick={show}>Show Ad</button>
|
|
41
|
+
* );
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function useAdsgram({ blockId, debug, debugBannerType, onReward, onError, }: AdsgramProps): AdsgramMethods;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { loadScript, prepareInitParams } from "@adsgram/common";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* React hook for managing Adsgram ad integration
|
|
5
|
+
* @param {AdsgramProps} props - Configuration parameters for Adsgram initialization
|
|
6
|
+
* @returns {AdsgramMethods} Methods to interact with the Adsgram sdk
|
|
7
|
+
* @param {string} params.blockId - Unique identifier for the ad block
|
|
8
|
+
* @param {boolean} [params.debug=false] - Enable debug mode
|
|
9
|
+
* @param {BannerType} [params.debugBannerType] - Forced banner type in debug mode
|
|
10
|
+
* @param {HandlerType} [params.onReward] - Callback when user completes a rewarded ad
|
|
11
|
+
* @param {HandlerType} [params.onError] - Callback when ad playback fails
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* basic usage example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { useAdsgram } from "@adsgram/react";
|
|
17
|
+
*
|
|
18
|
+
* function MyComponent() {
|
|
19
|
+
* const { show } = useAdsgram({
|
|
20
|
+
* blockId: "123",
|
|
21
|
+
* onReward: () => {
|
|
22
|
+
* // user watch ad till the end
|
|
23
|
+
* // your code to reward user for rewarded format
|
|
24
|
+
* },
|
|
25
|
+
* onError: () => {
|
|
26
|
+
* // user get error during playing ad
|
|
27
|
+
* // do nothing or whatever you want
|
|
28
|
+
* },
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* return (
|
|
32
|
+
* <button onClick={show}>Show Ad</button>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function useAdsgram({ blockId, debug, debugBannerType, onReward, onError, }) {
|
|
38
|
+
const adController = useRef(null);
|
|
39
|
+
const initPromise = useRef(null);
|
|
40
|
+
const ensureInitialized = useCallback(async () => {
|
|
41
|
+
if (adController.current)
|
|
42
|
+
return adController.current;
|
|
43
|
+
if (initPromise.current)
|
|
44
|
+
return initPromise.current.then(() => {
|
|
45
|
+
if (!adController.current) {
|
|
46
|
+
throw new Error("Adsgram controller failed to initialize.");
|
|
47
|
+
}
|
|
48
|
+
return adController.current;
|
|
49
|
+
});
|
|
50
|
+
initPromise.current = loadScript().then(() => {
|
|
51
|
+
const initParams = prepareInitParams({ blockId, debug, debugBannerType });
|
|
52
|
+
if (!window.Adsgram) {
|
|
53
|
+
throw new Error("Adsgram is not available on the window object.");
|
|
54
|
+
}
|
|
55
|
+
adController.current = window.Adsgram.init(initParams);
|
|
56
|
+
if (onReward) {
|
|
57
|
+
adController.current.addEventListener("onReward", onReward);
|
|
58
|
+
}
|
|
59
|
+
if (onError) {
|
|
60
|
+
adController.current.addEventListener("onError", onError);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
await initPromise.current;
|
|
64
|
+
if (!adController.current) {
|
|
65
|
+
throw new Error("Adsgram controller failed to initialize.");
|
|
66
|
+
}
|
|
67
|
+
return adController.current;
|
|
68
|
+
}, [blockId, debug, debugBannerType, onError, onReward]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
ensureInitialized();
|
|
71
|
+
return () => {
|
|
72
|
+
const controller = adController.current;
|
|
73
|
+
if (!controller)
|
|
74
|
+
return;
|
|
75
|
+
controller.destroy();
|
|
76
|
+
if (onReward)
|
|
77
|
+
controller.removeEventListener("onReward", onReward);
|
|
78
|
+
if (onError)
|
|
79
|
+
controller.removeEventListener("onError", onError);
|
|
80
|
+
};
|
|
81
|
+
}, [ensureInitialized, onError, onReward]);
|
|
82
|
+
return {
|
|
83
|
+
show: async () => {
|
|
84
|
+
const controller = await ensureInitialized();
|
|
85
|
+
return controller.show();
|
|
86
|
+
},
|
|
87
|
+
addEventListener: (event, handler) => {
|
|
88
|
+
ensureInitialized().then((controller) => {
|
|
89
|
+
controller.addEventListener(event, handler);
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
removeEventListener: (event, handler) => {
|
|
93
|
+
adController.current?.removeEventListener(event, handler);
|
|
94
|
+
},
|
|
95
|
+
destroy: () => {
|
|
96
|
+
adController.current?.destroy();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AdsgramTaskElement } from "@adsgram/common";
|
|
2
|
+
declare global {
|
|
3
|
+
namespace JSX {
|
|
4
|
+
interface IntrinsicElements {
|
|
5
|
+
"adsgram-task": React.DetailedHTMLProps<React.HTMLAttributes<AdsgramTaskElement>, AdsgramTaskElement>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
declare module "react" {
|
|
10
|
+
namespace JSX {
|
|
11
|
+
interface IntrinsicElements {
|
|
12
|
+
"adsgram-task": React.DetailedHTMLProps<React.HTMLAttributes<AdsgramTaskElement>, AdsgramTaskElement>;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adsgram/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@adsgram/common": "1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^24.0.1",
|
|
19
|
+
"@types/react": "^19.1.6",
|
|
20
|
+
"react": "^19.1.0",
|
|
21
|
+
"typescript": "^5.8.3"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": ">=16.8.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"build": "pnpm clean && tsc",
|
|
29
|
+
"lint": "eslint .",
|
|
30
|
+
"typecheck": "tsc --noEmit"
|
|
31
|
+
}
|
|
32
|
+
}
|