chrono-phylo-tree 1.1.8 → 1.1.10

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.
@@ -1,234 +0,0 @@
1
- import { Species } from "../classes/Species";
2
- import { codeText } from "../utils/translate";
3
- import { LanguageSelector } from "./LanguageSelector";
4
-
5
- export const NavBar = ({
6
- species,
7
- color,
8
- lineColor,
9
- setLineColor,
10
- language,
11
- languages,
12
- setLanguage,
13
- minScale = 1e-12,
14
- maxScale,
15
- scale,
16
- setScale,
17
- showScaleNumber,
18
- chronoScale,
19
- setChronoScale,
20
- showHover,
21
- setShowHover,
22
- showImages,
23
- setShowImages,
24
- presentTime,
25
- presentTimeBoolean,
26
- setPresentTimeBoolean,
27
- changePresentTime,
28
- setFromJson,
29
- deleteAllSpecies,
30
- createEmptySpecies,
31
- showExample
32
- }: NavBarProps) => {
33
- return (
34
- <nav style={{
35
- backgroundColor: color,
36
- maxWidth: window.screen.width - 64,
37
- }}
38
- className={`flex flex-col sm:flex-row w-auto ${species ? "fixed" : "static"} p-2.5 box-border ${species ? "transform translate-z-0" : "transform-none"}`}
39
- >
40
- <table className="flex flex-col justify-start text-start">
41
- <tbody>
42
- <LanguageSelector
43
- className="block sm:hidden"
44
- languages={languages}
45
- language={language}
46
- setLanguage={setLanguage}
47
- />
48
- <tr className="block sm:hidden h-2.5"/>
49
- <tr>
50
- <td>{codeText("nvlbl00", language)}: </td>
51
- <td>
52
- <input
53
- type="range"
54
- min={minScale}
55
- max={maxScale}
56
- step={minScale}
57
- value={maxScale - scale + minScale}
58
- onChange={(e) => setScale(maxScale - Number(e.target.value) + minScale)}
59
- /> {showScaleNumber && <input
60
- type="number"
61
- min={1}
62
- max={maxScale}
63
- value={scale}
64
- onChange={(e) => setScale(Number(e.target.value))}
65
- />}
66
- </td>
67
- </tr>
68
- <tr className="h-2.5"/>
69
- <tr>
70
- <td>{codeText("nvlbl06", language)}: </td>
71
- <td>
72
- <input
73
- type="checkbox"
74
- checked={chronoScale}
75
- onChange={(e) => setChronoScale(e.target.checked)}
76
- />
77
- </td>
78
- </tr>
79
- <tr className="h-2.5"/>
80
- <tr>
81
- <td>{codeText("nvlbl07", language)}: </td>
82
- <td>
83
- <input
84
- type="checkbox"
85
- checked={showHover}
86
- onChange={(e) => setShowHover(e.target.checked)}
87
- />
88
- </td>
89
- </tr>
90
- <tr className="h-2.5"/>
91
- <tr>
92
- <td>{codeText("nvlbl08", language)}</td>
93
- <td>
94
- <input
95
- type="checkbox"
96
- checked={showImages}
97
- onChange={(e) => setShowImages(e.target.checked)}
98
- />
99
- </td>
100
- </tr>
101
- <tr className="h-2.5"/>
102
- <tr>
103
- <td>{codeText("nvlbl01", language)}: </td>
104
- <td>
105
- <input
106
- type="range"
107
- min={species ? species.apparition : 0}
108
- max={species ? species.absoluteExtinction() : 1}
109
- value={presentTime}
110
- onChange={(e) => changePresentTime(Number(e.target.value))}
111
- disabled={!presentTimeBoolean || !chronoScale}
112
- /> <input
113
- type="number"
114
- min={species ? species.apparition : 0}
115
- max={species ? species.absoluteExtinction() : 1}
116
- value={presentTime}
117
- onChange={(e) => changePresentTime(Number(e.target.value))}
118
- disabled={!presentTimeBoolean || !chronoScale}
119
- /> <input
120
- type="checkbox"
121
- checked={presentTimeBoolean}
122
- onChange={(e) => setPresentTimeBoolean(e.target.checked)}
123
- disabled={!chronoScale}
124
- />
125
- </td>
126
- </tr>
127
- </tbody>
128
- </table>
129
- <div className="h-2.5 sm:w-2.5 sm:h-auto"/>
130
- <table className="flex flex-col justify-start text-start">
131
- <tbody>
132
- <tr>
133
- <td>{codeText("nvlbl03", language)}: </td>
134
- <td>
135
- <a href="https://github.com/LUCHER4321/chrono-phylo-tree" target="_blank" className="ml-1.25 flex items-center">
136
- <img style={{maxHeight: 25}} src="https://img.logo.dev/github.com?token=pk_VXzZR_o_QTelazRSvSRkNw&format=png"/>
137
- </a>
138
- </td>
139
- </tr>
140
- <tr className="h-2.5"/>
141
- <tr>
142
- <td>{codeText("nvlbl04", language)}: </td>
143
- <td>
144
- <input
145
- type="file"
146
- accept=".json"
147
- value={undefined}
148
- onChange={async (e) => await setFromJson(e.target.files?.[0])}
149
- />
150
- </td>
151
- </tr>
152
- <tr className="h-2.5"/>
153
- <tr className="block sm:hidden">
154
- <td>{codeText("nvlbl02", language)}: </td>
155
- <td>
156
- <input
157
- type="color"
158
- value={lineColor}
159
- onChange={(e) => setLineColor(e.target.value)}
160
- />
161
- </td>
162
- </tr>
163
- </tbody>
164
- <tbody>
165
- <tr className="h-2.5 sm:h-auto"/>
166
- <tr className="flex flex-col sm:flex-row">
167
- <button type="button" onClick={async () => species ? deleteAllSpecies() : await createEmptySpecies()}>
168
- {codeText("nvbtn00" + (species ? "_0" : ""), language)}
169
- </button>
170
- <div className="h-2.5 sm:w-2.5 sm:h-auto"/>
171
- <button type="button" onClick={showExample}>
172
- {codeText("nvbtn01", language)}
173
- </button>
174
- <div className="h-2.5 sm:w-2.5 sm:h-auto"/>
175
- <button onClick={async () => await species?.saveJSON()} disabled={!species}>
176
- {codeText("nvbtn02", language)}
177
- </button>
178
- </tr>
179
- </tbody>
180
- </table>
181
- <div className="h-2.5 sm:w-2.5 sm:h-auto"/>
182
- <table className="flex flex-col justify-start text-start hidden sm:block">
183
- <tbody>
184
- <LanguageSelector
185
- languages={languages}
186
- language={language}
187
- setLanguage={setLanguage}
188
- />
189
- <tr className="h-2.5"/>
190
- <tr>
191
- <td>{codeText("nvlbl02", language)}:</td>
192
- <td>
193
- <input
194
- type="color"
195
- value={lineColor}
196
- onChange={(e) => setLineColor(e.target.value)}
197
- />
198
- </td>
199
- </tr>
200
- </tbody>
201
- </table>
202
- </nav>
203
- );
204
- }
205
-
206
- interface NavBarProps {
207
- species?: Species;
208
- color: string;
209
- lineColor: string;
210
- setLineColor: (lineColor: string) => void;
211
- language: string;
212
- languages: Map<string, string>;
213
- setLanguage: (language: string) => void;
214
- minScale?: number;
215
- maxScale: number;
216
- scale: number;
217
- setScale: (scale: number) => void;
218
- showScaleNumber: boolean;
219
- chronoScale: boolean;
220
- setChronoScale: (chronoScale: boolean) => void;
221
- showHover: boolean;
222
- setShowHover: (showHover: boolean) => void;
223
- showImages: boolean;
224
- setShowImages: (showImages: boolean) => void;
225
- presentTime: number;
226
- setPresentTime: (presentTime: number) => void;
227
- presentTimeBoolean: boolean;
228
- setPresentTimeBoolean: (presentTimeBoolean: boolean) => void;
229
- changePresentTime: (presentTime: number) => void;
230
- setFromJson: (file: File | undefined) => Promise<void>;
231
- deleteAllSpecies: () => void;
232
- createEmptySpecies: () => Promise<void>;
233
- showExample: () => void;
234
- }
@@ -1,362 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { Species } from "../classes/Species";
3
-
4
- interface MultiplePhTreesProps {
5
- speciesList: Species[];
6
- width?: number;
7
- height?: number;
8
- padding?: number;
9
- stroke?: string;
10
- format?: (n: number) => string;
11
- chronoScale?: boolean;
12
- showImages?: boolean;
13
- presentTime?: number;
14
- handleMouseMove?: (x: number, y: number) => void;
15
- children?: (
16
- species: Species | undefined,
17
- showMenu: boolean,
18
- toggleShowMenu: (species: Species) => void,
19
- hoverSpecies: Species | undefined
20
- ) => any;
21
- }
22
-
23
- export const MultiplePhTrees = (
24
- {
25
- speciesList,
26
- width = 1000,
27
- height = 50,
28
- padding = 0,
29
- stroke = "grey",
30
- format = (n) => n.toString(),
31
- chronoScale = true,
32
- showImages = true,
33
- presentTime,
34
- handleMouseMove,
35
- children
36
- }: MultiplePhTreesProps
37
- ) => {
38
- const copies = speciesList.map(sp => sp.copy());
39
- const lifeApparition = Math.min(...copies.map(sp => sp.apparition));
40
- const lifeApparitionDuration = Math.max(...copies.map(sp => sp.apparition - lifeApparition));
41
- const life = new Species(
42
- "",
43
- lifeApparition,
44
- lifeApparitionDuration,
45
- undefined,
46
- []
47
- );
48
- life.display = false;
49
- life.linkDescendants(copies);
50
- return (
51
- <PhTree
52
- commonAncestor={life}
53
- width={width}
54
- height={height}
55
- padding={padding}
56
- stroke={stroke}
57
- format={format}
58
- chronoScale={chronoScale}
59
- showImages={showImages}
60
- presentTime={presentTime}
61
- handleMouseMove={handleMouseMove}
62
- >
63
- {(species, showMenu, toggleShowMenu, hoverSpecies) => children?.(species, showMenu, toggleShowMenu, hoverSpecies)}
64
- </PhTree>
65
- );
66
- };
67
-
68
- interface PhTreeProps {
69
- commonAncestor: Species;
70
- width?: number;
71
- height?: number;
72
- padding?: number;
73
- stroke?: string;
74
- format?: (n: number) => string;
75
- chronoScale?: boolean;
76
- showImages?: boolean;
77
- presentTime?: number;
78
- handleMouseMove?: (x: number, y: number) => void;
79
- children?: (
80
- species: Species | undefined,
81
- showMenu: boolean,
82
- toggleShowMenu: (species: Species) => void,
83
- hoverSpecies: Species | undefined
84
- ) => any;
85
- }
86
-
87
- export const PhTree = (
88
- {
89
- commonAncestor,
90
- width = 1000,
91
- height = 50,
92
- padding = 0,
93
- stroke = "grey",
94
- format = (n) => n.toString(),
95
- chronoScale = true,
96
- showImages = true,
97
- presentTime,
98
- handleMouseMove,
99
- children
100
- }: PhTreeProps
101
- ) => {
102
- const [showMenu, setShowMenu] = useState(false);
103
- const [species, setSpecies] = useState<Species | undefined>(undefined);
104
- const [hoverSpecies, setHoverSpecies] = useState<Species | undefined>(undefined);
105
- const [showDesc, setShowDesc] = useState<Map<Species, boolean>>(commonAncestor.allDescendants().reduce((acc, desc) => acc.set(desc, true), new Map()));
106
-
107
- useEffect(() => {
108
- setShowDesc(commonAncestor.allDescendants().reduce((acc, desc) => acc.set(desc, true), new Map()));
109
- }, [commonAncestor]);
110
-
111
- const toggleShowMenu = (sp: Species) => {
112
- setSpecies(showMenu ? undefined : sp);
113
- setShowMenu(!showMenu);
114
- };
115
-
116
- const toggleShowDesc = (sp: Species) => {
117
- const newShowDesc = new Map(showDesc);
118
- newShowDesc.set(sp, !showDesc.get(sp));
119
- setShowDesc(newShowDesc);
120
- };
121
-
122
- const hoverShowMenu = (sp: Species | undefined) => {
123
- setHoverSpecies(sp);
124
- };
125
-
126
- const isPresentTimeDefined = presentTime !== undefined && chronoScale;
127
- const heightFactor = (ad: Species[]) => showImages ? ad.map<number>(sp => sp.image ? 2 : 1).reduce((a, b) => a + b) : ad.length;
128
-
129
- return (
130
- <>
131
- <svg
132
- width={width * (isPresentTimeDefined ? (Math.min(presentTime, commonAncestor.absoluteExtinction()) - commonAncestor.apparition) / commonAncestor.absoluteDuration() : 1)}
133
- height={height * heightFactor(isPresentTimeDefined ? commonAncestor.allDescendants().filter(desc => desc.apparition < presentTime) : commonAncestor.allDescendants())}
134
- onMouseMove={(event) => {handleMouseMove?.(event.clientX, event.clientY)}}
135
- >
136
- <DrawTree
137
- commonAncestor={commonAncestor}
138
- species={commonAncestor}
139
- y={-1}
140
- scaleX={width / (chronoScale ? commonAncestor.absoluteDuration() : (Math.max(0, commonAncestor.stepsUntilLastDescendant()) + 1))}
141
- scaleY={height}
142
- padding={padding}
143
- stroke={stroke}
144
- format={format}
145
- chronoScale={chronoScale}
146
- showImages={showImages}
147
- presentTime={presentTime}
148
- toggleShowMenu={toggleShowMenu}
149
- hoverShowMenu={hoverShowMenu}
150
- showDesc={showDesc}
151
- changeShowDesc={toggleShowDesc}
152
- />
153
- </svg>
154
- {children?.(species, showMenu, toggleShowMenu, hoverSpecies)}
155
- </>
156
- );
157
- };
158
-
159
- interface DrawTreeProps {
160
- commonAncestor: Species;
161
- species: Species;
162
- y: number;
163
- scaleX: number;
164
- scaleY: number;
165
- padding?: number;
166
- stroke?: string;
167
- format?: (n: number) => string;
168
- chronoScale?: boolean;
169
- showImages?: boolean;
170
- presentTime?: number;
171
- toggleShowMenu: (s: Species) => void;
172
- hoverShowMenu: (s: Species | undefined) => void;
173
- showDesc: Map<Species, boolean>;
174
- changeShowDesc: (s: Species) => void;
175
- }
176
-
177
- const DrawTree = ({
178
- commonAncestor,
179
- species,
180
- y,
181
- scaleX,
182
- scaleY,
183
- padding = 0,
184
- stroke = "grey",
185
- format = (n: number) => n.toString(),
186
- chronoScale = true,
187
- showImages = true,
188
- presentTime = undefined,
189
- toggleShowMenu,
190
- hoverShowMenu,
191
- showDesc,
192
- changeShowDesc
193
- }: DrawTreeProps) => {
194
- const isPresentTimeDefined = presentTime !== undefined && chronoScale;
195
- const all = commonAncestor.allDescendants().filter(desc => isPresentTimeDefined ? desc.apparition < presentTime : true);
196
- const spIndex = all.indexOf(species);
197
- const startX = (chronoScale ? species.apparition - commonAncestor.apparition : (commonAncestor.stepsUntil(species) ?? 0)) * scaleX;
198
- const endX = startX + (chronoScale ? Math.min(showDesc.get(species) ? species.duration : species.absoluteDuration(), isPresentTimeDefined ? presentTime - species.apparition : species.absoluteDuration()) : 1) * scaleX;
199
- const endY = (showImages && spIndex > 0) ? [...Array(spIndex).keys()].map(i => all[i]).map(sp => (sp.image ? 2 : 1) * scaleY).reduce((a, b) => a + b) : (spIndex * scaleY);
200
- const descendants = species.descendants.filter(desc => isPresentTimeDefined ? desc.apparition < presentTime : true);
201
- const branchX = descendants.length > 0 ? startX + (Math.min(...descendants.map(desc => desc.apparition)) - species.apparition) * scaleX : endX;
202
-
203
- return (
204
- <g key={spIndex}>
205
- {y >= 0 && (
206
- <line
207
- x1={startX}
208
- y1={y}
209
- x2={startX}
210
- y2={endY}
211
- stroke={stroke}
212
- />
213
- )}
214
- {species.display && <HorizontalLine
215
- commonAncestor={commonAncestor}
216
- species={species}
217
- height={scaleY}
218
- x1={startX}
219
- x2={endX}
220
- x0={branchX}
221
- y={endY}
222
- stroke={stroke}
223
- changeShowDesc={() => changeShowDesc(species)}
224
- showDesc={showDesc.get(species)}
225
- padding={padding}
226
- format={format}
227
- chronoScale={chronoScale}
228
- showImages={showImages}
229
- presentTime={presentTime}
230
- toggleShowMenu={toggleShowMenu}
231
- hoverShowMenu={hoverShowMenu}
232
- />}
233
- {species.descendants.map((desc, index) => (
234
- <g className={(showDesc.get(species) && descendants.includes(desc)) ? "block" : "hidden"} key={all.length + index}>
235
- <DrawTree
236
- commonAncestor={commonAncestor}
237
- species={desc}
238
- y={desc.display ? endY : -1}
239
- scaleX={scaleX}
240
- scaleY={scaleY}
241
- padding={padding}
242
- stroke={stroke}
243
- format={format}
244
- chronoScale={chronoScale}
245
- showImages={showImages}
246
- presentTime={presentTime}
247
- toggleShowMenu={toggleShowMenu}
248
- showDesc={showDesc}
249
- changeShowDesc={changeShowDesc}
250
- hoverShowMenu={hoverShowMenu}
251
- />
252
- </g>
253
- ))}
254
- </g>
255
- );
256
- };
257
-
258
- interface HorizontalLineProps {
259
- commonAncestor: Species;
260
- species: Species;
261
- height: number;
262
- x1: number;
263
- x2: number;
264
- x0?: number;
265
- y: number;
266
- stroke: string;
267
- showDesc?: boolean;
268
- changeShowDesc?: () => void;
269
- padding?: number;
270
- format?: (n: number) => string;
271
- chronoScale?: boolean;
272
- showImages?: boolean;
273
- presentTime?: number;
274
- toggleShowMenu: (s: Species) => void;
275
- hoverShowMenu: (s: Species | undefined) => void;
276
- className?: string;
277
- buttonClassName?: string;
278
- }
279
-
280
- const HorizontalLine = ({
281
- commonAncestor,
282
- species,
283
- height,
284
- x1, x2, x0, y,
285
- stroke,
286
- showDesc = true,
287
- changeShowDesc = () => {},
288
- padding = 0,
289
- format = (n) => n.toString(),
290
- chronoScale = true,
291
- showImages = true,
292
- presentTime,
293
- toggleShowMenu,
294
- hoverShowMenu,
295
- className,
296
- buttonClassName
297
- }: HorizontalLineProps) => {
298
- const isPresentTimeDefined = presentTime !== undefined;
299
- const all = commonAncestor.allDescendants().filter(desc => isPresentTimeDefined ? desc.apparition < presentTime : true);
300
- const index = (s: Species) => all.indexOf(s);
301
- const orientation = species.ancestor ? (index(species) > index(species.ancestor) ? -3 : 1) : 1;
302
- const descendants = species.descendants.filter(desc => isPresentTimeDefined ? desc.apparition < presentTime : true);
303
- const lastOne = descendants.filter(desc => desc.apparition === species.extinction()).length === 0;
304
- const extinction = format(Math.min(showDesc ? species.extinction() : species.absoluteExtinction(), isPresentTimeDefined ? presentTime : species.absoluteExtinction()));
305
- return (
306
- <g>
307
- <line
308
- x1={x1}
309
- y1={y}
310
- x2={x2}
311
- y2={y}
312
- stroke={stroke}
313
- />
314
- <foreignObject
315
- x={x1 + padding}
316
- y={y + (padding) * orientation}
317
- width={(chronoScale ? x0 ?? x2 : x2) - x1 - 2 * padding}
318
- height={height + (showImages && species.image ? height : 0)}
319
- >
320
- <div className={`flex flex-row justify-between w-full ${className ?? ""}`}>
321
- <div>
322
- {chronoScale ? format(species.apparition) : ""}
323
- </div>
324
- <button
325
- className={`p-0.625 justify-center flex flex-col items-center ${buttonClassName ?? ""}`}
326
- onClick={() => toggleShowMenu(species)}
327
- onMouseEnter={() => hoverShowMenu(species)}
328
- onMouseLeave={() => hoverShowMenu(undefined)}
329
- >
330
- {species.name}
331
- {species.image && showImages && (
332
- <img
333
- src={species.image}
334
- alt={species.name}
335
- style={{ height: height }}
336
- />
337
- )}
338
- </button>
339
- {descendants.length > 0 ?
340
- <button onClick={changeShowDesc} className="h-1" style={{maxWidth: 4}}>
341
- {((lastOne || !showDesc) && (!x0 || x0 === x2)) ? extinction : ""}
342
- </button> :
343
- <div>
344
- {chronoScale ? extinction : ""}
345
- </div>
346
- }
347
- </div>
348
- </foreignObject>
349
- {chronoScale && x0 && x0 < x2 && (
350
- <foreignObject
351
- x={x0 + padding}
352
- y={y + padding * orientation}
353
- width={x2 - x0 - 2 * padding}
354
- height={height}
355
- >
356
- <div className="flex flex-row justify-end w-full">
357
- {(lastOne || !showDesc) && chronoScale ? extinction : ""}
358
- </div>
359
- </foreignObject>)}
360
- </g>
361
- );
362
- }
package/src/index.css DELETED
@@ -1,68 +0,0 @@
1
- :root {
2
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- }
15
-
16
- a {
17
- font-weight: 500;
18
- color: #646cff;
19
- text-decoration: inherit;
20
- }
21
- a:hover {
22
- color: #535bf2;
23
- }
24
-
25
- body {
26
- margin: 0;
27
- display: flex;
28
- place-items: center;
29
- min-width: 320px;
30
- min-height: 100vh;
31
- }
32
-
33
- h1 {
34
- font-size: 3.2em;
35
- line-height: 1.1;
36
- }
37
-
38
- button {
39
- border-radius: 8px;
40
- border: 1px solid transparent;
41
- padding: 0.6em 1.2em;
42
- font-size: 1em;
43
- font-weight: 500;
44
- font-family: inherit;
45
- background-color: #1a1a1a;
46
- cursor: pointer;
47
- transition: border-color 0.25s;
48
- }
49
- button:hover {
50
- border-color: #646cff;
51
- }
52
- button:focus,
53
- button:focus-visible {
54
- outline: 4px auto -webkit-focus-ring-color;
55
- }
56
-
57
- @media (prefers-color-scheme: light) {
58
- :root {
59
- color: #213547;
60
- background-color: #ffffff;
61
- }
62
- a:hover {
63
- color: #747bff;
64
- }
65
- button {
66
- background-color: #f9f9f9;
67
- }
68
- }
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { Species } from "./classes/Species";
2
- export { PhTree, MultiplePhTrees } from "./components/PhTree";
3
- export { Menu } from "./components/Menu";
4
- export { codeText, codeTextAlt, getLanguageOptions } from "./utils/translate";
package/src/main.tsx DELETED
@@ -1,10 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import './index.css'
4
- import App from './App'
5
-
6
- createRoot(document.getElementById('root')!).render(
7
- <StrictMode>
8
- <App />
9
- </StrictMode>,
10
- )
package/src/types.d.ts DELETED
@@ -1,9 +0,0 @@
1
- export interface SpeciesJSON {
2
- name: string;
3
- apparition?: number;
4
- duration?: number;
5
- description?: string;
6
- descendants?: SpeciesJSON[];
7
- afterApparition?: number;
8
- image?: string;
9
- }
@@ -1,3 +0,0 @@
1
- export const between = (n: number, min: number, max: number) => {
2
- return n < min ? min : n > max ? max : n;
3
- };