@knime/hub-features 1.27.0 → 1.29.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/CHANGELOG.md +32 -0
- package/package.json +4 -4
- package/src/analytics/analytics.ts +39 -13
- package/src/analytics/schema/cloudhome-event-functions.ts +245 -0
- package/src/analytics/schema/code-generator.js +17 -8
- package/src/analytics/schema/{event-functions.ts → editor-event-functions.ts} +7 -9
- package/src/analytics/schema/schema.d.ts +133 -7
- package/src/analytics/schema/schema.json +833 -157
- package/src/analytics/types.ts +4 -1
- package/src/httpClient/constants.ts +1 -0
- package/src/{common/ofetchClient.ts → httpClient/createHttpClient.ts} +6 -5
- package/src/httpClient/index.ts +2 -0
- package/src/index.ts +1 -0
- package/src/useDownloadArtifact/useDownloadArtifact.ts +2 -2
- package/src/useFileUpload/useFileUpload.ts +2 -2
- package/src/common/constants.ts +0 -1
- package/src/components/versions/components/CreateVersionForm.vue +0 -155
- package/src/components/versions/components/CurrentState.vue +0 -258
- package/src/components/versions/components/LabelList.vue +0 -253
- package/src/components/versions/components/ManageVersions.vue +0 -191
- package/src/components/versions/components/NoVersionItem.vue +0 -23
- package/src/components/versions/components/VersionHistory.vue +0 -138
- package/src/components/versions/components/VersionItem.vue +0 -278
- package/src/components/versions/components/VersionLimitInfo.vue +0 -37
- package/src/components/versions/composables/useVersionsApi.ts +0 -254
- package/src/components/versions/constants.ts +0 -3
- package/src/components/versions/index.ts +0 -7
- package/src/components/versions/types.ts +0 -77
package/src/analytics/types.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { AnalyticsEventSchema } from "./schema/schema";
|
|
2
2
|
|
|
3
|
-
export type DefaultContext =
|
|
3
|
+
export type DefaultContext = { eventSource: "cloudhome" | "editor" } & Record<
|
|
4
|
+
string,
|
|
5
|
+
string | boolean | number
|
|
6
|
+
>;
|
|
4
7
|
export type Metadata = Omit<AnalyticsEventSchema, "events">;
|
|
5
8
|
|
|
6
9
|
type NewEvents = AnalyticsEventSchema["events"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_BASE_URL = "/_/api";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { merge } from "lodash-es"; // eslint-disable-line depend/ban-dependencies
|
|
2
2
|
import { type FetchOptions, ofetch } from "ofetch";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { API_BASE_URL } from "./constants";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const defaultConfig: FetchOptions = {
|
|
7
7
|
headers: {
|
|
8
8
|
"Content-Type": "application/json",
|
|
9
9
|
|
|
@@ -12,11 +12,12 @@ export const defaultConfig: FetchOptions = {
|
|
|
12
12
|
},
|
|
13
13
|
} as const;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const { baseURL =
|
|
17
|
-
customOptions ?? {};
|
|
15
|
+
const createHttpClient = (customOptions?: FetchOptions) => {
|
|
16
|
+
const { baseURL = API_BASE_URL, ...otherCustomOptions } = customOptions ?? {};
|
|
18
17
|
|
|
19
18
|
return ofetch.create(
|
|
20
19
|
merge({ baseURL, ...otherCustomOptions }, defaultConfig),
|
|
21
20
|
);
|
|
22
21
|
};
|
|
22
|
+
|
|
23
|
+
export { type FetchOptions, defaultConfig, createHttpClient };
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { FetchError, type FetchOptions } from "ofetch";
|
|
|
4
4
|
import type { DownloadItem as DownloadItemExternal } from "@knime/components";
|
|
5
5
|
import { sleep } from "@knime/utils";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { createHttpClient } from "../httpClient/createHttpClient";
|
|
8
8
|
import { rfcErrors } from "../rfcErrors";
|
|
9
9
|
|
|
10
10
|
const DEFAULT_MAX_RETRIES = null;
|
|
@@ -91,7 +91,7 @@ const isAbortError = (error: unknown) =>
|
|
|
91
91
|
export const useDownloadArtifact = (
|
|
92
92
|
options: UseDownloadArtifactOptions = {},
|
|
93
93
|
) => {
|
|
94
|
-
const $ofetch =
|
|
94
|
+
const $ofetch = createHttpClient(options.customFetchClientOptions);
|
|
95
95
|
|
|
96
96
|
const downloadItems = ref<Record<string, DownloadItem>>({});
|
|
97
97
|
const abortControllers: Record<string, AbortController> = {};
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
promise,
|
|
10
10
|
} from "@knime/utils";
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { createHttpClient } from "../httpClient/createHttpClient";
|
|
13
13
|
import { rfcErrors } from "../rfcErrors";
|
|
14
14
|
|
|
15
15
|
const DEFAULT_MAX_UPLOAD_QUEUE_SIZE = 10;
|
|
@@ -120,7 +120,7 @@ type UseFileUploadOptions = {
|
|
|
120
120
|
* ```
|
|
121
121
|
*/
|
|
122
122
|
export const useFileUpload = (options: UseFileUploadOptions = {}) => {
|
|
123
|
-
const $ofetch =
|
|
123
|
+
const $ofetch = createHttpClient(options.customFetchClientOptions);
|
|
124
124
|
|
|
125
125
|
const prepareUpload = (
|
|
126
126
|
parentId: string,
|
package/src/common/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_API_BASE_URL = "/_/api";
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, nextTick, onMounted, ref } from "vue";
|
|
3
|
-
import { useTextareaAutosize } from "@vueuse/core";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
InputField,
|
|
7
|
-
Label,
|
|
8
|
-
SideDrawerControls,
|
|
9
|
-
SideDrawerHeader,
|
|
10
|
-
TextArea,
|
|
11
|
-
} from "@knime/components";
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
isCreationPending?: boolean;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
withDefaults(defineProps<Props>(), { isCreationPending: false });
|
|
18
|
-
|
|
19
|
-
const emit = defineEmits<{
|
|
20
|
-
create: [{ name: string; description: string }];
|
|
21
|
-
cancel: [hasUnsavedChanges: boolean];
|
|
22
|
-
}>();
|
|
23
|
-
|
|
24
|
-
const versionName = ref("");
|
|
25
|
-
const versionDescription = ref("");
|
|
26
|
-
|
|
27
|
-
const descriptionEditor = ref<InstanceType<typeof TextArea> | null>(null);
|
|
28
|
-
|
|
29
|
-
onMounted(() => {
|
|
30
|
-
useTextareaAutosize({
|
|
31
|
-
element: descriptionEditor.value?.$el.getElementsByTagName("textarea")[0],
|
|
32
|
-
input: versionDescription,
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const doValidate = ref(false);
|
|
37
|
-
|
|
38
|
-
const isVersionNameInvalid = computed(() => {
|
|
39
|
-
return doValidate.value && versionName.value.length <= 0;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const hasUnsavedChanges = computed(() => {
|
|
43
|
-
return versionName.value !== "" || versionDescription.value !== "";
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const onCreate = async () => {
|
|
47
|
-
doValidate.value = true;
|
|
48
|
-
await nextTick();
|
|
49
|
-
|
|
50
|
-
if (!isVersionNameInvalid.value) {
|
|
51
|
-
emit("create", {
|
|
52
|
-
name: versionName.value,
|
|
53
|
-
description: versionDescription.value,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
</script>
|
|
58
|
-
|
|
59
|
-
<template>
|
|
60
|
-
<div class="create-version-form">
|
|
61
|
-
<SideDrawerHeader
|
|
62
|
-
title="Create Version"
|
|
63
|
-
description="Give your version a name and description. Versions can be used to create a deployment.
|
|
64
|
-
"
|
|
65
|
-
:is-sub-drawer="true"
|
|
66
|
-
@close="$emit('cancel', hasUnsavedChanges)"
|
|
67
|
-
/>
|
|
68
|
-
<div class="form-content">
|
|
69
|
-
<Label text="Version name">
|
|
70
|
-
<InputField
|
|
71
|
-
v-model="versionName"
|
|
72
|
-
type="text"
|
|
73
|
-
required
|
|
74
|
-
:is-valid="!isVersionNameInvalid"
|
|
75
|
-
/>
|
|
76
|
-
<div v-if="isVersionNameInvalid" class="error">
|
|
77
|
-
Please enter at least 1 character.
|
|
78
|
-
</div>
|
|
79
|
-
</Label>
|
|
80
|
-
<Label text="Description">
|
|
81
|
-
<TextArea
|
|
82
|
-
ref="descriptionEditor"
|
|
83
|
-
v-model="versionDescription"
|
|
84
|
-
class="description"
|
|
85
|
-
/>
|
|
86
|
-
</Label>
|
|
87
|
-
<div class="controls">
|
|
88
|
-
<SideDrawerControls
|
|
89
|
-
class="create-version-controls"
|
|
90
|
-
:edit-enabled="true"
|
|
91
|
-
:processing="isCreationPending"
|
|
92
|
-
action-label="Create"
|
|
93
|
-
:disabled="isVersionNameInvalid || isCreationPending"
|
|
94
|
-
@save="onCreate"
|
|
95
|
-
@cancel="$emit('cancel', hasUnsavedChanges)"
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
</template>
|
|
101
|
-
|
|
102
|
-
<style lang="postcss" scoped>
|
|
103
|
-
.create-version-form {
|
|
104
|
-
display: flex;
|
|
105
|
-
flex-direction: column;
|
|
106
|
-
width: 100%;
|
|
107
|
-
height: 100%;
|
|
108
|
-
max-height: 100%;
|
|
109
|
-
|
|
110
|
-
& .form-content {
|
|
111
|
-
z-index: 1;
|
|
112
|
-
display: flex;
|
|
113
|
-
flex-direction: column;
|
|
114
|
-
gap: 30px;
|
|
115
|
-
height: 100%;
|
|
116
|
-
padding: 30px 30px 0;
|
|
117
|
-
overflow: auto;
|
|
118
|
-
overscroll-behavior: none;
|
|
119
|
-
background-color: var(--knime-gray-ultra-light);
|
|
120
|
-
isolation: isolate;
|
|
121
|
-
|
|
122
|
-
& .description {
|
|
123
|
-
display: flex;
|
|
124
|
-
width: 100%;
|
|
125
|
-
max-width: unset;
|
|
126
|
-
|
|
127
|
-
& :deep(textarea) {
|
|
128
|
-
box-sizing: content-box;
|
|
129
|
-
width: 100%;
|
|
130
|
-
max-height: 200px;
|
|
131
|
-
resize: none;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
& .error {
|
|
136
|
-
position: absolute;
|
|
137
|
-
margin-top: 6px;
|
|
138
|
-
font-size: 13px;
|
|
139
|
-
font-weight: 300;
|
|
140
|
-
line-height: 18px;
|
|
141
|
-
color: var(--theme-color-error);
|
|
142
|
-
word-break: keep-all;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
& .controls {
|
|
146
|
-
margin: 0 -30px;
|
|
147
|
-
margin-top: auto;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.create-version-controls {
|
|
151
|
-
margin: 0;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
</style>
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
|
|
4
|
-
import { Button, LocalDateTime, SubMenu } from "@knime/components";
|
|
5
|
-
import MenuIcon from "@knime/styles/img/icons/menu-options.svg";
|
|
6
|
-
import WrenchIcon from "@knime/styles/img/icons/wrench.svg";
|
|
7
|
-
|
|
8
|
-
import HubAvatar from "../../avatars/HubAvatar.vue";
|
|
9
|
-
import type { ItemSavepoint, WithAvatar, WithLabels } from "../types";
|
|
10
|
-
|
|
11
|
-
import LabelList from "./LabelList.vue";
|
|
12
|
-
|
|
13
|
-
const props = defineProps<{
|
|
14
|
-
isSelected: boolean;
|
|
15
|
-
hasEditCapability: boolean;
|
|
16
|
-
hasPreviousVersion: boolean;
|
|
17
|
-
currentStateSavepoint: ItemSavepoint & WithAvatar & WithLabels;
|
|
18
|
-
isVersionLimitExceeded?: boolean;
|
|
19
|
-
}>();
|
|
20
|
-
|
|
21
|
-
const emit = defineEmits<{
|
|
22
|
-
createVersion: [];
|
|
23
|
-
discard: [];
|
|
24
|
-
select: [];
|
|
25
|
-
}>();
|
|
26
|
-
|
|
27
|
-
const menuItems = computed(() => {
|
|
28
|
-
return props.hasEditCapability && props.hasPreviousVersion
|
|
29
|
-
? [
|
|
30
|
-
{
|
|
31
|
-
text: "Discard",
|
|
32
|
-
action: () => emit("discard"),
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
: [];
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const onMenuItemClick = (_: Event, item: (typeof menuItems.value)[number]) => {
|
|
39
|
-
item.action();
|
|
40
|
-
};
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<template>
|
|
44
|
-
<div class="wrapper">
|
|
45
|
-
<div
|
|
46
|
-
class="current-state"
|
|
47
|
-
:class="{ selected: isSelected }"
|
|
48
|
-
@click="!isSelected && $emit('select')"
|
|
49
|
-
>
|
|
50
|
-
<div class="left">
|
|
51
|
-
<div class="draft">
|
|
52
|
-
<h6><WrenchIcon class="draft-icon" /><span>Draft</span></h6>
|
|
53
|
-
|
|
54
|
-
<div class="labels">
|
|
55
|
-
<LabelList :labels="currentStateSavepoint.labels" />
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<span class="date">
|
|
59
|
-
Latest edits on
|
|
60
|
-
<LocalDateTime
|
|
61
|
-
class="date"
|
|
62
|
-
show-time
|
|
63
|
-
:date="currentStateSavepoint.changes[0].createdOn"
|
|
64
|
-
/>
|
|
65
|
-
</span>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
<div class="right">
|
|
69
|
-
<Button
|
|
70
|
-
v-if="hasEditCapability && !isVersionLimitExceeded"
|
|
71
|
-
with-border
|
|
72
|
-
compact
|
|
73
|
-
class="create-button"
|
|
74
|
-
@click.stop="$emit('createVersion')"
|
|
75
|
-
>
|
|
76
|
-
Create version
|
|
77
|
-
</Button>
|
|
78
|
-
<HubAvatar
|
|
79
|
-
class="profile-picture"
|
|
80
|
-
v-bind="currentStateSavepoint.avatar"
|
|
81
|
-
/>
|
|
82
|
-
<div v-if="menuItems.length" class="controls">
|
|
83
|
-
<nav>
|
|
84
|
-
<SubMenu
|
|
85
|
-
:items="menuItems"
|
|
86
|
-
button-title="Options"
|
|
87
|
-
@item-click="onMenuItemClick"
|
|
88
|
-
>
|
|
89
|
-
<MenuIcon class="open-icon" />
|
|
90
|
-
</SubMenu>
|
|
91
|
-
</nav>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</template>
|
|
97
|
-
|
|
98
|
-
<style lang="postcss" scoped>
|
|
99
|
-
@import url("@knime/styles/css/mixins.css");
|
|
100
|
-
|
|
101
|
-
.wrapper {
|
|
102
|
-
position: relative;
|
|
103
|
-
margin-left: 30px;
|
|
104
|
-
|
|
105
|
-
&::before {
|
|
106
|
-
position: absolute;
|
|
107
|
-
top: 0;
|
|
108
|
-
left: -22px;
|
|
109
|
-
width: 2px;
|
|
110
|
-
height: calc(100% + 30px);
|
|
111
|
-
content: "";
|
|
112
|
-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 5 5'%3E%3Ccircle cx='1' cy='1' r='.5' fill='%236E6E6E'/%3E%3C/svg%3E");
|
|
113
|
-
background-repeat: repeat-y;
|
|
114
|
-
background-size: 5px;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
& .current-state {
|
|
118
|
-
display: flex;
|
|
119
|
-
gap: 10px;
|
|
120
|
-
align-items: center;
|
|
121
|
-
justify-content: space-between;
|
|
122
|
-
padding: 10px;
|
|
123
|
-
background-color: var(--knime-gray-ultra-light);
|
|
124
|
-
transition: background-color 0.25s ease;
|
|
125
|
-
|
|
126
|
-
& .left {
|
|
127
|
-
& .draft {
|
|
128
|
-
display: flex;
|
|
129
|
-
flex-direction: column;
|
|
130
|
-
gap: 3px;
|
|
131
|
-
word-break: normal;
|
|
132
|
-
overflow-wrap: anywhere;
|
|
133
|
-
|
|
134
|
-
& h6 {
|
|
135
|
-
display: flex;
|
|
136
|
-
align-items: center;
|
|
137
|
-
margin: 0;
|
|
138
|
-
font-size: 13px;
|
|
139
|
-
font-weight: 700;
|
|
140
|
-
line-height: 18px;
|
|
141
|
-
transition: color 0.25s ease;
|
|
142
|
-
|
|
143
|
-
& .draft-icon {
|
|
144
|
-
@mixin svg-icon-size 20;
|
|
145
|
-
|
|
146
|
-
padding: 4px;
|
|
147
|
-
margin-right: 4px;
|
|
148
|
-
background: var(--knime-carrot-light);
|
|
149
|
-
border-radius: 1000px;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
& .labels {
|
|
154
|
-
display: flex;
|
|
155
|
-
flex-flow: row wrap;
|
|
156
|
-
gap: 5px;
|
|
157
|
-
align-items: center;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
& .date {
|
|
161
|
-
margin: 0;
|
|
162
|
-
font-size: 11px;
|
|
163
|
-
font-weight: 300;
|
|
164
|
-
line-height: 16px;
|
|
165
|
-
transition: color 0.25s ease;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
& .right {
|
|
171
|
-
display: flex;
|
|
172
|
-
gap: 8px;
|
|
173
|
-
align-items: center;
|
|
174
|
-
justify-content: flex-end;
|
|
175
|
-
|
|
176
|
-
& .create-button {
|
|
177
|
-
white-space: nowrap;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
& .controls {
|
|
181
|
-
display: flex;
|
|
182
|
-
flex-direction: column;
|
|
183
|
-
justify-content: center;
|
|
184
|
-
padding-left: 5px;
|
|
185
|
-
margin-right: -4px;
|
|
186
|
-
border-left: 1px solid var(--knime-silver-sand);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
&::before {
|
|
191
|
-
position: absolute;
|
|
192
|
-
top: 12px;
|
|
193
|
-
left: -6px;
|
|
194
|
-
width: 12px;
|
|
195
|
-
height: 12px;
|
|
196
|
-
content: "";
|
|
197
|
-
background-color: var(--knime-gray-ultra-light);
|
|
198
|
-
transform: rotateZ(45deg);
|
|
199
|
-
transition: background-color 0.25s ease;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
&::after {
|
|
203
|
-
position: absolute;
|
|
204
|
-
top: 15px;
|
|
205
|
-
left: -24px;
|
|
206
|
-
width: 6px;
|
|
207
|
-
height: 6px;
|
|
208
|
-
content: "";
|
|
209
|
-
background-color: var(--knime-masala);
|
|
210
|
-
border-radius: 100%;
|
|
211
|
-
box-shadow: 0 0 0 0 transparent;
|
|
212
|
-
transition:
|
|
213
|
-
background-color 0.25s ease,
|
|
214
|
-
box-shadow 0.25s ease;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
&:hover {
|
|
218
|
-
cursor: pointer;
|
|
219
|
-
box-shadow: 0 2px 10px 0 var(--knime-gray-dark-semi);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
&.selected {
|
|
223
|
-
background-color: var(--knime-cornflower-semi);
|
|
224
|
-
|
|
225
|
-
& .left {
|
|
226
|
-
& h6,
|
|
227
|
-
& .date {
|
|
228
|
-
color: var(--knime-cornflower-dark);
|
|
229
|
-
|
|
230
|
-
& .draft-icon {
|
|
231
|
-
background: none;
|
|
232
|
-
stroke: var(--knime-cornflower-dark);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
& .controls {
|
|
238
|
-
border-left: 1px solid var(--knime-cornflower-light);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
&::before {
|
|
242
|
-
background-color: var(--knime-cornflower-semi);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
&::after {
|
|
246
|
-
background-color: var(--knime-cornflower);
|
|
247
|
-
box-shadow:
|
|
248
|
-
0 0 0 3px var(--knime-porcelain),
|
|
249
|
-
0 0 0 5px var(--knime-cornflower);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
@media only screen and (width <= 900px) {
|
|
254
|
-
width: calc(100vw - 100px);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
</style>
|