@trebco/treb 31.7.8 → 31.9.1
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/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +8 -8
- package/dist/treb.d.ts +1 -1
- package/package.json +2 -2
- package/treb-data-model/src/annotation.ts +1 -1
- package/treb-embed/markup/toolbar.html +5 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +8 -1
- package/treb-embed/src/embedded-spreadsheet.ts +89 -28
- package/treb-embed/style/autocomplete.scss +4 -0
- package/treb-embed/style/dropdown-select.scss +3 -0
- package/treb-embed/style/font-stacks.scss +2 -0
- package/treb-embed/style/grid.scss +11 -11
- package/treb-embed/style/layout.scss +12 -9
- package/treb-embed/style/mouse-mask.scss +3 -0
- package/treb-embed/style/note.scss +4 -0
- package/treb-embed/style/overlay-editor.scss +3 -0
- package/treb-embed/style/tab-bar.scss +4 -0
- package/treb-embed/style/table.scss +3 -0
- package/treb-embed/style/theme-defaults.scss +5 -3
- package/treb-embed/style/toolbar.scss +17 -5
- package/treb-embed/style/tooltip.scss +4 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +1 -1
- package/treb-export/src/import.ts +9 -3
- package/treb-export/src/workbook-style2.ts +0 -39
- package/treb-export/src/workbook2.ts +103 -79
- package/treb-export/tsconfig.json +1 -0
- package/treb-grid/src/layout/base_layout.ts +21 -0
- package/treb-grid/src/render/svg_selection_block.ts +4 -40
- package/treb-grid/src/types/grid.ts +37 -2
package/dist/treb.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trebco/treb",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.9.1",
|
|
4
4
|
"license": "LGPL-3.0-or-later",
|
|
5
5
|
"homepage": "https://treb.app",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"@types/uzip": "^0.20201231.0",
|
|
17
17
|
"base64-js": "^1.5.1",
|
|
18
18
|
"cssnano": "^7.0.4",
|
|
19
|
-
"esbuild": "^0.
|
|
19
|
+
"esbuild": "^0.24.0",
|
|
20
20
|
"eslint": "^9.9.1",
|
|
21
21
|
"fast-xml-parser": "^4.0.7",
|
|
22
22
|
"html-minifier-terser": "^7.2.0",
|
|
@@ -98,6 +98,11 @@
|
|
|
98
98
|
<div class="treb-color-chooser">
|
|
99
99
|
<div class="treb-caption">Theme colors</div>
|
|
100
100
|
<div class="treb-swatches"></div>
|
|
101
|
+
<div class="treb-caption">No color</div>
|
|
102
|
+
<div class="treb-default-swatch">
|
|
103
|
+
<button class="treb-default-color" data-command="set-color" data-color="{}"></button>
|
|
104
|
+
<span class="treb-default-color-label" data-command="set-color" data-color="{}">...</span>
|
|
105
|
+
</div>
|
|
101
106
|
<div class="treb-caption">Other colors</div>
|
|
102
107
|
<div class="treb-swatches"></div>
|
|
103
108
|
<div>
|
|
@@ -778,12 +778,15 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
778
778
|
this.swatch_lists.theme?.replaceChildren(fragment);
|
|
779
779
|
|
|
780
780
|
fragment = this.DOM.Fragment();
|
|
781
|
+
|
|
782
|
+
/*
|
|
781
783
|
this.DOM.Create('button', 'treb-default-color', fragment, {
|
|
782
784
|
attrs: { title: 'Default color' },
|
|
783
785
|
data: { command: 'set-color', color: JSON.stringify({}) },
|
|
784
786
|
});
|
|
787
|
+
*/
|
|
785
788
|
|
|
786
|
-
const colors = ['Black', 'White', 'Gray', 'Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Violet'];
|
|
789
|
+
const colors = ['Black', 'White', 'Gray', 'Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet'];
|
|
787
790
|
|
|
788
791
|
const lc = colors.map(color => color.toLowerCase());
|
|
789
792
|
const additional_colors = sheet.document_styles.colors.filter(test => {
|
|
@@ -1320,6 +1323,10 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
1320
1323
|
|
|
1321
1324
|
if (parent.dataset.colorCommand) {
|
|
1322
1325
|
color_chooser.querySelector('.treb-default-color')?.setAttribute('title', parent.dataset.defaultColorText || 'Default color');
|
|
1326
|
+
const label = color_chooser.querySelector('.treb-default-color-label');
|
|
1327
|
+
if (label) {
|
|
1328
|
+
label.textContent = parent.dataset.defaultColorText || 'Default color';
|
|
1329
|
+
}
|
|
1323
1330
|
|
|
1324
1331
|
parent.appendChild(color_chooser);
|
|
1325
1332
|
color_chooser.dataset.colorCommand = parent.dataset.colorCommand;
|
|
@@ -76,7 +76,8 @@ import type {
|
|
|
76
76
|
|
|
77
77
|
import {
|
|
78
78
|
IsArea, ThemeColorTable, ComplexToString, Rectangle, IsComplex, type CellStyle,
|
|
79
|
-
Localization, Style, type Color, ResolveThemeColor, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, DOMContext
|
|
79
|
+
Localization, Style, type Color, ResolveThemeColor, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, DOMContext,
|
|
80
|
+
ValueType,
|
|
80
81
|
} from 'treb-base-types';
|
|
81
82
|
|
|
82
83
|
import { EventSource, ValidateURI } from 'treb-utils';
|
|
@@ -5209,13 +5210,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5209
5210
|
// now we have to inflate them on sheet change
|
|
5210
5211
|
|
|
5211
5212
|
for (const annotation of event.activate.annotations) {
|
|
5212
|
-
// if (annotation.update_callback) {
|
|
5213
|
-
// annotation.update_callback();
|
|
5214
|
-
// }
|
|
5215
|
-
|
|
5216
5213
|
this.InflateAnnotation(annotation);
|
|
5217
5214
|
this.calculator.UpdateAnnotations(annotation, event.activate);
|
|
5218
|
-
|
|
5219
5215
|
}
|
|
5220
5216
|
|
|
5221
5217
|
// we also need to update annotations that are already inflated
|
|
@@ -5529,21 +5525,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5529
5525
|
if (view) {
|
|
5530
5526
|
view.dirty = true;
|
|
5531
5527
|
}
|
|
5532
|
-
// else {
|
|
5533
|
-
// console.info("empty view")
|
|
5534
|
-
// }
|
|
5535
5528
|
|
|
5536
5529
|
}
|
|
5537
5530
|
}
|
|
5538
|
-
|
|
5539
|
-
/*
|
|
5540
|
-
const view: AnnotationViewData = annotation.view[this.grid.view_index] || {};
|
|
5541
|
-
if (view.update_callback) {
|
|
5542
|
-
view.update_callback();
|
|
5543
|
-
}
|
|
5544
|
-
|
|
5545
|
-
this.grid.AnnotationUpdated(annotation);
|
|
5546
|
-
*/
|
|
5547
5531
|
}
|
|
5548
5532
|
|
|
5549
5533
|
const view: AnnotationViewData = annotation.view[this.grid.view_index] || {};
|
|
@@ -5670,19 +5654,11 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5670
5654
|
|
|
5671
5655
|
if (annotation.data.type === 'treb-chart') {
|
|
5672
5656
|
|
|
5673
|
-
// if (!(self as any).TREB || !(self as any).TREB.CreateChart2) {
|
|
5674
|
-
// console.warn('missing chart library');
|
|
5675
|
-
// }
|
|
5676
|
-
// else
|
|
5677
5657
|
{
|
|
5678
5658
|
|
|
5679
5659
|
const chart = this.CreateChart();
|
|
5680
5660
|
chart.Initialize(view.content_node);
|
|
5681
5661
|
|
|
5682
|
-
//if (this.calculator.RegisterLibrary('treb-charts', ChartFunctions)) {
|
|
5683
|
-
// this.UpdateAC();
|
|
5684
|
-
//}
|
|
5685
|
-
|
|
5686
5662
|
const update_chart = () => {
|
|
5687
5663
|
|
|
5688
5664
|
if (annotation.data.formula) {
|
|
@@ -5766,11 +5742,21 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5766
5742
|
else if (annotation.data.type === 'textbox') {
|
|
5767
5743
|
|
|
5768
5744
|
if (annotation.data.data) {
|
|
5745
|
+
|
|
5746
|
+
let target: HTMLElement|undefined;
|
|
5747
|
+
|
|
5769
5748
|
const container = document.createElement('div');
|
|
5770
5749
|
container.classList.add('treb-annotation-textbox');
|
|
5771
5750
|
|
|
5772
5751
|
for (const paragraph of annotation.data.data.paragraphs) {
|
|
5773
5752
|
const p = document.createElement('p');
|
|
5753
|
+
|
|
5754
|
+
// FIXME: styling should move (to layout?) so we can
|
|
5755
|
+
// update it. possibly content as well? although we
|
|
5756
|
+
// can leave the formula in here, I guess.
|
|
5757
|
+
//
|
|
5758
|
+
// how about we move styling to the update routine?
|
|
5759
|
+
|
|
5774
5760
|
if (paragraph.style) {
|
|
5775
5761
|
if (paragraph.style.horizontal_align === 'right' ||
|
|
5776
5762
|
paragraph.style.horizontal_align === 'center') {
|
|
@@ -5779,17 +5765,36 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5779
5765
|
}
|
|
5780
5766
|
|
|
5781
5767
|
for (const entry of paragraph.content) {
|
|
5768
|
+
|
|
5782
5769
|
const span = document.createElement('span');
|
|
5783
|
-
|
|
5770
|
+
|
|
5771
|
+
if (!target) { target = span; }
|
|
5772
|
+
|
|
5773
|
+
if (!annotation.data.formula) {
|
|
5774
|
+
span.textContent = entry.text || '';
|
|
5775
|
+
}
|
|
5776
|
+
|
|
5784
5777
|
if (entry.style?.bold) {
|
|
5785
5778
|
span.style.fontWeight = '600';
|
|
5786
5779
|
}
|
|
5780
|
+
|
|
5787
5781
|
if (entry.style?.italic) {
|
|
5788
5782
|
span.style.fontStyle = 'italic';
|
|
5789
5783
|
}
|
|
5784
|
+
|
|
5790
5785
|
p.appendChild(span);
|
|
5786
|
+
|
|
5787
|
+
if (annotation.data.formula) {
|
|
5788
|
+
break; // only one text node allowed for formula (?)
|
|
5789
|
+
}
|
|
5790
|
+
|
|
5791
5791
|
}
|
|
5792
5792
|
container.append(p);
|
|
5793
|
+
|
|
5794
|
+
if (annotation.data.formula) {
|
|
5795
|
+
break; // only one text node allowed for formula (?)
|
|
5796
|
+
}
|
|
5797
|
+
|
|
5793
5798
|
}
|
|
5794
5799
|
|
|
5795
5800
|
view.content_node.append(container);
|
|
@@ -5807,12 +5812,68 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5807
5812
|
};
|
|
5808
5813
|
|
|
5809
5814
|
view.update_callback = () => {
|
|
5815
|
+
|
|
5810
5816
|
if (!this.grid.headless) {
|
|
5811
5817
|
update_textbox();
|
|
5812
5818
|
}
|
|
5819
|
+
|
|
5820
|
+
if (target && annotation.data.formula) {
|
|
5821
|
+
|
|
5822
|
+
const parse_result = this.parser.Parse(annotation.data.formula);
|
|
5823
|
+
if (parse_result && parse_result.expression) {
|
|
5824
|
+
|
|
5825
|
+
// FIXME: make a method for doing this
|
|
5826
|
+
|
|
5827
|
+
this.parser.Walk(parse_result.expression, (unit) => {
|
|
5828
|
+
if (unit.type === 'address' || unit.type === 'range') {
|
|
5829
|
+
this.model.ResolveSheetID(unit, undefined, this.grid.active_sheet);
|
|
5830
|
+
}
|
|
5831
|
+
return true;
|
|
5832
|
+
});
|
|
5833
|
+
|
|
5834
|
+
const result = this.calculator.CalculateExpression(parse_result.expression);
|
|
5835
|
+
|
|
5836
|
+
let format = NumberFormatCache.Get('General');
|
|
5837
|
+
let source: ICellAddress|undefined;
|
|
5838
|
+
switch (parse_result.expression.type) {
|
|
5839
|
+
case 'address':
|
|
5840
|
+
source = parse_result.expression;
|
|
5841
|
+
break;
|
|
5842
|
+
case 'range':
|
|
5843
|
+
source = parse_result.expression.start;
|
|
5844
|
+
break;
|
|
5845
|
+
}
|
|
5846
|
+
|
|
5847
|
+
if (source) {
|
|
5848
|
+
const sheet = source.sheet_id ? this.model.sheets.Find(source.sheet_id) : this.grid.active_sheet;
|
|
5849
|
+
const style = sheet?.CellStyleData(source);
|
|
5850
|
+
if (style?.number_format) {
|
|
5851
|
+
format = NumberFormatCache.Get(style.number_format);
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
|
|
5855
|
+
switch (result.type) {
|
|
5856
|
+
case ValueType.number:
|
|
5857
|
+
case ValueType.string:
|
|
5858
|
+
case ValueType.complex:
|
|
5859
|
+
target.textContent = format.Format(result.value);
|
|
5860
|
+
break;
|
|
5861
|
+
|
|
5862
|
+
default:
|
|
5863
|
+
target.textContent = result.value?.toString() || '';
|
|
5864
|
+
}
|
|
5865
|
+
|
|
5866
|
+
|
|
5867
|
+
// TODO: use reference number format
|
|
5868
|
+
|
|
5869
|
+
}
|
|
5870
|
+
|
|
5871
|
+
}
|
|
5872
|
+
|
|
5813
5873
|
};
|
|
5814
5874
|
|
|
5815
|
-
update_textbox();
|
|
5875
|
+
// update_textbox();
|
|
5876
|
+
view.update_callback();
|
|
5816
5877
|
|
|
5817
5878
|
}
|
|
5818
5879
|
|
|
@@ -21,20 +21,20 @@
|
|
|
21
21
|
|
|
22
22
|
// sass vars only
|
|
23
23
|
|
|
24
|
-
@
|
|
25
|
-
@
|
|
24
|
+
@use 'z-index.scss' as *;
|
|
25
|
+
@use 'defaults.scss' as *;
|
|
26
26
|
|
|
27
27
|
// clean, no imports (but use sass vars)
|
|
28
28
|
|
|
29
|
-
@
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
34
|
-
@
|
|
35
|
-
@
|
|
36
|
-
@
|
|
37
|
-
@
|
|
29
|
+
@use 'mouse-mask.scss';
|
|
30
|
+
@use 'note.scss';
|
|
31
|
+
@use 'table.scss';
|
|
32
|
+
@use 'tooltip.scss';
|
|
33
|
+
@use 'dropdown-select.scss';
|
|
34
|
+
@use 'autocomplete.scss';
|
|
35
|
+
@use 'formula-bar.scss';
|
|
36
|
+
@use 'tab-bar.scss';
|
|
37
|
+
@use 'overlay-editor.scss';
|
|
38
38
|
|
|
39
39
|
.treb-main.treb-main {
|
|
40
40
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
@
|
|
3
|
-
@
|
|
4
|
-
|
|
5
|
-
@
|
|
6
|
-
@
|
|
7
|
-
@
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
@
|
|
2
|
+
@use './defaults.scss' as *;
|
|
3
|
+
@use './z-index.scss' as *;
|
|
4
|
+
|
|
5
|
+
@use './grid.scss';
|
|
6
|
+
@use './theme-defaults.scss';
|
|
7
|
+
@use './dark-theme.scss';
|
|
8
|
+
@use '../../treb-charts/style/charts.scss';
|
|
9
|
+
@use './dialog.scss';
|
|
10
|
+
@use './spinner.scss';
|
|
11
|
+
@use './treb-icons.scss';
|
|
12
|
+
@use './toolbar.scss';
|
|
13
|
+
@use './font-stacks.scss';
|
|
11
14
|
|
|
12
15
|
/*
|
|
13
16
|
* switching to a double-selector to increase specificity. the particular
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
@use './defaults.scss' as *;
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
25
|
* we're using variables for selection colors because the colors are used
|
|
24
26
|
* in more than one place; that's a drawback of using CSS, but I think overall
|
|
@@ -356,12 +358,12 @@ $text-reference-color-5: rgb(254, 47, 1);
|
|
|
356
358
|
.selection {
|
|
357
359
|
stroke-width: var(--treb-selection-stroke-width, 2px);
|
|
358
360
|
|
|
359
|
-
.outline {
|
|
361
|
+
.treb-selection-outline {
|
|
360
362
|
stroke: currentColor;
|
|
361
363
|
fill: none;
|
|
362
364
|
}
|
|
363
365
|
|
|
364
|
-
.fill {
|
|
366
|
+
.treb-selection-fill {
|
|
365
367
|
stroke: none;
|
|
366
368
|
fill: currentColor;
|
|
367
369
|
opacity: var(--treb-selection-fill-opacity, .1);
|
|
@@ -405,7 +407,7 @@ $text-reference-color-5: rgb(254, 47, 1);
|
|
|
405
407
|
.primary-selection {
|
|
406
408
|
color: var(--treb-selection-color-unfocused, var(--treb-selection-color, $primary-selection-color));
|
|
407
409
|
|
|
408
|
-
.nub {
|
|
410
|
+
.treb-selection-nub {
|
|
409
411
|
stroke: #fff; // <-- ? maybe grid background color?
|
|
410
412
|
fill: currentColor;
|
|
411
413
|
stroke-width: 1px;
|
|
@@ -367,11 +367,7 @@ $swatch-size: 18px;
|
|
|
367
367
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
-
.treb-swatches {
|
|
371
|
-
|
|
372
|
-
display: grid;
|
|
373
|
-
grid-template-columns: repeat(10, 1fr);
|
|
374
|
-
gap: .5rem;
|
|
370
|
+
.treb-swatches, .treb-default-swatch {
|
|
375
371
|
|
|
376
372
|
button {
|
|
377
373
|
width: $swatch-size;
|
|
@@ -399,6 +395,22 @@ $swatch-size: 18px;
|
|
|
399
395
|
mask-size: 24px 24px;
|
|
400
396
|
-webkit-mask-size: 24px 24px;
|
|
401
397
|
}
|
|
398
|
+
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.treb-default-swatch {
|
|
402
|
+
display: grid;
|
|
403
|
+
grid-template-columns: auto 1fr;
|
|
404
|
+
gap: .5rem;
|
|
405
|
+
align-items: center;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.treb-swatches {
|
|
409
|
+
|
|
410
|
+
display: grid;
|
|
411
|
+
grid-template-columns: repeat(10, 1fr);
|
|
412
|
+
gap: .5rem;
|
|
413
|
+
|
|
402
414
|
}
|
|
403
415
|
}
|
|
404
416
|
|
|
@@ -1104,14 +1104,20 @@ export class Importer {
|
|
|
1104
1104
|
|
|
1105
1105
|
// console.info({descriptor});
|
|
1106
1106
|
|
|
1107
|
-
|
|
1107
|
+
const anchored_annotation: AnchoredAnnotation = {
|
|
1108
1108
|
layout,
|
|
1109
1109
|
type: 'textbox',
|
|
1110
1110
|
data: {
|
|
1111
1111
|
style: descriptor.style,
|
|
1112
1112
|
paragraphs: descriptor.paragraphs,
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1113
|
+
},
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
if (descriptor.reference) {
|
|
1117
|
+
anchored_annotation.formula = `=` + descriptor.reference;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
annotations.push(anchored_annotation);
|
|
1115
1121
|
|
|
1116
1122
|
}
|
|
1117
1123
|
|
|
@@ -983,45 +983,6 @@ export class StyleCache {
|
|
|
983
983
|
const new_fill: Fill = {...fill};
|
|
984
984
|
this.fills.push(new_fill);
|
|
985
985
|
|
|
986
|
-
/*
|
|
987
|
-
// add the node structure
|
|
988
|
-
|
|
989
|
-
if (!this.dom) throw new Error('missing dom');
|
|
990
|
-
const fills = this.dom.find('./fills');
|
|
991
|
-
|
|
992
|
-
if (!fills) throw new Error('fills not found');
|
|
993
|
-
fills.attrib.count = (Number(fills.attrib.count || 0) + 1).toString();
|
|
994
|
-
|
|
995
|
-
const new_element = Element('fill');
|
|
996
|
-
const pattern_fill = Element('patternFill', { patternType: fill.pattern_type });
|
|
997
|
-
|
|
998
|
-
switch (fill.pattern_type) {
|
|
999
|
-
case 'none':
|
|
1000
|
-
break;
|
|
1001
|
-
case 'solid':
|
|
1002
|
-
if (fill.fg_color) {
|
|
1003
|
-
const attrs: Record<string, string> = {};
|
|
1004
|
-
|
|
1005
|
-
if (fill.fg_color.argb) { attrs.rgb = fill.fg_color.argb; }
|
|
1006
|
-
if (fill.fg_color.indexed) { attrs.indexed = fill.fg_color.indexed.toString(); }
|
|
1007
|
-
if (fill.fg_color.tint) { attrs.tint = fill.fg_color.tint.toString(); }
|
|
1008
|
-
if (typeof fill.fg_color.theme !== 'undefined') { attrs.theme = fill.fg_color.theme.toString(); }
|
|
1009
|
-
|
|
1010
|
-
pattern_fill.append(Element('fgColor', attrs));
|
|
1011
|
-
}
|
|
1012
|
-
break;
|
|
1013
|
-
case 'gray':
|
|
1014
|
-
|
|
1015
|
-
// ...
|
|
1016
|
-
|
|
1017
|
-
break;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
new_element.append(pattern_fill);
|
|
1021
|
-
|
|
1022
|
-
fills.append(new_element);
|
|
1023
|
-
*/
|
|
1024
|
-
|
|
1025
986
|
return this.fills.length - 1;
|
|
1026
987
|
}
|
|
1027
988
|
|