game log reader reads async now.
should have done that a long time ago update profile page to have a bit better space usage
This commit is contained in:
parent
82bae772f0
commit
c8ec0eefa9
@ -6,7 +6,7 @@
|
|||||||
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||||
<Version>2.2.5.6</Version>
|
<Version>2.2.5.7</Version>
|
||||||
<Authors>RaidMax</Authors>
|
<Authors>RaidMax</Authors>
|
||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Product>IW4MAdmin</Product>
|
<Product>IW4MAdmin</Product>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<TieredCompilation>true</TieredCompilation>
|
<TieredCompilation>true</TieredCompilation>
|
||||||
<AssemblyVersion>2.2.5.6</AssemblyVersion>
|
<AssemblyVersion>2.2.5.7</AssemblyVersion>
|
||||||
<FileVersion>2.2.5.6</FileVersion>
|
<FileVersion>2.2.5.6</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ namespace IW4MAdmin.Application.IO
|
|||||||
// todo: max async
|
// todo: max async
|
||||||
// take the old start position and go back the number of new characters
|
// take the old start position and go back the number of new characters
|
||||||
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
||||||
// the difference should be in the range of a int :P
|
|
||||||
string newLine;
|
string newLine;
|
||||||
while (!String.IsNullOrEmpty(newLine = rd.ReadLine()))
|
while (!string.IsNullOrEmpty(newLine = await rd.ReadLineAsync()))
|
||||||
{
|
{
|
||||||
logLines.Add(newLine);
|
logLines.Add(newLine);
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,6 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
|
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
|
||||||
|
|
||||||
ServerManager = manager;
|
ServerManager = manager;
|
||||||
|
|
||||||
Manager = new StatManager(manager);
|
Manager = new StatManager(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content border-bottom">
|
<div class="tab-content border-bottom row">
|
||||||
<div role="tabpanel" class="tab-pane active striped" id="server_0">
|
<div role="tabpanel" class="tab-pane active striped" id="server_0">
|
||||||
@await Component.InvokeAsync("TopPlayers", new { count = 10, offset = 0 })
|
@await Component.InvokeAsync("TopPlayers", new { count = 10, offset = 0 })
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
public class ServerConfiguration : IBaseConfiguration
|
public class ServerConfiguration : IBaseConfiguration
|
||||||
{
|
{
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
public ushort Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public IList<string> Rules { get; set; }
|
public IList<string> Rules { get; set; }
|
||||||
public IList<string> AutoMessages { get; set; }
|
public IList<string> AutoMessages { get; set; }
|
||||||
@ -27,8 +27,15 @@ namespace SharedLibraryCore.Configuration
|
|||||||
eventParsers = new List<IEventParser>();
|
eventParsers = new List<IEventParser>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRConParser(IRConParser parser) => rconParsers.Add(parser);
|
public void AddRConParser(IRConParser parser)
|
||||||
public void AddEventParser(IEventParser parser) => eventParsers.Add(parser);
|
{
|
||||||
|
rconParsers.Add(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEventParser(IEventParser parser)
|
||||||
|
{
|
||||||
|
eventParsers.Add(parser);
|
||||||
|
}
|
||||||
|
|
||||||
public void ModifyParsers()
|
public void ModifyParsers()
|
||||||
{
|
{
|
||||||
@ -65,26 +72,26 @@ namespace SharedLibraryCore.Configuration
|
|||||||
string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
||||||
|
|
||||||
if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
|
if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
|
||||||
|
{
|
||||||
IPAddress = input;
|
IPAddress = input;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(Port < 1)
|
Port = Utilities.PromptInt(Utilities.PromptString(loc["SETUP_SERVER_PORT"]), null, 1, ushort.MaxValue);
|
||||||
{
|
|
||||||
string input = Utilities.PromptString(loc["SETUP_SERVER_PORT"]);
|
|
||||||
if (UInt16.TryParse(input, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture, out ushort port))
|
|
||||||
Port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]);
|
Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]);
|
||||||
AutoMessages = new List<string>();
|
AutoMessages = new List<string>();
|
||||||
Rules = new List<string>();
|
Rules = new List<string>();
|
||||||
ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32);
|
ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32);
|
||||||
|
ManualLogPath = null;
|
||||||
|
|
||||||
ModifyParsers();
|
ModifyParsers();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name() => "ServerConfiguration";
|
public string Name()
|
||||||
|
{
|
||||||
|
return "ServerConfiguration";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace SharedLibraryCore.Services
|
|||||||
//entity.CurrentServer.Logger.WriteDebug($"Updating alias link for {entity}");
|
//entity.CurrentServer.Logger.WriteDebug($"Updating alias link for {entity}");
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var alias in aliases.Union(new List<EFAlias>() { entity.CurrentAlias })
|
foreach (var alias in aliases.Append(entity.CurrentAlias)
|
||||||
.Where(_alias => !_alias.Active ||
|
.Where(_alias => !_alias.Active ||
|
||||||
_alias.LinkId != aliasLink.AliasLinkId))
|
_alias.LinkId != aliasLink.AliasLinkId))
|
||||||
{
|
{
|
||||||
|
@ -58,7 +58,7 @@ namespace SharedLibraryCore.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
||||||
{
|
{
|
||||||
using (var ctx = new DatabaseContext(disableTracking:true))
|
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||||
{
|
{
|
||||||
return await ctx.EFMeta
|
return await ctx.EFMeta
|
||||||
.Where(_meta => _meta.Key == metaKey)
|
.Where(_meta => _meta.Key == metaKey)
|
||||||
|
@ -6,100 +6,85 @@
|
|||||||
string gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value;
|
string gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value;
|
||||||
bool isTempBanned = Model.ActivePenaltyType == "TempBan";
|
bool isTempBanned = Model.ActivePenaltyType == "TempBan";
|
||||||
}
|
}
|
||||||
<div id="profile_wrapper" class="row d-flex d-sm-inline-flex justify-content-center justify-content-left pb-3">
|
<div id="profile_wrapper" class="d-flex row pb-3">
|
||||||
<div class="mr-auto ml-auto ml-sm-0 mr-sm-0">
|
<div id="profile_avatar" class="ml-auto mr-auto mb-4 mb-md-0 text-center level-bgcolor-@Model.LevelInt @(isTempBanned ? "penalties-bgcolor-tempban" : "")" style="background-image:url('@string.Format("https://gravatar.com/avatar/{0}?size=168&default=blank&rating=pg", gravatarUrl)')">
|
||||||
<div id="profile_avatar" class="mb-4 mb-md-0 text-center level-bgcolor-@Model.LevelInt @(isTempBanned ? "penalties-bgcolor-tempban" : "")" style="background-image:url('@string.Format("https://gravatar.com/avatar/{0}?size=168&default=blank&rating=pg", gravatarUrl)')">
|
@if (string.IsNullOrEmpty(gravatarUrl))
|
||||||
@if (string.IsNullOrEmpty(gravatarUrl))
|
{
|
||||||
{
|
<span class="profile-shortcode">@shortCode</span>
|
||||||
<span class="profile-shortcode">@shortCode</span>
|
}
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
<div class="ml-auto mr-auto">
|
||||||
<div id="profile_name">
|
<div id="profile_name" class="client-name h1 pl-3 pr-3">@Model.Name</div>
|
||||||
@if (Model.Online)
|
<div id="profile_info" class="text-center text-md-left pr-md-3 pl-md-3">
|
||||||
{
|
<div id="profile_level" class="text-muted mb-2">
|
||||||
<span class="oi oi-media-record text-success pr-2 h4" title="Online for @Model.TimeOnline"></span>
|
<h5><span class="level-color-@Model.LevelInt @(isTempBanned ? "penalties-color-tempban" : "")"><strong>@Model.Level @(isTempBanned ? $"({loc["WEBFRONT_PROFILE_TEMPBAN"]})" : "")</strong></span></h5>
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<span class="oi oi-media-record text-danger pr-2 h4" title="Offline"></span>
|
|
||||||
}
|
|
||||||
<div class="client-name h1 d-flex d-inline-flex">
|
|
||||||
@Model.Name
|
|
||||||
</div>
|
</div>
|
||||||
@{
|
<div id="profile_time_played" class="text-muted">
|
||||||
if (ViewBag.Authorized)
|
@loc["WEBFRONT_PROFILE_PLAYER"] <span class="text-primary">@Model.TimePlayed</span> @loc["GLOBAL_TIME_HOURS"]
|
||||||
|
</div>
|
||||||
|
<div id="profile_first_seen" class="text-muted">
|
||||||
|
@loc["WEBFRONT_PROFILE_FSEEN"] <span class="text-primary">@Model.FirstSeen</span> @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]
|
||||||
|
</div>
|
||||||
|
<div id="profile_last_seen" class="text-muted">
|
||||||
|
@loc["WEBFRONT_PROFILE_LSEEN"] <span class="text-primary">@Model.LastSeen</span> @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]
|
||||||
|
</div>
|
||||||
|
<div id="profile_meta_0" class="text-center text-sm-left"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-fill mb-1 pl-md-3 pr-md-3 align-self-end text-center text-sm-left" id="profile_meta_1">
|
||||||
|
</div>
|
||||||
|
@if (ViewBag.Authorized)
|
||||||
|
{
|
||||||
|
<div class="mr-auto ml-auto">
|
||||||
|
<div class="text-center text-sm-right mt-2">
|
||||||
|
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
|
||||||
|
@if (Model.LevelInt != -1)
|
||||||
{
|
{
|
||||||
<div class="d-flex d-md-inline-flex justify-content-center order-1">
|
<div id="profile_action_edit_btn" class="profile-action oi oi-cog text-muted h3 ml-2" title="Client Options" data-action="edit" aria-hidden="true"></div>
|
||||||
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
|
|
||||||
@if (Model.LevelInt != -1)
|
|
||||||
{
|
|
||||||
<div id="profile_action_edit_btn" class="profile-action oi oi-cog text-muted h3 ml-2" title="Client Options" data-action="edit" aria-hidden="true"></div>
|
|
||||||
}
|
|
||||||
@if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty)
|
|
||||||
{
|
|
||||||
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty)
|
|
||||||
{
|
|
||||||
@if (isTempBanned)
|
|
||||||
{
|
|
||||||
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
|
||||||
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked penalties-color-tempban h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked text-danger h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">
|
|
||||||
@{
|
|
||||||
foreach (var linked in Model.LinkedAccounts)
|
|
||||||
{
|
|
||||||
@Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new { id = linked.Key }, new { @class = "link-inverse" })<br />
|
|
||||||
}
|
|
||||||
foreach (string alias in Model.Aliases)
|
|
||||||
{
|
|
||||||
@alias<br />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ViewBag.Authorized)
|
|
||||||
{
|
|
||||||
foreach (string ip in Model.IPs)
|
|
||||||
{
|
|
||||||
<a class="ip-locate-link" href="#" data-ip="@ip">@ip</a><br />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
@if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty)
|
||||||
|
{
|
||||||
|
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty)
|
||||||
|
{
|
||||||
|
@if (isTempBanned)
|
||||||
|
{
|
||||||
|
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
||||||
|
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked penalties-color-tempban h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked text-danger h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div id="profile_aliases" class="text-muted">
|
||||||
|
@{
|
||||||
|
foreach (var linked in Model.LinkedAccounts)
|
||||||
|
{
|
||||||
|
@Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new { id = linked.Key }, new { @class = "link-inverse" })<br />
|
||||||
|
}
|
||||||
|
foreach (string alias in Model.Aliases)
|
||||||
|
{
|
||||||
|
@alias<br />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewBag.Authorized)
|
||||||
|
{
|
||||||
|
foreach (string ip in Model.IPs)
|
||||||
|
{
|
||||||
|
<a class="ip-locate-link" href="#" data-ip="@ip">@ip</a><br />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-fill text-center text-sm-right" id="profile_meta_2">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="profile_level" class="text-muted mb-2">
|
}
|
||||||
<h5><span class="level-color-@Model.LevelInt @(isTempBanned ? "penalties-color-tempban" : "")"><strong>@Model.Level @(isTempBanned ? $"({loc["WEBFRONT_PROFILE_TEMPBAN"]})" : "")</strong></span></h5>
|
|
||||||
</div>
|
|
||||||
<div id="profile_time_played" class="text-muted">
|
|
||||||
@loc["WEBFRONT_PROFILE_PLAYER"] <span class="text-primary">@Model.TimePlayed</span> @loc["GLOBAL_TIME_HOURS"]
|
|
||||||
</div>
|
|
||||||
<div id="profile_first_seen" class="text-muted">
|
|
||||||
@loc["WEBFRONT_PROFILE_FSEEN"] <span class="text-primary">@Model.FirstSeen</span> @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]
|
|
||||||
</div>
|
|
||||||
<div id="profile_last_seen" class="text-muted">
|
|
||||||
@loc["WEBFRONT_PROFILE_LSEEN"] <span class="text-primary">@Model.LastSeen</span> @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="profile_meta" class="text-center text-sm-right pt-2 mt-md-4 pt-md-3 mr-4 pr-4 mr-md-0 ml-4 pl-4 ml-md-0 pr-md-0 pl-md-0">
|
|
||||||
<div class="profile-meta-entry">
|
|
||||||
<span class="profile-meta-value text-primary">@Model.ConnectionCount</span>
|
|
||||||
<span class="profile-meta-value text-muted"> @loc["WEBFRONT_CLIENT_META_CONNECTIONS"]</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row d-md-flex pt-2">
|
<div class="row d-md-flex pt-2">
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||||
<RazorCompileOnBuild>true</RazorCompileOnBuild>
|
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||||
<RazorCompileOnPublish>true</RazorCompileOnPublish>
|
<RazorCompileOnPublish>true</RazorCompileOnPublish>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<TypeScriptToolsVersion>2.6</TypeScriptToolsVersion>
|
<TypeScriptToolsVersion>2.6</TypeScriptToolsVersion>
|
||||||
|
@ -164,10 +164,6 @@
|
|||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile_wrapper {
|
|
||||||
border-bottom: 2px rgb(0, 122, 204) solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-event-timestep {
|
.profile-event-timestep {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// keeps track of how many events have been displayed
|
// keeps track of how many events have been displayed
|
||||||
let count = 1;
|
let count = 1;
|
||||||
|
let metaIndex = 0;
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
@ -51,7 +52,15 @@ $(document).ready(function () {
|
|||||||
$.each(clientInfo.Meta, function (index, meta) {
|
$.each(clientInfo.Meta, function (index, meta) {
|
||||||
if (!meta.key.includes("Event")) {
|
if (!meta.key.includes("Event")) {
|
||||||
let metaString = `<div class="profile-meta-entry"><span class="profile-meta-value text-primary">${meta.value}</span><span class="profile-meta-title text-muted"> ${meta.key}</span></div>`;
|
let metaString = `<div class="profile-meta-entry"><span class="profile-meta-value text-primary">${meta.value}</span><span class="profile-meta-title text-muted"> ${meta.key}</span></div>`;
|
||||||
$("#profile_meta").append(metaString);
|
if (metaIndex < 10) {
|
||||||
|
let selector = '#profile_meta_' + ((metaIndex % 2) + 1);
|
||||||
|
$(selector).append(metaString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let selector = '#profile_meta_' + (metaIndex % 3);
|
||||||
|
$(selector).append(metaString);
|
||||||
|
}
|
||||||
|
metaIndex++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user