@formicoidea/labre-framework-ddd-context-map 0.30.1 → 0.32.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/dist/commands.d.ts +4 -0
- package/dist/commands.js +108 -0
- package/dist/descriptor.d.ts +1 -1
- package/dist/descriptor.js +1 -1
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/toolbar/context-map-menu.d.ts +11 -9
- package/dist/toolbar/context-map-menu.js +19 -103
- package/dist/translations.d.ts +14 -0
- package/dist/translations.js +15 -0
- package/dist/view.js +5 -2
- package/package.json +3 -3
package/dist/commands.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { addBubble, addCloud, addLegend, addRelationship, CLOUD, CM_BUBBLE, CM_RELATIONSHIPS, placeDddElement, } from '@formicoidea/labre-ddd-shared';
|
|
2
|
+
import { svg } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* The Context Map palette as commands: the bounded-context bubble, the cloud,
|
|
5
|
+
* the nine relationship patterns and the notation legend (`docs/adr/0008`).
|
|
6
|
+
*/
|
|
7
|
+
const bubbleSwatch = svg `<svg viewBox="0 0 24 24" fill="none"><rect x="2" y="7" width="20" height="10" rx="5" fill="#e6f0fa" stroke="#2f6fb0" stroke-width="1.6"/></svg>`;
|
|
8
|
+
const cloudSwatch = svg `<svg viewBox="0 0 24 24" fill="none"><path d="M6 17 C3 17 2 14 4.5 12.5 C4 9 8 8 9.5 10 C11 6.5 16 7.5 16 11 C19 10.5 20.5 14 18 16 C18 17 16.5 17 15 17 Z" fill="#f0eef6" stroke="#6d6e71" stroke-width="1.4"/></svg>`;
|
|
9
|
+
const legendSwatch = svg `<svg viewBox="0 0 24 24" fill="none"><rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.6"/><circle cx="7" cy="9" r="1.6" fill="currentColor"/><circle cx="7" cy="14" r="1.6" fill="currentColor"/><path d="M11 9 H18 M11 14 H18" stroke="currentColor" stroke-width="1.4"/></svg>`;
|
|
10
|
+
const relationSwatch = (dashed, arrow) => svg `<svg viewBox="0 0 24 24" fill="none"><path d="M3 12 H${arrow ? 17 : 21}" stroke="currentColor" stroke-width="2" stroke-dasharray="${dashed ? '3 3' : '0'}"/>${arrow ? svg `<path d="M15 8 L21 12 L15 16" stroke="currentColor" stroke-width="2" fill="none"/>` : ''}</svg>`;
|
|
11
|
+
/** The notation legend's sections, built from the same relationship presets. */
|
|
12
|
+
function legendSections() {
|
|
13
|
+
const patRows = (kinds) => CM_RELATIONSHIPS.filter(r => kinds.includes(r.kind)).map((r) => ({
|
|
14
|
+
swatch: 'line',
|
|
15
|
+
color: '#1f2328',
|
|
16
|
+
label: `${r.abbrev} — ${r.label}`,
|
|
17
|
+
}));
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
title: 'Boundaries',
|
|
21
|
+
rows: [
|
|
22
|
+
{ swatch: 'square', color: CM_BUBBLE.fill, label: 'Bounded Context' },
|
|
23
|
+
{
|
|
24
|
+
swatch: 'square',
|
|
25
|
+
color: CLOUD.fill,
|
|
26
|
+
label: 'System / Big Ball of Mud',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: 'Mutually dependent',
|
|
32
|
+
rows: patRows(['partnership', 'sharedKernel']),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
title: 'Upstream → Downstream (U/D)',
|
|
36
|
+
rows: patRows([
|
|
37
|
+
'customerSupplier',
|
|
38
|
+
'conformist',
|
|
39
|
+
'acl',
|
|
40
|
+
'ohs',
|
|
41
|
+
'publishedLanguage',
|
|
42
|
+
]),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
title: 'Separate / no integration',
|
|
46
|
+
rows: patRows(['separateWays', 'bbom']),
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
const SPECS = [
|
|
51
|
+
{
|
|
52
|
+
id: 'addBoundedContext',
|
|
53
|
+
label: 'Bounded Context',
|
|
54
|
+
iconKey: 'ddd-context-map.bubble',
|
|
55
|
+
element: 'bounded-context',
|
|
56
|
+
icon: bubbleSwatch,
|
|
57
|
+
run: std => placeDddElement(std, (surface, cx, cy) => addBubble(surface, cx, cy, 'Bounded Context')),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'addCloud',
|
|
61
|
+
label: 'Cloud / System (Big Ball of Mud)',
|
|
62
|
+
iconKey: 'ddd-context-map.cloud',
|
|
63
|
+
element: 'cloud',
|
|
64
|
+
icon: cloudSwatch,
|
|
65
|
+
run: std => placeDddElement(std, (surface, cx, cy) => addCloud(surface, std, cx, cy, 'System')),
|
|
66
|
+
},
|
|
67
|
+
...CM_RELATIONSHIPS.map((preset) => ({
|
|
68
|
+
id: `add${preset.kind[0].toUpperCase()}${preset.kind.slice(1)}`,
|
|
69
|
+
label: preset.label,
|
|
70
|
+
iconKey: `ddd-context-map.relationship.${preset.kind}`,
|
|
71
|
+
element: `relationship:${preset.kind}`,
|
|
72
|
+
icon: relationSwatch(preset.dashed, preset.upDown),
|
|
73
|
+
run: std => placeDddElement(std, (surface, cx, cy) => addRelationship(surface, std, cx, cy, preset)),
|
|
74
|
+
})),
|
|
75
|
+
{
|
|
76
|
+
id: 'addLegend',
|
|
77
|
+
label: 'Legend',
|
|
78
|
+
iconKey: 'ddd-context-map.legend',
|
|
79
|
+
// Deliberately NOT `kind: 'legend'`: this palette entry has always emitted
|
|
80
|
+
// `FrameworkElementAdded` with element `'legend'`, and ADR 0008's
|
|
81
|
+
// no-analytics-breakage rule outranks the taxonomy tidy-up. Promoting it
|
|
82
|
+
// to `FrameworkLegendCreated` is a telemetry change, not a refactor.
|
|
83
|
+
element: 'legend',
|
|
84
|
+
icon: legendSwatch,
|
|
85
|
+
run: std => placeDddElement(std, (surface, cx, cy) => addLegend(surface, std, cx - 140, cy - 210, {
|
|
86
|
+
title: 'Légende',
|
|
87
|
+
sections: legendSections(),
|
|
88
|
+
width: 290,
|
|
89
|
+
})),
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
export const contextMapCommands = SPECS.map((spec, order) => ({
|
|
93
|
+
id: `ddd-context-map.${spec.id}`,
|
|
94
|
+
owner: 'ddd-context-map',
|
|
95
|
+
kind: 'artefact',
|
|
96
|
+
labelKey: `com.labre.commands.ddd-context-map.${spec.id}`,
|
|
97
|
+
labelFallback: spec.label,
|
|
98
|
+
category: 'map',
|
|
99
|
+
iconKey: spec.iconKey,
|
|
100
|
+
surfaces: ['senior-menu', 'catalogue', 'palette', 'agent'],
|
|
101
|
+
order,
|
|
102
|
+
scope: 'edgeless',
|
|
103
|
+
defaultKeys: { mac: [], other: [] },
|
|
104
|
+
availability: 'always',
|
|
105
|
+
run: spec.run,
|
|
106
|
+
telemetry: { framework: 'ddd-context-map', element: spec.element },
|
|
107
|
+
}));
|
|
108
|
+
export const contextMapCommandIcons = Object.fromEntries(SPECS.map(spec => [spec.iconKey, spec.icon]));
|
package/dist/descriptor.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import { DddContextMapViewExtension } from './view.js';
|
|
|
2
2
|
/** Host wiring for the ddd-context-map framework. */
|
|
3
3
|
export declare const dddContextMapFramework: {
|
|
4
4
|
readonly flag: "ddd-context-map";
|
|
5
|
-
readonly
|
|
5
|
+
readonly telemetryKey: "context-map";
|
|
6
6
|
readonly viewExtension: typeof DddContextMapViewExtension;
|
|
7
7
|
};
|
package/dist/descriptor.js
CHANGED
|
@@ -2,6 +2,6 @@ import { DddContextMapViewExtension } from './view.js';
|
|
|
2
2
|
/** Host wiring for the ddd-context-map framework. */
|
|
3
3
|
export const dddContextMapFramework = {
|
|
4
4
|
flag: 'ddd-context-map',
|
|
5
|
-
|
|
5
|
+
telemetryKey: 'context-map',
|
|
6
6
|
viewExtension: DddContextMapViewExtension,
|
|
7
7
|
};
|
package/dist/effects.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgelessDddContextMapMenu } from './toolbar/context-map-menu';
|
|
2
|
-
import { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button';
|
|
1
|
+
import { EdgelessDddContextMapMenu } from './toolbar/context-map-menu.js';
|
|
2
|
+
import { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button.js';
|
|
3
3
|
export declare function contextMapEffects(): void;
|
|
4
4
|
declare global {
|
|
5
5
|
interface HTMLElementTagNameMap {
|
package/dist/effects.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgelessDddContextMapMenu } from './toolbar/context-map-menu';
|
|
2
|
-
import { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button';
|
|
1
|
+
import { EdgelessDddContextMapMenu } from './toolbar/context-map-menu.js';
|
|
2
|
+
import { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button.js';
|
|
3
3
|
/** Define a custom element once (each tool's effect may run more than once). */
|
|
4
4
|
function define(tag, ctor) {
|
|
5
5
|
if (!customElements.get(tag))
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { contextMapCommandIcons, contextMapCommands } from './commands.js';
|
|
2
|
+
export { contextMapTranslationEntries } from './translations.js';
|
|
1
3
|
export { contextMapTemplateCategory } from './templates.js';
|
|
2
4
|
export { contextMapSeniorTool } from './toolbar/senior-tool.js';
|
|
3
5
|
export { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { contextMapCommandIcons, contextMapCommands } from './commands.js';
|
|
2
|
+
export { contextMapTranslationEntries } from './translations.js';
|
|
1
3
|
export { contextMapTemplateCategory } from './templates.js';
|
|
2
4
|
export { contextMapSeniorTool } from './toolbar/senior-tool.js';
|
|
3
5
|
export { EdgelessDddContextMapSeniorButton } from './toolbar/senior-button.js';
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
2
|
+
import { EdgelessCommandMenu } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
3
|
+
/**
|
|
4
|
+
* Context Map palette, rendered from `contextMapCommands` for the
|
|
5
|
+
* `senior-menu` surface (`docs/adr/0008`). Keeps the DDD palettes' tighter gap
|
|
6
|
+
* — it is the widest one, at 12 buttons.
|
|
7
|
+
*/
|
|
8
|
+
export declare class EdgelessDddContextMapMenu extends EdgelessCommandMenu {
|
|
9
|
+
static styles: import("lit").CSSResultGroup[];
|
|
10
|
+
protected owner: "ddd-context-map";
|
|
11
|
+
type: typeof EmptyTool;
|
|
10
12
|
}
|
|
@@ -1,107 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
2
|
+
import { EdgelessCommandMenu } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
3
|
+
import { css } from 'lit';
|
|
4
|
+
/**
|
|
5
|
+
* Context Map palette, rendered from `contextMapCommands` for the
|
|
6
|
+
* `senior-menu` surface (`docs/adr/0008`). Keeps the DDD palettes' tighter gap
|
|
7
|
+
* — it is the widest one, at 12 buttons.
|
|
8
|
+
*/
|
|
9
|
+
export class EdgelessDddContextMapMenu extends EdgelessCommandMenu {
|
|
9
10
|
constructor() {
|
|
10
11
|
super(...arguments);
|
|
11
|
-
this.
|
|
12
|
-
|
|
13
|
-
_createBubble() {
|
|
14
|
-
const surface = this.surface;
|
|
15
|
-
if (!surface)
|
|
16
|
-
return;
|
|
17
|
-
const { cx, cy } = this.center;
|
|
18
|
-
const id = addBubble(surface, cx, cy, 'Bounded Context');
|
|
19
|
-
this.track('FrameworkElementAdded', 'bounded-context');
|
|
20
|
-
this.finish(id);
|
|
21
|
-
}
|
|
22
|
-
_createCloud() {
|
|
23
|
-
const surface = this.surface;
|
|
24
|
-
if (!surface)
|
|
25
|
-
return;
|
|
26
|
-
const { cx, cy } = this.center;
|
|
27
|
-
const id = addCloud(surface, this.edgeless.std, cx, cy, 'System');
|
|
28
|
-
this.track('FrameworkElementAdded', 'cloud');
|
|
29
|
-
this.finish(id);
|
|
30
|
-
}
|
|
31
|
-
_createRelationship(preset) {
|
|
32
|
-
const surface = this.surface;
|
|
33
|
-
if (!surface)
|
|
34
|
-
return;
|
|
35
|
-
const { cx, cy } = this.center;
|
|
36
|
-
const id = addRelationship(surface, this.edgeless.std, cx, cy, preset);
|
|
37
|
-
this.track('FrameworkElementAdded', `relationship:${preset.kind}`);
|
|
38
|
-
this.finish(id);
|
|
39
|
-
}
|
|
40
|
-
_createLegend() {
|
|
41
|
-
const surface = this.surface;
|
|
42
|
-
if (!surface)
|
|
43
|
-
return;
|
|
44
|
-
const { cx, cy } = this.center;
|
|
45
|
-
const patRows = (kinds) => CM_RELATIONSHIPS.filter(r => kinds.includes(r.kind)).map((r) => ({
|
|
46
|
-
swatch: 'line',
|
|
47
|
-
color: '#1f2328',
|
|
48
|
-
label: `${r.abbrev} — ${r.label}`,
|
|
49
|
-
}));
|
|
50
|
-
const sections = [
|
|
51
|
-
{
|
|
52
|
-
title: 'Boundaries',
|
|
53
|
-
rows: [
|
|
54
|
-
{ swatch: 'square', color: CM_BUBBLE.fill, label: 'Bounded Context' },
|
|
55
|
-
{ swatch: 'square', color: CLOUD.fill, label: 'System / Big Ball of Mud' },
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
{ title: 'Mutually dependent', rows: patRows(['partnership', 'sharedKernel']) },
|
|
59
|
-
{
|
|
60
|
-
title: 'Upstream → Downstream (U/D)',
|
|
61
|
-
rows: patRows(['customerSupplier', 'conformist', 'acl', 'ohs', 'publishedLanguage']),
|
|
62
|
-
},
|
|
63
|
-
{ title: 'Separate / no integration', rows: patRows(['separateWays', 'bbom']) },
|
|
64
|
-
];
|
|
65
|
-
const id = addLegend(surface, this.edgeless.std, cx - 140, cy - 210, {
|
|
66
|
-
title: 'Légende',
|
|
67
|
-
sections,
|
|
68
|
-
width: 290,
|
|
69
|
-
});
|
|
70
|
-
this.track('FrameworkElementAdded', 'legend');
|
|
71
|
-
this.finish(id);
|
|
72
|
-
}
|
|
73
|
-
render() {
|
|
74
|
-
return html `
|
|
75
|
-
<edgeless-slide-menu>
|
|
76
|
-
<div class="menu-content">
|
|
77
|
-
<div class="button-group-container">
|
|
78
|
-
<edgeless-tool-icon-button
|
|
79
|
-
.tooltip=${'Bounded Context'}
|
|
80
|
-
@click=${() => this._createBubble()}
|
|
81
|
-
>
|
|
82
|
-
${bubbleSwatch}
|
|
83
|
-
</edgeless-tool-icon-button>
|
|
84
|
-
<edgeless-tool-icon-button
|
|
85
|
-
.tooltip=${'Cloud / System (Big Ball of Mud)'}
|
|
86
|
-
@click=${() => this._createCloud()}
|
|
87
|
-
>
|
|
88
|
-
${cloudSwatch}
|
|
89
|
-
</edgeless-tool-icon-button>
|
|
90
|
-
${CM_RELATIONSHIPS.map(preset => html `<edgeless-tool-icon-button
|
|
91
|
-
.tooltip=${preset.label}
|
|
92
|
-
@click=${() => this._createRelationship(preset)}
|
|
93
|
-
>
|
|
94
|
-
${relationSwatch(preset.dashed, preset.upDown)}
|
|
95
|
-
</edgeless-tool-icon-button>`)}
|
|
96
|
-
<edgeless-tool-icon-button
|
|
97
|
-
.tooltip=${'Legend'}
|
|
98
|
-
@click=${() => this._createLegend()}
|
|
99
|
-
>
|
|
100
|
-
${legendSwatch}
|
|
101
|
-
</edgeless-tool-icon-button>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
</edgeless-slide-menu>
|
|
105
|
-
`;
|
|
12
|
+
this.owner = 'ddd-context-map';
|
|
13
|
+
this.type = EmptyTool;
|
|
106
14
|
}
|
|
15
|
+
static { this.styles = [
|
|
16
|
+
EdgelessCommandMenu.styles,
|
|
17
|
+
css `
|
|
18
|
+
:host {
|
|
19
|
+
--labre-command-menu-gap: 10px;
|
|
20
|
+
}
|
|
21
|
+
`,
|
|
22
|
+
]; }
|
|
107
23
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type TranslationKeyManifestEntry } from '@formicoidea/labre-core/std';
|
|
2
|
+
/**
|
|
3
|
+
* THIS framework's contribution to the translation-key manifest.
|
|
4
|
+
*
|
|
5
|
+
* Its command labels and descriptions are built from a TEMPLATE, so the
|
|
6
|
+
* concrete keys exist nowhere but in the declarations themselves and the
|
|
7
|
+
* core manifest could not restate them even if it wanted to. The
|
|
8
|
+
* contribution therefore ships WITH the framework: in the bundled
|
|
9
|
+
* distribution `@formicoidea/labre-framework-ddd-context-map` carries it, and a host
|
|
10
|
+
* composes it into its catalogue exactly as it already composes
|
|
11
|
+
* `contextMapCommands` into the command registry. See
|
|
12
|
+
* `packages/affine/all/src/translations.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const contextMapTranslationEntries: TranslationKeyManifestEntry[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { commandTranslationEntries, } from '@formicoidea/labre-core/std';
|
|
2
|
+
import { contextMapCommands } from './commands.js';
|
|
3
|
+
/**
|
|
4
|
+
* THIS framework's contribution to the translation-key manifest.
|
|
5
|
+
*
|
|
6
|
+
* Its command labels and descriptions are built from a TEMPLATE, so the
|
|
7
|
+
* concrete keys exist nowhere but in the declarations themselves and the
|
|
8
|
+
* core manifest could not restate them even if it wanted to. The
|
|
9
|
+
* contribution therefore ships WITH the framework: in the bundled
|
|
10
|
+
* distribution `@formicoidea/labre-framework-ddd-context-map` carries it, and a host
|
|
11
|
+
* composes it into its catalogue exactly as it already composes
|
|
12
|
+
* `contextMapCommands` into the command registry. See
|
|
13
|
+
* `packages/affine/all/src/translations.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export const contextMapTranslationEntries = commandTranslationEntries(contextMapCommands);
|
package/dist/view.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { CommandExtension } from '@formicoidea/labre-core/std';
|
|
3
|
+
import { contextMapCommandIcons, contextMapCommands } from './commands.js';
|
|
4
|
+
import { contextMapEffects } from './effects.js';
|
|
5
|
+
import { contextMapSeniorTool } from './toolbar/senior-tool.js';
|
|
4
6
|
/**
|
|
5
7
|
* Context Map — independently flag-gated (`ddd-context-map`).
|
|
6
8
|
*
|
|
@@ -21,6 +23,7 @@ export class DddContextMapViewExtension extends ViewExtensionProvider {
|
|
|
21
23
|
super.setup(context);
|
|
22
24
|
if (this.isEdgeless(context.scope)) {
|
|
23
25
|
context.register(contextMapSeniorTool);
|
|
26
|
+
context.register(CommandExtension(contextMapCommands, contextMapCommandIcons));
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-framework-ddd-context-map",
|
|
3
3
|
"description": "Labre ddd-context-map framework for @formicoidea/labre-core.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.32.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@formicoidea/labre-core": "0.
|
|
31
|
-
"@formicoidea/labre-ddd-shared": "0.
|
|
30
|
+
"@formicoidea/labre-core": "0.32.0",
|
|
31
|
+
"@formicoidea/labre-ddd-shared": "0.32.0",
|
|
32
32
|
"lit": "^3.2.0"
|
|
33
33
|
}
|
|
34
34
|
}
|