Skip to content

Commit 8aa2ee0

Browse files
committed
Merge branch 'master' into feat-opc-ua
2 parents 9a4f3f7 + 5729731 commit 8aa2ee0

12 files changed

Lines changed: 907 additions & 58 deletions

File tree

BootstrapBlazor.Extensions.sln

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
4-
VisualStudioVersion = 17.11.35303.130
3+
# Visual Studio Version 18
4+
VisualStudioVersion = 18.0.10828.68 main
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{FF1089BE-C704-4374-B629-C57C08E1798F}"
77
EndProject
@@ -53,16 +53,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BarCode", "
5353
EndProject
5454
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BootstrapIcon", "src\components\BootstrapBlazor.BootstrapIcon\BootstrapBlazor.BootstrapIcon.csproj", "{C78E90E8-47EC-4A1E-A108-D0CD018CE417}"
5555
EndProject
56-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BootstrapIcon.Extensions", "src\components\BootstrapBlazor.BootstrapIcon.Extensions\BootstrapBlazor.BootstrapIcon.Extensions.csproj", "{9881C559-4DD1-48A7-B02C-5516FE06B513}"
57-
EndProject
5856
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Chart", "src\components\BootstrapBlazor.Chart\BootstrapBlazor.Chart.csproj", "{A9D480F3-7D56-4E82-A71A-3378DE18A2D2}"
5957
EndProject
6058
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.CherryMarkdown", "src\components\BootstrapBlazor.CherryMarkdown\BootstrapBlazor.CherryMarkdown.csproj", "{A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}"
6159
EndProject
6260
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.CodeEditor", "src\components\BootstrapBlazor.CodeEditor\BootstrapBlazor.CodeEditor.csproj", "{80A0135B-6F5B-4CB0-90F5-0060D241136A}"
6361
EndProject
64-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Core", "src\components\BootstrapBlazor.Core\BootstrapBlazor.Core.csproj", "{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD}"
65-
EndProject
6662
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Dock", "src\components\BootstrapBlazor.Dock\BootstrapBlazor.Dock.csproj", "{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}"
6763
EndProject
6864
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DockView", "src\components\BootstrapBlazor.DockView\BootstrapBlazor.DockView.csproj", "{4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}"
@@ -89,8 +85,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Markdown",
8985
EndProject
9086
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MaterialDesign", "src\components\BootstrapBlazor.MaterialDesign\BootstrapBlazor.MaterialDesign.csproj", "{126D5C86-2C33-4E79-A762-DBEA6E7A779E}"
9187
EndProject
92-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MaterialDesign.Extensions", "src\components\BootstrapBlazor.MaterialDesign.Extensions\BootstrapBlazor.MaterialDesign.Extensions.csproj", "{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7}"
93-
EndProject
9488
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MouseFollower", "src\components\BootstrapBlazor.MouseFollower\BootstrapBlazor.MouseFollower.csproj", "{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}"
9589
EndProject
9690
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Player", "src\components\BootstrapBlazor.Player\BootstrapBlazor.Player.csproj", "{2D0F834A-522F-4CF3-A6BB-BC2585D8505E}"
@@ -308,10 +302,6 @@ Global
308302
{C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Debug|Any CPU.Build.0 = Debug|Any CPU
309303
{C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Release|Any CPU.ActiveCfg = Release|Any CPU
310304
{C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Release|Any CPU.Build.0 = Release|Any CPU
311-
{9881C559-4DD1-48A7-B02C-5516FE06B513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
312-
{9881C559-4DD1-48A7-B02C-5516FE06B513}.Debug|Any CPU.Build.0 = Debug|Any CPU
313-
{9881C559-4DD1-48A7-B02C-5516FE06B513}.Release|Any CPU.ActiveCfg = Release|Any CPU
314-
{9881C559-4DD1-48A7-B02C-5516FE06B513}.Release|Any CPU.Build.0 = Release|Any CPU
315305
{A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
316306
{A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
317307
{A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -324,10 +314,6 @@ Global
324314
{80A0135B-6F5B-4CB0-90F5-0060D241136A}.Debug|Any CPU.Build.0 = Debug|Any CPU
325315
{80A0135B-6F5B-4CB0-90F5-0060D241136A}.Release|Any CPU.ActiveCfg = Release|Any CPU
326316
{80A0135B-6F5B-4CB0-90F5-0060D241136A}.Release|Any CPU.Build.0 = Release|Any CPU
327-
{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
328-
{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
329-
{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
330-
{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD}.Release|Any CPU.Build.0 = Release|Any CPU
331317
{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
332318
{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
333319
{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -380,10 +366,6 @@ Global
380366
{126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Debug|Any CPU.Build.0 = Debug|Any CPU
381367
{126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Release|Any CPU.ActiveCfg = Release|Any CPU
382368
{126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Release|Any CPU.Build.0 = Release|Any CPU
383-
{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
384-
{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
385-
{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
386-
{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7}.Release|Any CPU.Build.0 = Release|Any CPU
387369
{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
388370
{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Debug|Any CPU.Build.0 = Debug|Any CPU
389371
{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -594,11 +576,9 @@ Global
594576
{14EBAC86-E192-4BFA-80C1-91F91BDDE6A4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
595577
{A5F1444E-D86F-4740-BADD-EF042193FD92} = {FF1089BE-C704-4374-B629-C57C08E1798F}
596578
{C78E90E8-47EC-4A1E-A108-D0CD018CE417} = {FF1089BE-C704-4374-B629-C57C08E1798F}
597-
{9881C559-4DD1-48A7-B02C-5516FE06B513} = {FF1089BE-C704-4374-B629-C57C08E1798F}
598579
{A9D480F3-7D56-4E82-A71A-3378DE18A2D2} = {FF1089BE-C704-4374-B629-C57C08E1798F}
599580
{A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3} = {FF1089BE-C704-4374-B629-C57C08E1798F}
600581
{80A0135B-6F5B-4CB0-90F5-0060D241136A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
601-
{A2163EE1-9F38-425A-97B1-C4CDAD6E6DAD} = {FF1089BE-C704-4374-B629-C57C08E1798F}
602582
{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0} = {FF1089BE-C704-4374-B629-C57C08E1798F}
603583
{4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
604584
{F538D58D-58B1-4BA2-A933-E91AC168F568} = {FF1089BE-C704-4374-B629-C57C08E1798F}
@@ -612,7 +592,6 @@ Global
612592
{496554A7-52B0-4E01-9CF3-08F0A2B2FCD8} = {FF1089BE-C704-4374-B629-C57C08E1798F}
613593
{7C875492-41A2-401D-90B1-161BCD5103DC} = {FF1089BE-C704-4374-B629-C57C08E1798F}
614594
{126D5C86-2C33-4E79-A762-DBEA6E7A779E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
615-
{FE0E98BE-0C24-4C41-BFF6-E031630F5AA7} = {FF1089BE-C704-4374-B629-C57C08E1798F}
616595
{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
617596
{2D0F834A-522F-4CF3-A6BB-BC2585D8505E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
618597
{9F61C73D-EFF4-4226-ADEF-4B32F03AA371} = {FF1089BE-C704-4374-B629-C57C08E1798F}

src/components/BootstrapBlazor.PdfViewer/BootstrapBlazor.PdfViewer.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.0.3</Version>
4+
<Version>9.0.6</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/components/BootstrapBlazor.PdfViewer/PdfViewer.razor.cs

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public partial class PdfViewer
1717
[Parameter]
1818
public string? Url { get; set; }
1919

20+
/// <summary>
21+
/// Gets or sets the page index of the PDF file.
22+
/// </summary>
23+
[Parameter]
24+
public int PageIndex { get; set; }
25+
2026
/// <summary>
2127
/// Gets or sets the viewer height. Default is null.
2228
/// </summary>
@@ -52,9 +58,6 @@ public partial class PdfViewer
5258
.AddClass($"--bb-pdf-viewer-height: {Height};", !string.IsNullOrEmpty(Height))
5359
.Build();
5460

55-
private string? _url;
56-
private bool _useGoogleDocs;
57-
5861
private string? UseGoogleDocsString => UseGoogleDocs ? "true" : null;
5962

6063
/// <summary>
@@ -66,29 +69,9 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
6669
{
6770
await base.OnAfterRenderAsync(firstRender);
6871

69-
if (firstRender)
70-
{
71-
_url = Url;
72-
_useGoogleDocs = UseGoogleDocs;
73-
return;
74-
}
75-
76-
var rerender = false;
77-
if (_url != Url)
72+
if (!firstRender)
7873
{
79-
_url = Url;
80-
rerender = true;
81-
}
82-
83-
if (_useGoogleDocs != UseGoogleDocs)
84-
{
85-
_useGoogleDocs = UseGoogleDocs;
86-
rerender = true;
87-
}
88-
89-
if (rerender)
90-
{
91-
await InvokeVoidAsync("loadPdf", Id, GetAbsoluteUri(_url));
74+
await InvokeVoidAsync("loadPdf", Id, GetAbsoluteUri(Url));
9275
}
9376
}
9477

@@ -105,13 +88,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
10588

10689
private string GetAbsoluteUri(string? url)
10790
{
108-
url ??= string.Empty;
109-
if (string.IsNullOrEmpty(url) || !UseGoogleDocs)
91+
if (string.IsNullOrEmpty(url))
11092
{
111-
return url;
93+
return string.Empty;
11294
}
95+
11396
var uri = NavigationManager.ToAbsoluteUri(url);
114-
return uri.AbsoluteUri;
97+
return $"{uri.AbsoluteUri}#page={PageIndex}";
11598
}
11699

117100
/// <summary>

src/components/BootstrapBlazor.Sortable/BootstrapBlazor.Sortable.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.0.2</Version>
4+
<Version>9.0.3</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>
@@ -10,7 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="BootstrapBlazor" Version="$(BBVersion)" />
13+
<PackageReference Include="BootstrapBlazor" Version="9.9.1" />
1414
</ItemGroup>
1515

1616
<ItemGroup>

src/components/BootstrapBlazor.Sortable/Components/SortableList.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace BootstrapBlazor.Components;
1010
/// <summary>
1111
/// SortableList 组件
1212
/// </summary>
13-
public partial class SortableList
13+
public partial class SortableList : ISortableList
1414
{
1515
/// <summary>
1616
/// 获得/设置 配置项实例 <see cref="SortableOption"/>

src/extensions/BootstrapBlazor.Socket/BootstrapBlazor.Socket.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<Version>9.0.0-beta01</Version>
4+
<Version>9.0.0</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/extensions/BootstrapBlazor.TcpSocket/BootstrapBlazor.TcpSocket.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<Version>9.0.0-beta01</Version>
4+
<Version>9.0.0</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Topology FlowChart</PackageTags>
9+
<Description>Bootstrap UI components extensions of FlowChart</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<SupportedPlatform Remove="browser" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="TouchSocket" Version="4.0.0-Alpha.10" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<!--<PackageReference Include="BootstrapBlazor" Version="9.8.0-beta07" />-->
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\..\..\..\BootstrapBlazor\src\BootstrapBlazor\BootstrapBlazor.csproj" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<Using Include="Microsoft.JSInterop" />
30+
</ItemGroup>
31+
32+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using BootstrapBlazor.Components;
6+
7+
namespace Microsoft.Extensions.DependencyInjection;
8+
9+
/// <summary>
10+
/// BootstrapBlazor 服务扩展类
11+
/// </summary>
12+
public static class BootstrapBlazorTouchSocketServiceExtensions
13+
{
14+
/// <summary>
15+
/// 添加 TouchSocket 服务
16+
/// </summary>
17+
/// <param name="services"></param>
18+
public static IServiceCollection AddBootstrapBlazorTouchSocketProviderService(this IServiceCollection services)
19+
{
20+
services.AddTransient<ISocketClientProvider, DefaultTcpSocketProvider>();
21+
22+
return services;
23+
}
24+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using System.Buffers;
6+
using System.Diagnostics;
7+
using System.IO.Pipelines;
8+
using System.Net;
9+
using TouchSocket.Core;
10+
using TouchSocket.Sockets;
11+
12+
namespace BootstrapBlazor.Components;
13+
14+
internal sealed class DefaultTcpSocketProvider : TcpClientBase, ISocketClientProvider
15+
{
16+
/// <summary>
17+
/// <inheritdoc/>
18+
/// </summary>
19+
public bool IsConnected => base.Online;
20+
21+
/// <summary>
22+
/// <inheritdoc/>
23+
/// </summary>
24+
public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
25+
26+
/// <summary>
27+
/// <inheritdoc/>
28+
/// </summary>
29+
public async ValueTask CloseAsync()
30+
{
31+
await base.CloseAsync(string.Empty);
32+
}
33+
34+
/// <summary>
35+
/// <inheritdoc/>
36+
/// </summary>
37+
public async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
38+
{
39+
await SetupAsync(new TouchSocketConfig()
40+
.SetBindIPHost(new IPHost(LocalEndPoint.Address, LocalEndPoint.Port))
41+
.SetRemoteIPHost(new IPHost(endPoint.Address, endPoint.Port)));
42+
43+
try
44+
{
45+
await TcpConnectAsync(int.MaxValue, token);
46+
Debug.Assert(MainSocket != null, "MainSocket cannot be null after connection.");
47+
Debug.Assert(base.Online, "Online should be true after successful connection.");
48+
if (MainSocket.LocalEndPoint is IPEndPoint localEndPoint)
49+
{
50+
LocalEndPoint = localEndPoint;
51+
}
52+
return true;
53+
}
54+
catch (Exception ex)
55+
{
56+
this.Logger?.Exception(this, ex);
57+
return false;
58+
}
59+
}
60+
61+
/// <summary>
62+
/// <inheritdoc/>
63+
/// </summary>
64+
public async ValueTask<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
65+
{
66+
token.ThrowIfCancellationRequested();
67+
this.ThrowIfTcpClientNotConnected();
68+
this.ThrowIfDisposed();
69+
70+
var result = await base.Transport.Input.ReadAsync(token);
71+
if (result.IsCompleted)
72+
{
73+
return 0;
74+
}
75+
var length = (int)Math.Min(result.Buffer.Length, buffer.Length);
76+
77+
var sequence = result.Buffer.Slice(0, length);
78+
79+
sequence.CopyTo(buffer.Span);
80+
base.Transport.Input.AdvanceTo(sequence.End);
81+
return length;
82+
}
83+
84+
/// <summary>
85+
/// <inheritdoc/>
86+
/// </summary>
87+
public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
88+
{
89+
token.ThrowIfCancellationRequested();
90+
base.ThrowIfTcpClientNotConnected();
91+
base.ThrowIfDisposed();
92+
var pipeWriter = base.Transport.Output;
93+
var locker = base.Transport.SemaphoreSlimForWriter;
94+
await locker.WaitAsync(token);
95+
try
96+
{
97+
pipeWriter.Write(data.Span);
98+
var result = await pipeWriter.FlushAsync(token);
99+
if (result.IsCanceled || result.IsCompleted)
100+
{
101+
return false;
102+
}
103+
return true;
104+
}
105+
catch (Exception ex)
106+
{
107+
this.Logger?.Exception(this, ex);
108+
return false;
109+
}
110+
finally
111+
{
112+
locker.Release();
113+
}
114+
}
115+
116+
protected override sealed async Task ReceiveLoopAsync(ITransport transport)
117+
{
118+
//重写接收循环方法
119+
//此处不做任何数据读取
120+
//让数据直接到ReceiveAsync使用管道直接读取数据
121+
await Task.Delay(-1, transport.ClosedToken);
122+
}
123+
}

0 commit comments

Comments
 (0)