Search Query Feature (#1729) +3 other changes

- Implemented Search query feature (see linked issue at the end for details)

List of unrelated features and changes (other than search query) in this PR that affect the change log:

- Player tables now show active playtime instead of playtime
  - /v1/players and /v1/network/players endpoints no longer give playtime (Considered non-breaking change since the endpoints return arrays have been dynamic for DataTables anyway)
- Updated all locale files with missing translations
- Updated HighCharts

Affects issues:
- Close #963
- Notify #638 #1252 #1282 #1283 #1624
This commit is contained in:
Risto Lahtela 2021-01-31 13:33:23 +02:00 committed by GitHub
commit ed18a7b20c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 6407 additions and 6374 deletions

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.addons.placeholderapi.BukkitPlaceholderRegistrar;
import com.djrapitops.plan.commands.PlanCommand;
import com.djrapitops.plan.gathering.ServerShutdownSave;
import com.djrapitops.plan.modules.APFModule;
import com.djrapitops.plan.modules.FiltersModule;
import com.djrapitops.plan.modules.PlaceholderModule;
import com.djrapitops.plan.modules.ServerCommandModule;
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
@ -42,6 +43,7 @@ import javax.inject.Singleton;
BukkitPlanModule.class,
SystemObjectProvidingModule.class,
APFModule.class,
FiltersModule.class,
PlaceholderModule.class,
ServerCommandModule.class,

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan;
import com.djrapitops.plan.commands.PlanCommand;
import com.djrapitops.plan.modules.APFModule;
import com.djrapitops.plan.modules.FiltersModule;
import com.djrapitops.plan.modules.PlaceholderModule;
import com.djrapitops.plan.modules.ProxySuperClassBindingModule;
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
@ -38,6 +39,7 @@ import javax.inject.Singleton;
BungeeCommandModule.class,
SystemObjectProvidingModule.class,
APFModule.class,
FiltersModule.class,
PlaceholderModule.class,
ProxySuperClassBindingModule.class,

View File

@ -33,7 +33,7 @@ public class TablePlayer implements Comparable<TablePlayer> {
private UUID uuid;
private String name;
private ActivityIndex activityIndex;
private Long playtime;
private Long activePlaytime;
private Integer sessionCount;
private Long registered;
private Long lastSeen;
@ -67,8 +67,8 @@ public class TablePlayer implements Comparable<TablePlayer> {
return Optional.ofNullable(activityIndex);
}
public Optional<Long> getPlaytime() {
return Optional.ofNullable(playtime);
public Optional<Long> getActivePlaytime() {
return Optional.ofNullable(activePlaytime);
}
public Optional<Integer> getSessionCount() {
@ -105,7 +105,7 @@ public class TablePlayer implements Comparable<TablePlayer> {
if (this == o) return true;
if (!(o instanceof TablePlayer)) return false;
TablePlayer that = (TablePlayer) o;
return playtime.equals(that.playtime) &&
return activePlaytime.equals(that.activePlaytime) &&
sessionCount.equals(that.sessionCount) &&
registered.equals(that.registered) &&
lastSeen.equals(that.lastSeen) &&
@ -116,7 +116,7 @@ public class TablePlayer implements Comparable<TablePlayer> {
@Override
public int hashCode() {
return Objects.hash(name, activityIndex, playtime, sessionCount, registered, lastSeen, geolocation);
return Objects.hash(name, activityIndex, activePlaytime, sessionCount, registered, lastSeen, geolocation);
}
@Override
@ -125,7 +125,7 @@ public class TablePlayer implements Comparable<TablePlayer> {
"uuid=" + uuid +
", name='" + name + '\'' +
", activityIndex=" + activityIndex +
", playtime=" + playtime +
", activePlaytime=" + activePlaytime +
", sessionCount=" + sessionCount +
", registered=" + registered +
", lastSeen=" + lastSeen +
@ -165,8 +165,8 @@ public class TablePlayer implements Comparable<TablePlayer> {
return this;
}
public Builder playtime(long playtime) {
player.playtime = playtime;
public Builder activePlaytime(long activePlaytime) {
player.activePlaytime = activePlaytime;
return this;
}

View File

@ -101,11 +101,11 @@ public class ActivityIndex {
};
}
return new String[]{
locale.get(HtmlLang.INDEX_VERY_ACTIVE).toString(),
locale.get(HtmlLang.INDEX_ACTIVE).toString(),
locale.get(HtmlLang.INDEX_REGULAR).toString(),
locale.get(HtmlLang.INDEX_IRREGULAR).toString(),
locale.get(HtmlLang.INDEX_INACTIVE).toString()
locale.getString(HtmlLang.INDEX_VERY_ACTIVE),
locale.getString(HtmlLang.INDEX_ACTIVE),
locale.getString(HtmlLang.INDEX_REGULAR),
locale.getString(HtmlLang.INDEX_IRREGULAR),
locale.getString(HtmlLang.INDEX_INACTIVE)
};
}
@ -175,4 +175,18 @@ public class ActivityIndex {
public String getGroup() {
return getGroup(value);
}
public String getGroup(Locale locale) {
if (value >= VERY_ACTIVE) {
return locale.getString(HtmlLang.INDEX_VERY_ACTIVE);
} else if (value >= ACTIVE) {
return locale.getString(HtmlLang.INDEX_ACTIVE);
} else if (value >= REGULAR) {
return locale.getString(HtmlLang.INDEX_REGULAR);
} else if (value >= IRREGULAR) {
return locale.getString(HtmlLang.INDEX_IRREGULAR);
} else {
return locale.getString(HtmlLang.INDEX_INACTIVE);
}
}
}

View File

@ -42,6 +42,7 @@ public class Formatters {
private final DayFormatter dayLongFormatter;
private final SecondFormatter secondLongFormatter;
private final ClockFormatter clockLongFormatter;
private final JavascriptDateFormatter javascriptDateFormatter;
private final ISO8601NoClockFormatter iso8601NoClockLongFormatter;
private final ISO8601NoClockTZIndependentFormatter iso8601NoClockTZIndependentFormatter;
@ -57,6 +58,7 @@ public class Formatters {
dayLongFormatter = new DayFormatter(config, locale);
clockLongFormatter = new ClockFormatter(config, locale);
secondLongFormatter = new SecondFormatter(config, locale);
javascriptDateFormatter = new JavascriptDateFormatter(config, locale);
iso8601NoClockLongFormatter = new ISO8601NoClockFormatter(config, locale);
iso8601NoClockTZIndependentFormatter = new ISO8601NoClockTZIndependentFormatter();
@ -109,6 +111,10 @@ public class Formatters {
return iso8601NoClockFormatter;
}
public Formatter<Long> javascriptDateFormatterLong() {
return javascriptDateFormatter;
}
public Formatter<Long> iso8601NoClockLong() {
return iso8601NoClockLongFormatter;
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.formatting.time;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.locale.Locale;
/**
* Formats epoch milliseconds to the date format Javascript Date constructor expects.
*
* @author Rsl1122
*/
public class JavascriptDateFormatter extends DateFormatter {
public JavascriptDateFormatter(PlanConfig config, Locale locale) {
super(config, locale);
}
@Override
public String apply(Long epochMs) {
return format(epochMs, "dd/MM/yyyy kk:mm");
}
}

View File

@ -24,7 +24,7 @@ import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerPlayerDataTableQuery;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerTableDataQuery;
import com.djrapitops.plan.gathering.cache.SessionCache;
import com.djrapitops.plan.gathering.domain.Ping;
import com.djrapitops.plan.gathering.domain.PlayerKill;
@ -41,6 +41,8 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
import com.djrapitops.plan.storage.database.queries.objects.*;
import com.djrapitops.plan.storage.database.queries.objects.playertable.NetworkTablePlayersQuery;
import com.djrapitops.plan.storage.database.queries.objects.playertable.ServerTablePlayersQuery;
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
import javax.inject.Inject;
@ -80,7 +82,7 @@ public class JSONFactory {
this.formatters = formatters;
}
public String serverPlayersTableJSON(UUID serverUUID) {
public Map<String, Object> serverPlayersTableJSON(UUID serverUUID) {
Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE);
Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
boolean openPlayerLinksInNewTab = config.isTrue(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB);
@ -89,13 +91,13 @@ public class JSONFactory {
return new PlayersTableJSONCreator(
database.query(new ServerTablePlayersQuery(serverUUID, System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)),
database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)),
database.query(new ExtensionServerTableDataQuery(serverUUID, xMostRecentPlayers)),
openPlayerLinksInNewTab,
formatters, locale
).toJSONString();
).toJSONMap();
}
public String networkPlayersTableJSON() {
public Map<String, Object> networkPlayersTableJSON() {
Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_PLAYERS_PAGE);
Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
boolean openPlayerLinksInNewTab = config.isTrue(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB);
@ -103,14 +105,14 @@ public class JSONFactory {
Database database = dbSystem.getDatabase();
UUID mainServerUUID = database.query(ServerQueries.fetchProxyServerInformation()).map(Server::getUuid).orElse(serverInfo.getServerUUID());
Map<UUID, ExtensionTabData> pluginData = database.query(new ExtensionServerPlayerDataTableQuery(mainServerUUID, xMostRecentPlayers));
Map<UUID, ExtensionTabData> pluginData = database.query(new ExtensionServerTableDataQuery(mainServerUUID, xMostRecentPlayers));
return new PlayersTableJSONCreator(
database.query(new NetworkTablePlayersQuery(System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)),
pluginData,
openPlayerLinksInNewTab,
formatters, locale
).toJSONString();
).toJSONMap();
}
public List<Map<String, Object>> serverSessionsAsJSONMap(UUID serverUUID) {

View File

@ -28,6 +28,7 @@ import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.implementation.results.*;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.HtmlLang;
import com.djrapitops.plan.utilities.java.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
@ -94,43 +95,37 @@ public class PlayersTableJSONCreator {
}
}
public String toJSONString() {
String data = createData();
String columnHeaders = createColumnHeaders();
return "{\"columns\":" + columnHeaders + ",\"data\":" + data + '}';
public Map<String, Object> toJSONMap() {
return Maps.builder(String.class, Object.class)
.put("columns", createColumnHeaders())
.put("data", createData())
.build();
}
private String createData() {
StringBuilder dataJSON = new StringBuilder("[");
private List<Map<String, Object>> createData() {
List<Map<String, Object>> dataJson = new ArrayList<>();
int currentPlayerNumber = 0;
ExtensionTabData emptyExtensionData = new ExtensionTabData.Builder(null).build();
for (TablePlayer player : players) {
UUID playerUUID = player.getPlayerUUID();
if (playerUUID == null) {
continue;
}
if (currentPlayerNumber > 0) {
dataJSON.append(','); // Previous item
}
dataJSON.append('{'); // Start new item
appendPlayerData(dataJSON, player);
appendExtensionData(dataJSON, extensionData.getOrDefault(playerUUID, new ExtensionTabData.Builder(null).build()));
dataJSON.append('}'); // Close new item
currentPlayerNumber++;
Map<String, Object> playerEntry = new HashMap<>();
addPlayerData(playerEntry, player);
addExtensionData(playerEntry, extensionData.getOrDefault(playerUUID, emptyExtensionData));
dataJson.add(playerEntry);
}
return dataJSON.append(']').toString();
return dataJson;
}
private void appendPlayerData(StringBuilder dataJSON, TablePlayer player) {
private void addPlayerData(Map<String, Object> dataJson, TablePlayer player) {
String name = player.getName().orElse(player.getPlayerUUID().toString());
String url = "../player/" + Html.encodeToURL(name);
int loginTimes = player.getSessionCount().orElse(0);
long playtime = player.getPlaytime().orElse(-1L);
long activePlaytime = player.getActivePlaytime().orElse(-1L);
long registered = player.getRegistered().orElse(-1L);
long lastSeen = player.getLastSeen().orElse(-1L);
@ -143,33 +138,35 @@ public class PlayersTableJSONCreator {
Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK;
dataJSON.append(makeDataEntry(link.create(url, StringUtils.replace(StringEscapeUtils.escapeHtml4(name), "\\", "\\\\")), "name")).append(',') // Backslashes escaped to prevent json errors
.append(makeDataEntry(activityIndex.getValue(), activityString, "index")).append(',')
.append(makeDataEntry(playtime, numberFormatters.get(FormatType.TIME_MILLISECONDS).apply(playtime), "playtime")).append(',')
.append(makeDataEntry(loginTimes, "sessions")).append(',')
.append(makeDataEntry(registered, numberFormatters.get(FormatType.DATE_YEAR).apply(registered), "registered")).append(',')
.append(makeDataEntry(lastSeen, numberFormatters.get(FormatType.DATE_YEAR).apply(lastSeen), "seen")).append(',')
.append(makeDataEntry(geolocation, "geolocation"));
putDataEntry(dataJson, link.create(url, StringUtils.replace(StringEscapeUtils.escapeHtml4(name), "\\", "\\\\") /* Backslashes escaped to prevent json errors */), "name");
putDataEntry(dataJson, activityIndex.getValue(), activityString, "index");
putDataEntry(dataJson, activePlaytime, numberFormatters.get(FormatType.TIME_MILLISECONDS).apply(activePlaytime), "activePlaytime");
putDataEntry(dataJson, loginTimes, "sessions");
putDataEntry(dataJson, registered, numberFormatters.get(FormatType.DATE_YEAR).apply(registered), "registered");
putDataEntry(dataJson, lastSeen, numberFormatters.get(FormatType.DATE_YEAR).apply(lastSeen), "seen");
putDataEntry(dataJson, geolocation, "geolocation");
}
private String makeDataEntry(Object data, String dataName) {
return "\"" + dataName + "\":\"" + StringEscapeUtils.escapeJson(data.toString()) + "\"";
private void putDataEntry(Map<String, Object> dataJson, Object data, String dataName) {
dataJson.put(dataName, data.toString());
}
private String makeDataEntry(Object data, String formatted, String dataName) {
return "\"" + dataName + "\":{\"v\":\"" + StringEscapeUtils.escapeJson(data.toString()) + "\", \"d\":\"" + StringEscapeUtils.escapeJson(formatted) + "\"}";
private void putDataEntry(Map<String, Object> dataJson, Object data, String formatted, String dataName) {
dataJson.put(dataName, Maps.builder(String.class, Object.class)
.put("v", data.toString())
.put("d", formatted)
.build());
}
private void appendExtensionData(StringBuilder dataJSON, ExtensionTabData tabData) {
private void addExtensionData(Map<String, Object> dataJson, ExtensionTabData tabData) {
for (ExtensionDescriptive descriptive : extensionDescriptives) {
dataJSON.append(',');
String key = descriptive.getName();
// If it's a double, append a double
Optional<ExtensionDoubleData> doubleValue = tabData.getDouble(key);
if (doubleValue.isPresent()) {
dataJSON.append(makeDataEntry(doubleValue.get().getRawValue(), doubleValue.get().getFormattedValue(decimalFormatter), key));
putDataEntry(dataJson, doubleValue.get().getRawValue(), doubleValue.get().getFormattedValue(decimalFormatter), key);
continue;
}
@ -177,48 +174,53 @@ public class PlayersTableJSONCreator {
if (numberValue.isPresent()) {
ExtensionNumberData numberData = numberValue.get();
FormatType formatType = numberData.getFormatType();
dataJSON.append(makeDataEntry(numberData.getRawValue(), numberData.getFormattedValue(numberFormatters.get(formatType)), key));
putDataEntry(dataJson, numberData.getRawValue(), numberData.getFormattedValue(numberFormatters.get(formatType)), key);
continue;
}
// If it's a String append a String, otherwise the player has no value for this extension provider.
// If it's a String add a String, otherwise the player has no value for this extension provider.
String stringValue = tabData.getString(key).map(ExtensionStringData::getFormattedValue).orElse("-");
dataJSON.append(makeDataEntry(stringValue, stringValue, key));
putDataEntry(dataJson, stringValue, stringValue, key);
}
}
private String createColumnHeaders() {
StringBuilder columnHeaders = new StringBuilder("[");
private List<Map<String, Object>> createColumnHeaders() {
List<Map<String, Object>> columnHeaders = new ArrayList<>();
// Is the data for the column formatted
columnHeaders.add(makeColumnHeader(Icon.called("user") + " " + locale.get(HtmlLang.LABEL_NAME), "name"));
columnHeaders.add(makeFColumnHeader(Icon.called("check") + " " + locale.get(HtmlLang.LABEL_ACTIVITY_INDEX), "index"));
columnHeaders.add(makeFColumnHeader(Icon.called("clock").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_ACTIVE_PLAYTIME), "activePlaytime"));
columnHeaders.add(makeColumnHeader(Icon.called("calendar-plus").of(Family.REGULAR) + " " + locale.get(HtmlLang.SIDE_SESSIONS), "sessions"));
columnHeaders.add(makeFColumnHeader(Icon.called("user-plus") + " " + locale.get(HtmlLang.LABEL_REGISTERED), "registered"));
columnHeaders.add(makeFColumnHeader(Icon.called("calendar-check").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_LAST_SEEN), "seen"));
columnHeaders.add(makeColumnHeader(Icon.called("globe") + " " + locale.get(HtmlLang.TITLE_COUNTRY), "geolocation"));
columnHeaders
.append(makeColumnHeader(Icon.called("user") + " " + locale.get(HtmlLang.LABEL_NAME), "name")).append(',')
.append(makeFColumnHeader(Icon.called("check") + " " + locale.get(HtmlLang.LABEL_ACTIVITY_INDEX), "index")).append(',')
.append(makeFColumnHeader(Icon.called("clock").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_PLAYTIME), "playtime")).append(',')
.append(makeColumnHeader(Icon.called("calendar-plus").of(Family.REGULAR) + " " + locale.get(HtmlLang.SIDE_SESSIONS), "sessions")).append(',')
.append(makeFColumnHeader(Icon.called("user-plus") + " " + locale.get(HtmlLang.LABEL_REGISTERED), "registered")).append(',')
.append(makeFColumnHeader(Icon.called("calendar-check").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_LAST_SEEN), "seen")).append(',')
.append(makeColumnHeader(Icon.called("globe") + " " + locale.get(HtmlLang.TITLE_COUNTRY), "geolocation"));
addExtensionHeaders(columnHeaders);
appendExtensionHeaders(columnHeaders);
return columnHeaders.append(']').toString();
return columnHeaders;
}
private String makeColumnHeader(String title, String dataProperty) {
return "{\"title\": \"" + StringEscapeUtils.escapeJson(title) + "\",\"data\":\"" + dataProperty + "\"}";
private Map<String, Object> makeColumnHeader(String title, String dataProperty) {
return Maps.builder(String.class, Object.class)
.put("title", title)
.put("data", dataProperty)
.build();
}
private String makeFColumnHeader(String title, String dataProperty) {
return "{\"title\": \"" + StringEscapeUtils.escapeJson(title) + "\",\"data\":{\"_\":\"" + dataProperty + ".v\",\"display\":\"" + dataProperty + ".d\"}}";
private Map<String, Object> makeFColumnHeader(String title, String dataProperty) {
return Maps.builder(String.class, Object.class)
.put("title", title)
.put("data", Maps.builder(String.class, String.class)
.put("_", dataProperty + ".v")
.put("display", dataProperty + ".d")
.build()
).build();
}
private void appendExtensionHeaders(StringBuilder columnHeaders) {
private void addExtensionHeaders(List<Map<String, Object>> columnHeaders) {
for (ExtensionDescriptive provider : extensionDescriptives) {
columnHeaders.append(',');
String headerText = Icon.fromExtensionIcon(provider.getIcon().setColor(Color.NONE)).toHtml().replace('"', '\'') + ' ' + provider.getText();
columnHeaders.append(makeFColumnHeader(headerText, provider.getName()));
columnHeaders.add(makeFColumnHeader(headerText, provider.getName()));
}
}
}

View File

@ -138,8 +138,9 @@ public class GraphJSONCreator {
long now = System.currentTimeMillis();
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
List<Point> points = Lists.map(db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, serverUUID)),
point -> new Point(point.getDate(), point.getValue())
List<Point> points = Lists.map(
db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, serverUUID)),
Point::fromDateObj
);
return "{\"playersOnline\":" + graphs.line().lineGraph(points).toHighChartsSeries() +
",\"color\":\"" + theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE) + "\"}";

View File

@ -44,8 +44,12 @@ public class LineGraphFactory {
}
public LineGraph lineGraph(List<Point> points) {
return lineGraph(points, shouldDisplayGapsInData());
}
public LineGraph lineGraph(List<Point> points, boolean displayGaps) {
points.sort(new PointComparator());
return new LineGraph(points, shouldDisplayGapsInData());
return new LineGraph(points, displayGaps);
}
public LineGraph chunkGraph(TPSMutator mutator) {

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.delivery.rendering.json.graphs.line;
import com.djrapitops.plan.delivery.domain.DateObj;
import java.util.Objects;
/**
@ -23,15 +25,21 @@ import java.util.Objects;
*/
public class Point {
private final double x;
private final Double y;
private Double y;
public Point(double x, Double y) {
this.x = x;
this.y = y;
}
public Point(double x, double y) {
this(x, (Double) y);
public <V extends Number> Point(double x, V y) {
this.x = x;
this.y = y == null ? null : y.doubleValue();
}
public static <V extends Number> Point fromDateObj(DateObj<V> dateObj) {
V value = dateObj.getValue();
return new Point(dateObj.getDate(), value != null ? value.doubleValue() : null);
}
public double getX() {
@ -42,6 +50,10 @@ public class Point {
return y;
}
public void setY(Double y) {
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -63,7 +75,7 @@ public class Point {
"y=" + y + '}';
}
public double[] toArray() {
return new double[]{x, y};
public Double[] toArray() {
return new Double[]{x, y};
}
}

View File

@ -18,6 +18,9 @@ package com.djrapitops.plan.delivery.rendering.pages;
import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.theme.Theme;
import com.djrapitops.plan.utilities.java.UnaryChain;
import com.djrapitops.plugin.api.Check;
/**
@ -29,20 +32,30 @@ public class LoginPage implements Page {
private final String template;
private final ServerInfo serverInfo;
private final Locale locale;
private final Theme theme;
LoginPage(
String htmlTemplate,
ServerInfo serverInfo
ServerInfo serverInfo,
Locale locale,
Theme theme
) {
this.template = htmlTemplate;
this.serverInfo = serverInfo;
this.locale = locale;
this.theme = theme;
}
@Override
public String toHtml() {
PlaceholderReplacer placeholders = new PlaceholderReplacer();
placeholders.put("command", getCommand());
return placeholders.apply(template);
return UnaryChain.of(template)
.chain(theme::replaceThemeColors)
.chain(placeholders::apply)
.chain(locale::replaceLanguageInHtml)
.apply();
}
private String getCommand() {

View File

@ -216,10 +216,17 @@ public class PageFactory {
}
public Page loginPage() throws IOException {
return new LoginPage(getResource("login.html"), serverInfo.get());
return new LoginPage(getResource("login.html"), serverInfo.get(), locale.get(), theme.get());
}
public Page registerPage() throws IOException {
return new LoginPage(getResource("register.html"), serverInfo.get());
return new LoginPage(getResource("register.html"), serverInfo.get(), locale.get(), theme.get());
}
public Page queryPage() throws IOException {
return new QueryPage(
getResource("query.html"),
locale.get(), theme.get(), versionChecker.get()
);
}
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.pages;
import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer;
import com.djrapitops.plan.delivery.rendering.html.Contributors;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.theme.Theme;
import com.djrapitops.plan.utilities.java.UnaryChain;
import com.djrapitops.plan.version.VersionChecker;
/**
* Page to display error stacktrace.
*
* @author Rsl1122
*/
public class QueryPage implements Page {
private final String template;
private final Locale locale;
private final Theme theme;
private final VersionChecker versionChecker;
public QueryPage(
String template,
Locale locale, Theme theme, VersionChecker versionChecker
) {
this.template = template;
this.locale = locale;
this.theme = theme;
this.versionChecker = versionChecker;
}
@Override
public String toHtml() {
PlaceholderReplacer placeholders = new PlaceholderReplacer();
placeholders.put("version", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton()));
placeholders.put("updateModal", versionChecker.getUpdateModal());
placeholders.put("contributors", Contributors.generateContributorHtml());
return UnaryChain.of(template)
.chain(theme::replaceThemeColors)
.chain(placeholders::apply)
.chain(locale::replaceLanguageInHtml)
.apply();
}
}

View File

@ -423,4 +423,12 @@ public class ResponseFactory {
return forInternalError(e, "Failed to generate player page");
}
}
public Response queryPageResponse() {
try {
return forPage(pageFactory.queryPage());
} catch (IOException e) {
return forInternalError(e, "Failed to generate query page");
}
}
}

View File

@ -54,6 +54,7 @@ import java.util.regex.Pattern;
public class ResponseResolver {
private final DebugPageResolver debugPageResolver;
private QueryPageResolver queryPageResolver;
private final PlayersPageResolver playersPageResolver;
private final PlayerPageResolver playerPageResolver;
private final ServerPageResolver serverPageResolver;
@ -78,6 +79,7 @@ public class ResponseResolver {
Lazy<WebServer> webServer,
DebugPageResolver debugPageResolver,
QueryPageResolver queryPageResolver,
PlayersPageResolver playersPageResolver,
PlayerPageResolver playerPageResolver,
ServerPageResolver serverPageResolver,
@ -97,6 +99,7 @@ public class ResponseResolver {
this.responseFactory = responseFactory;
this.webServer = webServer;
this.debugPageResolver = debugPageResolver;
this.queryPageResolver = queryPageResolver;
this.playersPageResolver = playersPageResolver;
this.playerPageResolver = playerPageResolver;
this.serverPageResolver = serverPageResolver;
@ -115,6 +118,7 @@ public class ResponseResolver {
String plugin = "Plan";
resolverService.registerResolver(plugin, "/robots.txt", (NoAuthResolver) request -> Optional.of(responseFactory.robotsResponse()));
resolverService.registerResolver(plugin, "/debug", debugPageResolver);
resolverService.registerResolver(plugin, "/query", queryPageResolver);
resolverService.registerResolver(plugin, "/players", playersPageResolver);
resolverService.registerResolver(plugin, "/player", playerPageResolver);
resolverService.registerResolver(plugin, "/favicon.ico", (NoAuthResolver) request -> Optional.of(responseFactory.faviconResponse()));

View File

@ -53,6 +53,11 @@ public class WebServerSystem implements SubSystem {
ResourceService.getInstance().addStylesToResource("Plan", "players.html", ResourceService.Position.PRE_CONTENT, "./css/noauth.css");
ResourceService.getInstance().addStylesToResource("Plan", "network.html", ResourceService.Position.PRE_CONTENT, "./css/noauth.css");
}
if (webServer.isEnabled()) {
ResourceService.getInstance().addStylesToResource("Plan", "server.html", ResourceService.Position.PRE_CONTENT, "../css/querybutton.css");
ResourceService.getInstance().addStylesToResource("Plan", "players.html", ResourceService.Position.PRE_CONTENT, "./css/querybutton.css");
ResourceService.getInstance().addStylesToResource("Plan", "network.html", ResourceService.Position.PRE_CONTENT, "./css/querybutton.css");
}
}
@Override

View File

@ -0,0 +1,47 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.webserver.resolver;
import com.djrapitops.plan.delivery.web.resolver.Resolver;
import com.djrapitops.plan.delivery.web.resolver.Response;
import com.djrapitops.plan.delivery.web.resolver.request.Request;
import com.djrapitops.plan.delivery.webserver.ResponseFactory;
import javax.inject.Inject;
import java.util.Optional;
public class QueryPageResolver implements Resolver {
private final ResponseFactory responseFactory;
@Inject
public QueryPageResolver(
ResponseFactory responseFactory
) {
this.responseFactory = responseFactory;
}
@Override
public boolean canAccess(Request request) {
return request.getUser().map(user -> user.hasPermission("page.players")).orElse(false);
}
@Override
public Optional<Response> resolve(Request request) {
return Optional.of(responseFactory.queryPageResponse());
}
}

View File

@ -0,0 +1,161 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.webserver.resolver.json;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point;
import com.djrapitops.plan.delivery.web.resolver.MimeType;
import com.djrapitops.plan.delivery.web.resolver.Resolver;
import com.djrapitops.plan.delivery.web.resolver.Response;
import com.djrapitops.plan.delivery.web.resolver.request.Request;
import com.djrapitops.plan.delivery.web.resolver.request.WebUser;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
import com.djrapitops.plan.storage.database.queries.objects.TPSQueries;
import com.djrapitops.plan.utilities.java.Lists;
import org.apache.commons.lang3.StringUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Singleton
public class FiltersJSONResolver implements Resolver {
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final QueryFilters filters;
private final Graphs graphs;
private final Formatters formatters;
@Inject
public FiltersJSONResolver(
ServerInfo serverInfo,
DBSystem dbSystem,
QueryFilters filters,
Graphs graphs,
Formatters formatters
) {
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.filters = filters;
this.graphs = graphs;
this.formatters = formatters;
}
@Override
public boolean canAccess(Request request) {
WebUser user = request.getUser().orElse(new WebUser(""));
return user.hasPermission("page.players");
}
@Override
public Optional<Response> resolve(Request request) {
return Optional.of(getResponse());
}
private Response getResponse() {
return Response.builder()
.setMimeType(MimeType.JSON)
.setJSONContent(new FilterResponseJSON(
filters.getFilters(),
new ViewJSON(formatters),
fetchViewGraphPoints()
)).build();
}
private List<Double[]> fetchViewGraphPoints() {
List<DateObj<Integer>> data = dbSystem.getDatabase().query(TPSQueries.fetchViewPreviewGraphData(serverInfo.getServerUUID()));
Long earliestStart = dbSystem.getDatabase().query(SessionQueries.earliestSessionStart());
data.add(0, new DateObj<>(earliestStart, 1));
boolean displayGaps = true;
return graphs.line().lineGraph(Lists.map(data, Point::fromDateObj), displayGaps).getPoints()
.stream().map(point -> {
if (point.getY() == null) point.setY(0.0);
return point.toArray();
}).collect(Collectors.toList());
}
/**
* JSON serialization class.
*/
static class FilterResponseJSON {
final List<FilterJSON> filters;
final ViewJSON view;
final List<Double[]> viewPoints;
public FilterResponseJSON(Map<String, Filter> filtersByKind, ViewJSON view, List<Double[]> viewPoints) {
this.viewPoints = viewPoints;
this.filters = new ArrayList<>();
for (Map.Entry<String, Filter> entry : filtersByKind.entrySet()) {
filters.add(new FilterJSON(entry.getKey(), entry.getValue()));
}
this.view = view;
}
}
/**
* JSON serialization class.
*/
static class FilterJSON {
final String kind;
final Map<String, Object> options;
final String[] expectedParameters;
public FilterJSON(String kind, Filter filter) {
this.kind = kind;
this.options = filter.getOptions();
this.expectedParameters = filter.getExpectedParameters();
}
}
/**
* JSON serialization class.
*/
static class ViewJSON {
final String afterDate;
final String afterTime;
final String beforeDate;
final String beforeTime;
public ViewJSON(Formatters formatters) {
long now = System.currentTimeMillis();
long monthAgo = now - TimeUnit.DAYS.toMillis(30);
Formatter<Long> formatter = formatters.javascriptDateFormatterLong();
String[] after = StringUtils.split(formatter.apply(monthAgo), " ");
String[] before = StringUtils.split(formatter.apply(now), " ");
this.afterDate = after[0];
this.afterTime = after[1];
this.beforeDate = before[0];
this.beforeTime = before[1];
}
}
}

View File

@ -0,0 +1,213 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.webserver.resolver.json;
import com.djrapitops.plan.delivery.domain.DateMap;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONCreator;
import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator;
import com.djrapitops.plan.delivery.web.resolver.MimeType;
import com.djrapitops.plan.delivery.web.resolver.Resolver;
import com.djrapitops.plan.delivery.web.resolver.Response;
import com.djrapitops.plan.delivery.web.resolver.exception.BadRequestException;
import com.djrapitops.plan.delivery.web.resolver.request.Request;
import com.djrapitops.plan.delivery.web.resolver.request.WebUser;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionQueryResultTableDataQuery;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.GeoInfoQueries;
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
import com.djrapitops.plan.storage.json.JSONStorage;
import com.djrapitops.plan.utilities.java.Maps;
import com.djrapitops.plugin.api.TimeAmount;
import com.google.gson.Gson;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
@Singleton
public class QueryJSONResolver implements Resolver {
private final QueryFilters filters;
private final PlanConfig config;
private final DBSystem dbSystem;
private final JSONStorage jsonStorage;
private final GraphJSONCreator graphJSONCreator;
private final Locale locale;
private final Formatters formatters;
@Inject
public QueryJSONResolver(
QueryFilters filters,
PlanConfig config,
DBSystem dbSystem,
JSONStorage jsonStorage,
GraphJSONCreator graphJSONCreator,
Locale locale,
Formatters formatters
) {
this.filters = filters;
this.config = config;
this.dbSystem = dbSystem;
this.jsonStorage = jsonStorage;
this.graphJSONCreator = graphJSONCreator;
this.locale = locale;
this.formatters = formatters;
}
@Override
public boolean canAccess(Request request) {
WebUser user = request.getUser().orElse(new WebUser(""));
return user.hasPermission("page.players");
}
@Override
public Optional<Response> resolve(Request request) {
return Optional.of(getResponse(request));
}
private Response getResponse(Request request) {
Optional<Response> cachedResult = checkForCachedResult(request);
if (cachedResult.isPresent()) return cachedResult.get();
String q = request.getQuery().get("q").orElseThrow(() -> new BadRequestException("'q' parameter not set (expecting json array)"));
String view = request.getQuery().get("view").orElseThrow(() -> new BadRequestException("'view' parameter not set (expecting json object {afterDate, afterTime, beforeDate, beforeTime})"));
try {
String query = URLDecoder.decode(q, "UTF-8");
List<SpecifiedFilterInformation> queries = SpecifiedFilterInformation.parse(query);
Filter.Result result = filters.apply(queries);
List<Filter.ResultPath> resultPath = result.getInverseResultPath();
Collections.reverse(resultPath);
return buildAndStoreResponse(view, result, resultPath);
} catch (IOException e) {
throw new BadRequestException("Failed to decode json: '" + q + "', " + e.getMessage());
}
}
private Optional<Response> checkForCachedResult(Request request) {
try {
return request.getQuery().get("timestamp")
.flatMap(queryTimestamp -> jsonStorage.fetchExactJson("query", Long.parseLong(queryTimestamp)))
.map(results -> Response.builder()
.setMimeType(MimeType.JSON)
.setJSONContent(results.json)
.build());
} catch (NumberFormatException e) {
throw new BadRequestException("Could not parse 'timestamp' into a number. Remove parameter or fix it.");
}
}
private Response buildAndStoreResponse(String view, Filter.Result result, List<Filter.ResultPath> resultPath) {
try {
long timestamp = System.currentTimeMillis();
Map<String, Object> json = Maps.builder(String.class, Object.class)
.put("path", resultPath)
.put("view", new Gson().fromJson(view, FiltersJSONResolver.ViewJSON.class))
.put("timestamp", timestamp)
.build();
if (!result.isEmpty()) {
json.put("data", getDataFor(result.getResultUUIDs(), view));
}
JSONStorage.StoredJSON stored = jsonStorage.storeJson("query", json, timestamp);
return Response.builder()
.setMimeType(MimeType.JSON)
.setJSONContent(stored.json)
.build();
} catch (ParseException e) {
throw new BadRequestException("'view' date format was incorrect (expecting afterDate dd/mm/yyyy, afterTime hh:mm, beforeDate dd/mm/yyyy, beforeTime hh:mm}): " + e.getMessage());
}
}
private Map<String, Object> getDataFor(Set<UUID> playerUUIDs, String view) throws ParseException {
FiltersJSONResolver.ViewJSON viewJSON = new Gson().fromJson(view, FiltersJSONResolver.ViewJSON.class);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy kk:mm");
long after = dateFormat.parse(viewJSON.afterDate + " " + viewJSON.afterTime).getTime();
long before = dateFormat.parse(viewJSON.beforeDate + " " + viewJSON.beforeTime).getTime();
return Maps.builder(String.class, Object.class)
.put("players", getPlayersTableData(playerUUIDs, after, before))
.put("activity", getActivityGraphData(playerUUIDs, after, before))
.put("geolocation", getGeolocationData(playerUUIDs))
.put("sessions", getSessionSummaryData(playerUUIDs, after, before))
.build();
}
private Map<String, String> getSessionSummaryData(Set<UUID> playerUUIDs, long after, long before) {
Database database = dbSystem.getDatabase();
Map<String, Long> summary = database.query(SessionQueries.summaryOfPlayers(playerUUIDs, after, before));
Map<String, String> formattedSummary = new HashMap<>();
Formatter<Long> timeAmount = formatters.timeAmount();
for (Map.Entry<String, Long> entry : summary.entrySet()) {
formattedSummary.put(entry.getKey(), timeAmount.apply(entry.getValue()));
}
formattedSummary.put("total_sessions", Long.toString(summary.get("total_sessions")));
formattedSummary.put("average_sessions", Long.toString(summary.get("average_sessions")));
return formattedSummary;
}
private Map<String, Object> getGeolocationData(Set<UUID> playerUUIDs) {
Database database = dbSystem.getDatabase();
return graphJSONCreator.createGeolocationJSON(
database.query(GeoInfoQueries.networkGeolocationCounts(playerUUIDs))
);
}
private Map<String, Object> getActivityGraphData(Set<UUID> playerUUIDs, long after, long before) {
Database database = dbSystem.getDatabase();
Long threshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
long twoMonthsBeforeLastDate = before - TimeAmount.MONTH.toMillis(2L);
long stopDate = Math.max(twoMonthsBeforeLastDate, after);
DateMap<Map<String, Integer>> activityData = new DateMap<>();
for (long time = before; time >= stopDate; time -= TimeAmount.WEEK.toMillis(1L)) {
activityData.put(time, database.query(NetworkActivityIndexQueries.fetchActivityIndexGroupingsOn(time, threshold, playerUUIDs)));
}
return graphJSONCreator.createActivityGraphJSON(activityData);
}
private Map<String, Object> getPlayersTableData(Set<UUID> playerUUIDs, long after, long before) {
Database database = dbSystem.getDatabase();
return new PlayersTableJSONCreator(
database.query(new QueryTablePlayersQuery(playerUUIDs, after, before, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD))),
database.query(new ExtensionQueryResultTableDataQuery(playerUUIDs)),
config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB),
formatters, locale
).toJSONMap();
}
}

View File

@ -52,7 +52,9 @@ public class RootJSONResolver {
PerformanceJSONCreator performanceJSONCreator,
PlayerJSONResolver playerJSONResolver,
NetworkJSONResolver networkJSONResolver
NetworkJSONResolver networkJSONResolver,
FiltersJSONResolver filtersJSONResolver,
QueryJSONResolver queryJSONResolver
) {
this.identifiers = identifiers;
@ -70,6 +72,8 @@ public class RootJSONResolver {
.add("performanceOverview", forJSON(DataID.PERFORMANCE_OVERVIEW, performanceJSONCreator))
.add("player", playerJSONResolver)
.add("network", networkJSONResolver.getResolver())
.add("filters", filtersJSONResolver)
.add("query", queryJSONResolver)
.build();
}

View File

@ -0,0 +1,203 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.queries;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.results.*;
import com.djrapitops.plan.storage.database.SQLDB;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.*;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Query Extension data of x most recent players matching a query
* <p>
* Returns Map: Player UUID - {@link ExtensionTabData} (container for provider based data)
*
* @author Rsl1122
*/
public class ExtensionQueryResultTableDataQuery implements Query<Map<UUID, ExtensionTabData>> {
private final Collection<UUID> playerUUIDs;
public ExtensionQueryResultTableDataQuery(Collection<UUID> playerUUIDs) {
this.playerUUIDs = playerUUIDs;
}
@Override
public Map<UUID, ExtensionTabData> executeQuery(SQLDB db) {
return combine(db.query(fetchPlayerData()), db.query(fetchPlayerGroups()));
}
private Map<UUID, ExtensionTabData> combine(Map<UUID, ExtensionTabData> one, Map<UUID, ExtensionTabData> two) {
for (Map.Entry<UUID, ExtensionTabData> entry : two.entrySet()) {
UUID playerUUID = entry.getKey();
ExtensionTabData data = entry.getValue();
ExtensionTabData existingData = one.get(playerUUID);
if (existingData != null) {
existingData.combine(data);
} else {
existingData = data;
}
one.put(playerUUID, existingData);
}
return one;
}
private Query<Map<UUID, ExtensionTabData>> fetchPlayerData() {
String sql = SELECT +
"v1." + ExtensionPlayerValueTable.USER_UUID + " as uuid," +
"v1." + ExtensionPlayerValueTable.BOOLEAN_VALUE + " as boolean_value," +
"v1." + ExtensionPlayerValueTable.DOUBLE_VALUE + " as double_value," +
"v1." + ExtensionPlayerValueTable.PERCENTAGE_VALUE + " as percentage_value," +
"v1." + ExtensionPlayerValueTable.LONG_VALUE + " as long_value," +
"v1." + ExtensionPlayerValueTable.STRING_VALUE + " as string_value," +
"null as group_value," +
"p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
"p1." + ExtensionProviderTable.TEXT + " as text," +
"p1." + ExtensionProviderTable.FORMAT_TYPE + " as format_type," +
"p1." + ExtensionProviderTable.IS_PLAYER_NAME + " as is_player_name," +
"i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," +
"i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" +
FROM + ExtensionPlayerValueTable.TABLE_NAME + " v1" +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionPlayerValueTable.PROVIDER_ID +
INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID +
WHERE + "v1." + ExtensionPlayerValueTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')" +
AND + "p1." + ExtensionProviderTable.SHOW_IN_PLAYERS_TABLE + "=?" +
AND + "p1." + ExtensionProviderTable.IS_PLAYER_NAME + "=?";
return new QueryStatement<Map<UUID, ExtensionTabData>>(sql, 1000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true); // Select only values that should be shown
statement.setBoolean(2, false); // Don't select player_name String values
}
@Override
public Map<UUID, ExtensionTabData> processResults(ResultSet set) throws SQLException {
return extractDataByPlayer(set);
}
};
}
private Query<Map<UUID, ExtensionTabData>> fetchPlayerGroups() {
String sql = SELECT +
"v1." + ExtensionGroupsTable.USER_UUID + " as uuid," +
"v1." + ExtensionGroupsTable.GROUP_NAME + " as group_value," +
"p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
"p1." + ExtensionProviderTable.TEXT + " as text," +
"i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," +
"i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" +
FROM + ExtensionGroupsTable.TABLE_NAME + " v1" +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionGroupsTable.PROVIDER_ID +
INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID +
WHERE + "v1." + ExtensionPlayerValueTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')";
return new QueryAllStatement<Map<UUID, ExtensionTabData>>(sql, 1000) {
@Override
public Map<UUID, ExtensionTabData> processResults(ResultSet set) throws SQLException {
return extractDataByPlayer(set);
}
};
}
private Map<UUID, ExtensionTabData> extractDataByPlayer(ResultSet set) throws SQLException {
Map<UUID, ExtensionTabData.Builder> dataByPlayer = new HashMap<>();
while (set.next()) {
UUID playerUUID = UUID.fromString(set.getString("uuid"));
ExtensionTabData.Builder data = dataByPlayer.getOrDefault(playerUUID, new ExtensionTabData.Builder(null));
ExtensionDescriptive extensionDescriptive = extractDescriptive(set);
extractAndPutDataTo(data, extensionDescriptive, set);
dataByPlayer.put(playerUUID, data);
}
return dataByPlayer.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build()));
}
private void extractAndPutDataTo(ExtensionTabData.Builder extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException {
String groupValue = set.getString("group_value");
if (groupValue != null) {
extensionTab.putGroupData(new ExtensionStringData(descriptive, false, groupValue));
return;
}
boolean booleanValue = set.getBoolean(ExtensionServerValueTable.BOOLEAN_VALUE);
if (!set.wasNull()) {
extensionTab.putBooleanData(new ExtensionBooleanData(descriptive, booleanValue));
return;
}
double doubleValue = set.getDouble(ExtensionPlayerValueTable.DOUBLE_VALUE);
if (!set.wasNull()) {
extensionTab.putDoubleData(new ExtensionDoubleData(descriptive, doubleValue));
return;
}
double percentageValue = set.getDouble(ExtensionServerValueTable.PERCENTAGE_VALUE);
if (!set.wasNull()) {
extensionTab.putPercentageData(new ExtensionDoubleData(descriptive, percentageValue));
return;
}
long numberValue = set.getLong(ExtensionPlayerValueTable.LONG_VALUE);
if (!set.wasNull()) {
FormatType formatType = FormatType.getByName(set.getString(ExtensionProviderTable.FORMAT_TYPE)).orElse(FormatType.NONE);
extensionTab.putNumberData(new ExtensionNumberData(descriptive, formatType, numberValue));
return;
}
String stringValue = set.getString(ExtensionPlayerValueTable.STRING_VALUE);
if (stringValue != null) {
boolean isPlayerName = false;
extensionTab.putStringData(new ExtensionStringData(descriptive, isPlayerName, stringValue));
}
}
private ExtensionDescriptive extractDescriptive(ResultSet set) throws SQLException {
String name = set.getString("provider_name");
String text = set.getString(ExtensionProviderTable.TEXT);
String iconName = set.getString("provider_icon_name");
Family family = Family.getByName(set.getString("provider_icon_family")).orElse(Family.SOLID);
Icon icon = new Icon(family, iconName, Color.NONE);
return new ExtensionDescriptive(name, text, null, icon, 0);
}
}

View File

@ -43,12 +43,12 @@ import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
*
* @author Rsl1122
*/
public class ExtensionServerPlayerDataTableQuery implements Query<Map<UUID, ExtensionTabData>> {
public class ExtensionServerTableDataQuery implements Query<Map<UUID, ExtensionTabData>> {
private final UUID serverUUID;
private final int xMostRecentPlayers;
public ExtensionServerPlayerDataTableQuery(UUID serverUUID, int xMostRecentPlayers) {
public ExtensionServerTableDataQuery(UUID serverUUID, int xMostRecentPlayers) {
this.serverUUID = serverUUID;
this.xMostRecentPlayers = xMostRecentPlayers;
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.queries;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionGroupsTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
public class ExtensionUUIDsInGroupQuery extends QueryStatement<Set<UUID>> {
private final String pluginName;
private final String groupProvider;
private final List<String> inGroups;
public ExtensionUUIDsInGroupQuery(String pluginName, String groupProvider, List<String> inGroups) {
super(buildSQL(inGroups), 100);
this.pluginName = pluginName;
this.groupProvider = groupProvider;
this.inGroups = inGroups;
}
private static String buildSQL(Collection<String> inGroups) {
TextStringBuilder dynamicInClauseAllocation = new TextStringBuilder();
dynamicInClauseAllocation.appendWithSeparators(inGroups.stream().map(group -> "?").toArray(), ",");
return SELECT + ExtensionGroupsTable.USER_UUID +
FROM + ExtensionGroupsTable.TABLE_NAME +
WHERE + ExtensionGroupsTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID +
AND + ExtensionGroupsTable.GROUP_NAME + " IN (" + dynamicInClauseAllocation.build() + ")";
}
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, groupProvider);
statement.setString(2, pluginName);
for (int i = 1; i <= inGroups.size(); i++) {
statement.setString(i + 2, inGroups.get(i));
}
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> uuids = new HashSet<>();
while (set.next()) {
uuids.add(UUID.fromString(set.getString(ExtensionGroupsTable.USER_UUID)));
}
return uuids;
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.modules;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.filters.*;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
@Module
public interface FiltersModule {
@Binds
@IntoSet
Filter filter1(PlayedBetweenDateRangeFilter filter);
@Binds
@IntoSet
Filter filter2(RegisteredBetweenDateRangeFilter filter);
@Binds
@IntoSet
Filter filter3(OperatorsFilter filter);
@Binds
@IntoSet
Filter filter4(BannedFilter filter);
@Binds
@IntoSet
Filter filter5(ActivityIndexFilter filter);
}

View File

@ -25,6 +25,8 @@ import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.LocaleSystem;
import com.djrapitops.plan.storage.file.JarResource;
import com.djrapitops.plan.storage.json.JSONFileStorage;
import com.djrapitops.plan.storage.json.JSONStorage;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
import dagger.Module;
@ -96,4 +98,10 @@ public class SystemObjectProvidingModule {
return dataService;
}
@Provides
@Singleton
JSONStorage provideJSONStorage(JSONFileStorage jsonFileStorage) {
return jsonFileStorage;
}
}

View File

@ -178,6 +178,15 @@ public class Locale extends HashMap<Lang, Message> {
HtmlLang.LABEL_FREE_DISK_SPACE,
HtmlLang.LABEL_NEW_PLAYERS,
HtmlLang.LABEL_UNIQUE_PLAYERS,
HtmlLang.LABEL_ACTIVE_PLAYTIME,
HtmlLang.LABEL_AFK_TIME,
HtmlLang.LABEL_AVG_SESSION_LENGTH,
HtmlLang.LABEL_AVG_PLAYTIME,
HtmlLang.LABEL_AVG_ACTIVE_PLAYTIME,
HtmlLang.LABEL_AVG_AFK_TIME,
HtmlLang.LABEL_AVG_PLAYTIME,
HtmlLang.SIDE_GEOLOCATIONS,
HtmlLang.LABEL_PER_PLAYER
}) {
getNonDefault(extra).ifPresent(replacement ->

View File

@ -69,14 +69,15 @@ public class LocaleSystem implements SubSystem {
public static Map<String, Lang> getIdentifiers() {
Lang[][] lang = new Lang[][]{
CommandLang.values(),
HelpLang.values(),
DeepHelpLang.values(),
PluginLang.values(),
GenericLang.values(),
HtmlLang.values(),
ErrorPageLang.values(),
FailReason.values(),
FilterLang.values(),
GenericLang.values(),
HelpLang.values(),
HtmlLang.values(),
JSLang.values(),
PluginLang.values(),
};
return Arrays.stream(lang)

View File

@ -0,0 +1,42 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.settings.locale.lang;
public enum FilterLang implements Lang {
OPERATORS("Operators"),
NON_OPERATORS("Non operators"),
BANNED("Banned"),
NOT_BANNED("Not banned"),
;
private final String defaultValue;
FilterLang(String defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public String getIdentifier() {
return "HTML - " + name() + " (Filters)";
}
@Override
public String getDefault() {
return defaultValue;
}
}

View File

@ -35,6 +35,7 @@ public enum HtmlLang implements Lang {
SIDE_PLAYERBASE_OVERVIEW("Playerbase Overview"),
SIDE_GEOLOCATIONS("Geolocations"),
SIDE_PLUGINS("PLUGINS"), // Nav group title
SIDE_LINKS("LINKS"),
UNIT_NO_DATA("No Data"), // Generic
// Modals
TITLE_THEME_SELECT("Theme Select"),
@ -44,6 +45,7 @@ public enum HtmlLang implements Lang {
LINK_WIKI("Plan Wiki, Tutorials & Documentation"),
LINK_ISSUES("Report Issues"),
LINK_DISCORD("General Support on Discord"),
AND_BUG_REPORTERS("& Bug reporters!"),
TEXT_DEVELOPED_BY("is developed by"),
TEXT_CONTRIBUTORS_THANKS("In addition following <span class=\"col-plan\">awesome people</span> have contributed:"),
TEXT_CONTRIBUTORS_CODE("code contributor"),
@ -59,6 +61,7 @@ public enum HtmlLang implements Lang {
// Network overview tab
TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY("Network Online Activity"),
TITLE_GRAPH_DAY_BY_DAY("Day by Day"),
TITLE_GRAPH_HOUR_BY_HOUR("Hour by Hour"),
UNIT_THE_PLAYERS("Players"),
TITLE_LAST_24_HOURS("Last 24 hours"),
TITLE_LAST_7_DAYS("Last 7 days"),
@ -71,9 +74,13 @@ public enum HtmlLang implements Lang {
LABEL_PLAYERS_ONLINE("Players Online"),
LABEL_TOTAL_PLAYTIME("Total Playtime"),
LABEL_PLAYTIME("Playtime"),
LABEL_ACTIVE_PLAYTIME("Active Playtime"),
LABEL_LAST_PEAK("Last Peak"),
LABEL_BEST_PEAK("Best Peak"),
LABEL_AVG_PLAYTIME("Average Playtime"),
LABEL_AVG_SESSIONS("Average Sessions"),
LABEL_AVG_ACTIVE_PLAYTIME("Average Active Playtime"),
LABEL_AVG_AFK_TIME("Average AFK Time"),
LABEL_PER_PLAYER("/ Player"),
LABEL_AVG_SESSION_LENGTH("Average Session Length"),
TITLE_WEEK_COMPARISON("Week Comparison"),
@ -211,7 +218,44 @@ public enum HtmlLang implements Lang {
WITH("<th>With"),
NO_KILLS("No Kills"),
LABEL_MAX_FREE_DISK("Max Free Disk"),
LABEL_MIN_FREE_DISK("Min Free Disk")
LABEL_MIN_FREE_DISK("Min Free Disk"),
LOGIN_LOGIN("Login"),
LOGIN_LOGOUT("Logout"),
LOGIN_USERNAME("\"Username\""),
LOGIN_PASSWORD("\"Password\""),
LOGIN_FORGOT_PASSWORD("Forgot Password?"),
LOGIN_CREATE_ACCOUNT("Create an Account!"),
LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1("Forgot password? Unregister and register again."),
LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2("Use the following command in game to remove your current user:"),
LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3("Or using console:"),
LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4("After using the command, "),
LOGIN_FAILED("Login failed: "),
REGISTER("Register"),
REGISTER_CREATE_USER("Create a new user"),
REGISTER_USERNAME_TIP("Username can be up to 50 characters."),
REGISTER_PASSWORD_TIP("Password should be more than 8 characters, but there are no limitations."),
REGISTER_HAVE_ACCOUNT("Already have an account? Login!"),
REGISTER_USERNAME_LENGTH("Username can be up to 50 characters, yours is "),
REGISTER_SPECIFY_USERNAME("You need to specify a Username"),
REGISTER_SPECIFY_PASSWORD("You need to specify a Password"),
REGISTER_COMPLETE("Complete Registration"),
REGISTER_COMPLETE_INSTRUCTIONS_1("You can now finish registering the user."),
REGISTER_COMPLETE_INSTRUCTIONS_2("Code expires in 15 minutes"),
REGISTER_COMPLETE_INSTRUCTIONS_3("Use the following command in game to finish registration:"),
REGISTER_COMPLETE_INSTRUCTIONS_4("Or using console:"),
REGISTER_FAILED("Registration failed: "),
REGISTER_CHECK_FAILED("Checking registration status failed: "),
QUERY_PERFORM_QUERY("Perform Query!"),
QUERY_LOADING_FILTERS("Loading filters.."),
QUERY_ADD_FILTER("Add a filter.."),
QUERY_TIME_TO(">to</label>"),
QUERY_TIME_FROM(">from</label>"),
QUERY_SHOW_VIEW("Show a view"),
QUERY("Query<"),
QUERY_MAKE_ANOTHER("Make another query"),
QUERY_MAKE("Make a query"),
;
private final String defaultValue;

View File

@ -38,7 +38,23 @@ public enum JSLang implements Lang {
UNIT_CHUNKS("Chunks"),
LABEL_RELATIVE_JOIN_ACTIVITY("Relative Join Activity"),
LABEL_DAY_OF_WEEK("Day of the Week"),
LABEL_WEEK_DAYS("'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'");
LABEL_WEEK_DAYS("'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'"),
QUERY_ARE_ACTIVITY_GROUP("are in Activity Groups"),
QUERY_ARE_PLUGIN_GROUP("are in ${plugin}'s ${group} Groups"),
QUERY_OF_PLAYERS("of Players who "),
QUERY_AND("and "),
QUERY_PLAYED_BETWEEN("Played between"),
QUERY_REGISTERED_BETWEEN("Registered between"),
QUERY_ZERO_RESULTS("Query produced 0 results"),
QUERY_RESULTS("Query Results"),
QUERY_RESULTS_MATCH("matched ${resultCount} players"),
QUERY_VIEW(" View:"),
QUERY_ACTIVITY_OF_MATCHED_PLAYERS("Activity of matched players"),
QUERY_ACTIVITY_ON("Activity on <span id=\"activity-date\"></span>"),
QUERY_ARE("`are`"),
QUERY_SESSIONS_WITHIN_VIEW("Sessions within view"),
;
private final String defaultValue;

View File

@ -45,7 +45,7 @@ public enum PluginLang implements Lang {
WEB_SERVER_FAIL_EMPTY_FILE("WebServer FAIL - EOF", "WebServer: EOF when reading Certificate file. (Check that the file is not empty)"),
WEB_SERVER_NOTIFY_NO_CERT_FILE("WebServer - Notify no Cert file", "WebServer: Certificate KeyStore File not Found: ${0}"),
WEB_SERVER_NOTIFY_HTTP("WebServer - Notify HTTP", "WebServer: No Certificate -> Using HTTP-server for Visualization."),
WEB_SERVER_NOTIFY_USING_PROXY_MODE("WebServer - Notify Using Proxy", "WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy"),
WEB_SERVER_NOTIFY_USING_PROXY_MODE("WebServer - Notify Using Proxy", "WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy"),
WEB_SERVER_NOTIFY_HTTP_USER_AUTH("WebServer - Notify HTTP User Auth", "WebServer: User Authorization Disabled! (Not secure over HTTP)"),
WEB_SERVER_NOTIFY_HTTPS_USER_AUTH("WebServer - Notify HTTPS User Auth", "WebServer: User Authorization Disabled! (Disabled in config)"),
WEB_SERVER_NOTIFY_IP_WHITELIST("Webserver - Notify IP Whitelist", "Webserver: IP Whitelist is enabled."),
@ -64,6 +64,14 @@ public enum PluginLang implements Lang {
VERSION_FAIL_READ_VERSIONS("Version FAIL - Read versions.txt", "Version information could not be loaded from Github/versions.txt"),
VERSION_FAIL_READ_OLD("Version FAIL - Read info (old)", "Failed to check newest version number"),
VERSION_UPDATE("HTML - Version Update", "Update"),
VERSION_UPDATE_AVAILABLE("HTML - Version Update Available", "Version ${0} is Available!"),
VERSION_UPDATE_INFO("HTML - Version Update Info", "A new version has been released and is now available for download."),
VERSION_UPDATE_DEV("HTML - Version Update Dev", "This version is a DEV release."),
VERSION_CHANGE_LOG("HTML - Version Change log", "View Changelog"),
VERSION_DOWNLOAD("HTML - Version Download", "Download Plan-${0}.jar"),
VERSION_CURRENT("HTML - Version Current", "You have version ${0}"),
DB_APPLY_PATCH("Database - Apply Patch", "Applying Patch: ${0}.."),
DB_APPLIED_PATCHES("Database - Patches Applied", "All database patches applied successfully."),
DB_APPLIED_PATCHES_ALREADY("Database - Patches Applied Already", "All database patches already applied."),

View File

@ -21,14 +21,12 @@ import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
@ -165,6 +163,36 @@ public class NetworkActivityIndexQueries {
};
}
public static Query<Map<String, Integer>> fetchActivityIndexGroupingsOn(long date, long threshold, Collection<UUID> playerUUIDs) {
String selectActivityIndex = selectActivityIndexSQL();
String selectIndexes = SELECT + "activity_index" +
FROM + UsersTable.TABLE_NAME + " u" +
LEFT_JOIN + '(' + selectActivityIndex + ") s on s." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID +
WHERE + "u." + UsersTable.REGISTERED + "<=?" +
AND + "u." + UsersTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')";
return new QueryStatement<Map<String, Integer>>(selectIndexes) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
setSelectActivityIndexSQLParameters(statement, 1, threshold, date);
statement.setLong(9, date);
}
@Override
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
Map<String, Integer> groups = new HashMap<>();
while (set.next()) {
double activityIndex = set.getDouble("activity_index");
String group = ActivityIndex.getGroup(activityIndex);
groups.put(group, groups.getOrDefault(group, 0) + 1);
}
return groups;
}
};
}
public static Query<Integer> countNewPlayersTurnedRegular(long after, long before, Long threshold) {
String selectActivityIndex = selectActivityIndexSQL();
@ -429,4 +457,26 @@ public class NetworkActivityIndexQueries {
}
};
}
public static Query<Map<UUID, ActivityIndex>> activityIndexForAllPlayers(long date, long playtimeThreshold) {
String selectActivityIndex = selectActivityIndexSQL();
return new QueryStatement<Map<UUID, ActivityIndex>>(selectActivityIndex, 1000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
setSelectActivityIndexSQLParameters(statement, 1, playtimeThreshold, date);
}
@Override
public Map<UUID, ActivityIndex> processResults(ResultSet set) throws SQLException {
Map<UUID, ActivityIndex> indexes = new HashMap<>();
while (set.next()) {
indexes.put(
UUID.fromString(set.getString(SessionsTable.USER_UUID)),
new ActivityIndex(set.getDouble("activity_index"), date)
);
}
return indexes;
}
};
}
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter;
/**
* Exception to throw when a {@link Filter} results in a complete set.
*
* @author Rsl1122
*/
public class CompleteSetException extends IllegalStateException {
}

View File

@ -0,0 +1,104 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter;
import java.util.*;
/**
* Represents a query filter for /query page.
*
* @author Rsl1122
*/
public interface Filter {
String getKind();
String[] getExpectedParameters();
default Map<String, Object> getOptions() {
return Collections.emptyMap();
}
/**
* Match some UUIDs to the filter.
*
* @param query Query for the filter
* @return Set of UUIDs this filter applies to
* @throws IllegalArgumentException If the arguments are not valid.
* @throws CompleteSetException If the arguments produce a complete set.
*/
Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query);
default Result apply(SpecifiedFilterInformation query) {
return new Result(null, getKind(), getMatchingUUIDs(query));
}
class Result {
private final Result previous;
private final String filterKind;
private final int resultSize;
private final Set<UUID> currentUUIDs;
private Result(Result previous, String filterKind, Set<UUID> currentUUIDs) {
this.previous = previous;
this.filterKind = filterKind;
this.resultSize = currentUUIDs.size();
this.currentUUIDs = currentUUIDs;
}
public Result apply(Filter filter, SpecifiedFilterInformation query) {
Set<UUID> got = filter.getMatchingUUIDs(query);
currentUUIDs.retainAll(got);
return new Result(this, filter.getKind(), currentUUIDs);
}
public Result notApplied(Filter filter) {
return new Result(this, filter.getKind() + " (skip)", currentUUIDs);
}
public boolean isEmpty() {
return resultSize <= 0;
}
public Set<UUID> getResultUUIDs() {
return currentUUIDs;
}
public List<ResultPath> getInverseResultPath() {
List<ResultPath> path = new ArrayList<>();
Result current = this;
while (current != null) {
path.add(new ResultPath(current.filterKind, current.resultSize));
current = current.previous;
}
return path;
}
}
class ResultPath {
final String kind;
final int size;
public ResultPath(String kind, int size) {
this.kind = kind;
this.size = size;
}
}
}

View File

@ -0,0 +1,116 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter;
import com.djrapitops.plan.delivery.web.resolver.exception.BadRequestException;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.filters.AllPlayersFilter;
import com.djrapitops.plan.storage.database.queries.filter.filters.PluginGroupsFilter;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Contains a single instance of each filter kind.
*
* @author Rsl1122
* @see com.djrapitops.plan.modules.FiltersModule for list of Filters
*/
@Singleton
public class QueryFilters {
private final Map<String, Filter> filters;
private final AllPlayersFilter allPlayersFilter;
private final DBSystem dbSystem;
private final AtomicBoolean fetchedPluginFilters = new AtomicBoolean(false);
@Inject
public QueryFilters(
Set<Filter> filters,
AllPlayersFilter allPlayersFilter,
DBSystem dbSystem
) {
this.allPlayersFilter = allPlayersFilter;
this.dbSystem = dbSystem;
this.filters = new HashMap<>();
put(filters);
}
private void put(Iterable<? extends Filter> filters) {
for (Filter filter : filters) {
this.filters.put(filter.getKind(), filter);
}
}
private void prepareFilters() {
if (!fetchedPluginFilters.get()) {
put(dbSystem.getDatabase().query(new PluginGroupsFilter.PluginGroupsFilterQuery(dbSystem)));
fetchedPluginFilters.set(true);
}
}
public Optional<Filter> getFilter(String kind) {
prepareFilters();
return Optional.ofNullable(filters.get(kind));
}
/**
* Apply queries to get a {@link com.djrapitops.plan.storage.database.queries.filter.Filter.Result}.
*
* @param filterQueries FilterQueries to use as filter parameters.
* @return the result object or null if none of the filterQueries could be applied.
* @throws BadRequestException If the request kind is not supported or if filter was given bad options.
*/
public Filter.Result apply(List<SpecifiedFilterInformation> filterQueries) {
prepareFilters();
Filter.Result current = null;
if (filterQueries.isEmpty()) return allPlayersFilter.apply(null);
for (SpecifiedFilterInformation specifiedFilterInformation : filterQueries) {
current = apply(current, specifiedFilterInformation);
if (current != null && current.isEmpty()) break;
}
return current;
}
private Filter.Result apply(Filter.Result current, SpecifiedFilterInformation specifiedFilterInformation) {
String kind = specifiedFilterInformation.getKind();
Filter filter = getFilter(kind).orElseThrow(() -> new BadRequestException("Filter kind not supported: '" + kind + "'"));
current = getResult(current, filter, specifiedFilterInformation);
return current;
}
private Filter.Result getResult(Filter.Result current, Filter filter, SpecifiedFilterInformation query) {
try {
return current == null ? filter.apply(query) : current.apply(filter, query);
} catch (IllegalArgumentException badOptions) {
throw new BadRequestException("Bad parameters for filter '" + filter.getKind() +
"': expecting " + Arrays.asList(filter.getExpectedParameters()) +
", but was given " + query.getSetParameters());
} catch (CompleteSetException complete) {
return current == null ? null : current.notApplied(filter);
}
}
public Map<String, Filter> getFilters() {
prepareFilters();
return filters;
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.util.*;
/**
* Represents parameters for a single {@link Filter} parsed from the query json.
*
* @author Rsl1122
*/
public class SpecifiedFilterInformation {
private final String kind;
private final Map<String, String> parameters;
public SpecifiedFilterInformation(String kind, Map<String, String> parameters) {
this.kind = kind;
this.parameters = parameters;
}
public static List<SpecifiedFilterInformation> parse(String json) throws IOException {
return new Gson().getAdapter(new TypeToken<List<SpecifiedFilterInformation>>() {}).fromJson(json);
}
public String getKind() {
return kind;
}
public Optional<String> get(String key) {
if (parameters == null) return Optional.empty();
return Optional.ofNullable(parameters.get(key));
}
public Set<String> getSetParameters() {
if (parameters == null) return Collections.emptySet();
return parameters.keySet();
}
}

View File

@ -0,0 +1,88 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries;
import com.djrapitops.plan.storage.database.queries.filter.CompleteSetException;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.stream.Collectors;
@Singleton
public class ActivityIndexFilter extends MultiOptionFilter {
private final PlanConfig config;
private final Locale locale;
private final DBSystem dbSystem;
@Inject
public ActivityIndexFilter(
PlanConfig config,
Locale locale,
DBSystem dbSystem
) {
this.dbSystem = dbSystem;
this.config = config;
this.locale = locale;
}
@Override
public String getKind() {
return "activityIndexNow";
}
private String[] getOptionsArray() {
return ActivityIndex.getGroups(locale);
}
@Override
public Map<String, Object> getOptions() {
return Collections.singletonMap("options", getOptionsArray());
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
List<String> selected = getSelected(query);
String[] options = getOptionsArray();
boolean includeVeryActive = selected.contains(options[0]);
boolean includeActive = selected.contains(options[1]);
boolean includeRegular = selected.contains(options[2]);
boolean includeIrregular = selected.contains(options[3]);
boolean includeInactive = selected.contains(options[4]);
if (includeVeryActive && includeActive && includeRegular && includeIrregular && includeInactive) {
throw new CompleteSetException(); // Full set, no need for query
}
long date = System.currentTimeMillis();
long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
Map<UUID, ActivityIndex> indexes = dbSystem.getDatabase().query(NetworkActivityIndexQueries.activityIndexForAllPlayers(date, playtimeThreshold));
return indexes.entrySet().stream()
.filter(entry -> selected.contains(entry.getValue().getGroup(locale)))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Set;
import java.util.UUID;
/**
* Special filter only used in cases where no filters are specified.
*
* @author Rsl1122
*/
@Singleton
public class AllPlayersFilter implements Filter {
private final DBSystem dbSystem;
@Inject
public AllPlayersFilter(DBSystem dbSystem) {
this.dbSystem = dbSystem;
}
@Override
public String getKind() {
return "allPlayers";
}
@Override
public String[] getExpectedParameters() {
return new String[0];
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
return dbSystem.getDatabase().query(UserIdentifierQueries.fetchAllPlayerUUIDs());
}
}

View File

@ -0,0 +1,73 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.FilterLang;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.CompleteSetException;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
@Singleton
public class BannedFilter extends MultiOptionFilter {
private final DBSystem dbSystem;
private final Locale locale;
@Inject
public BannedFilter(
DBSystem dbSystem,
Locale locale
) {
this.dbSystem = dbSystem;
this.locale = locale;
}
@Override
public String getKind() {
return "banned";
}
private String[] getOptionsArray() {
return new String[]{locale.getString(FilterLang.BANNED), locale.getString(FilterLang.NOT_BANNED)};
}
@Override
public Map<String, Object> getOptions() {
return Collections.singletonMap("options", getOptionsArray());
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
List<String> selected = getSelected(query);
Set<UUID> uuids = new HashSet<>();
String[] options = getOptionsArray();
boolean includeBanned = selected.contains(options[0]);
boolean includeNotBanned = selected.contains(options[1]);
if (includeBanned && includeNotBanned) throw new CompleteSetException(); // Full set, no need for query
if (includeBanned) uuids.addAll(dbSystem.getDatabase().query(UserInfoQueries.uuidsOfBanned()));
if (includeNotBanned) uuids.addAll(dbSystem.getDatabase().query(UserInfoQueries.uuidsOfNotBanned()));
return uuids;
}
}

View File

@ -0,0 +1,82 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.delivery.web.resolver.exception.BadRequestException;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries;
import com.djrapitops.plan.utilities.java.Maps;
import org.apache.commons.lang3.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Optional;
public abstract class DateRangeFilter implements Filter {
private final DBSystem dbSystem;
private final SimpleDateFormat dateFormat;
protected DateRangeFilter(DBSystem dbSystem) {
this.dbSystem = dbSystem;
this.dateFormat = new SimpleDateFormat("dd/MM/yyyy kk:mm");
}
@Override
public String[] getExpectedParameters() {
return new String[]{
"afterDate",
"afterTime",
"beforeDate",
"beforeTime"
};
}
@Override
public Map<String, Object> getOptions() {
Optional<Long> earliestData = dbSystem.getDatabase().query(BaseUserQueries.minimumRegisterDate());
long now = System.currentTimeMillis();
String[] afterDate = StringUtils.split(dateFormat.format(earliestData.orElse(now)), ' ');
String[] beforeDate = StringUtils.split(dateFormat.format(now), ' ');
return Maps.builder(String.class, Object.class)
.put("after", afterDate)
.put("before", beforeDate)
.build();
}
protected long getAfter(SpecifiedFilterInformation query) {
return getTime(query, "afterDate", "afterTime");
}
protected long getBefore(SpecifiedFilterInformation query) {
return getTime(query, "beforeDate", "beforeTime");
}
private long getTime(SpecifiedFilterInformation query, String dateKey, String timeKey) {
String date = query.get(dateKey).orElseThrow(() -> new BadRequestException("'" + dateKey + "' not specified in parameters for " + getKind()));
String time = query.get(timeKey).orElseThrow(() -> new BadRequestException("'" + timeKey + "' not specified in parameters for " + getKind()));
try {
return dateFormat.parse(date + ' ' + time).getTime();
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.storage.database.queries.filter.CompleteSetException;
import com.djrapitops.plan.storage.database.queries.filter.Filter;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.List;
public abstract class MultiOptionFilter implements Filter {
@Override
public String[] getExpectedParameters() {
return new String[]{"selected"};
}
protected List<String> getSelected(SpecifiedFilterInformation query) {
String selectedJSON = query.get("selected").orElseThrow(IllegalArgumentException::new);
List<String> selected = new Gson().fromJson(selectedJSON, new TypeToken<List<String>>() {}.getType());
if (selected.isEmpty()) throw new CompleteSetException();
return selected;
}
}

View File

@ -0,0 +1,73 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.FilterLang;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.CompleteSetException;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
@Singleton
public class OperatorsFilter extends MultiOptionFilter {
private final DBSystem dbSystem;
private final Locale locale;
@Inject
public OperatorsFilter(
DBSystem dbSystem,
Locale locale
) {
this.dbSystem = dbSystem;
this.locale = locale;
}
@Override
public String getKind() {
return "operators";
}
private String[] getOptionsArray() {
return new String[]{locale.getString(FilterLang.OPERATORS), locale.getString(FilterLang.NON_OPERATORS)};
}
@Override
public Map<String, Object> getOptions() {
return Collections.singletonMap("options", getOptionsArray());
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
List<String> selected = getSelected(query);
Set<UUID> uuids = new HashSet<>();
String[] options = getOptionsArray();
boolean includeOperators = selected.contains(options[0]);
boolean includeNonOperators = selected.contains(options[1]);
if (includeOperators && includeNonOperators) throw new CompleteSetException(); // Full set, no need for query
if (includeOperators) uuids.addAll(dbSystem.getDatabase().query(UserInfoQueries.uuidsOfOperators()));
if (includeNonOperators) uuids.addAll(dbSystem.getDatabase().query(UserInfoQueries.uuidsOfNonOperators()));
return uuids;
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Set;
import java.util.UUID;
@Singleton
public class PlayedBetweenDateRangeFilter extends DateRangeFilter {
private final DBSystem dbSystem;
@Inject
public PlayedBetweenDateRangeFilter(DBSystem dbSystem) {
super(dbSystem);
this.dbSystem = dbSystem;
}
@Override
public String getKind() {
return "playedBetween";
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
long after = getAfter(query);
long before = getBefore(query);
return dbSystem.getDatabase().query(SessionQueries.uuidsOfPlayedBetween(after, before));
}
}

View File

@ -0,0 +1,120 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionUUIDsInGroupQuery;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionGroupsTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable;
import com.djrapitops.plan.utilities.java.Maps;
import javax.inject.Singleton;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
public class PluginGroupsFilter extends MultiOptionFilter {
private final DBSystem dbSystem;
private final String pluginName;
private final String groupProvider;
private final List<String> groups;
public PluginGroupsFilter(
DBSystem dbSystem,
String pluginName,
String groupProvider,
List<String> groups
) {
this.dbSystem = dbSystem;
this.pluginName = pluginName;
this.groupProvider = groupProvider;
this.groups = groups;
}
@Override
public String getKind() {
return "pluginGroups-" + pluginName + "-" + groupProvider;
}
@Override
public Map<String, Object> getOptions() {
return Maps.builder(String.class, Object.class)
.put("plugin", pluginName)
.put("group", groupProvider)
.put("options", groups)
.build();
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
return dbSystem.getDatabase().query(
new ExtensionUUIDsInGroupQuery(pluginName, groupProvider, getSelected(query))
);
}
@Singleton
public static class PluginGroupsFilterQuery extends QueryAllStatement<Collection<PluginGroupsFilter>> {
private final DBSystem dbSystem;
public PluginGroupsFilterQuery(DBSystem dbSystem) {
super(SELECT + DISTINCT + "pl." + ExtensionPluginTable.PLUGIN_NAME + " as plugin_name," +
"pr." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
"gr." + ExtensionGroupsTable.GROUP_NAME + " as group_name" +
FROM + ExtensionPluginTable.TABLE_NAME + " pl" +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " pr on pl." + ExtensionPluginTable.ID + "=pr." + ExtensionProviderTable.PLUGIN_ID +
INNER_JOIN + ExtensionGroupsTable.TABLE_NAME + " gr on pr." + ExtensionProviderTable.ID + "=gr." + ExtensionGroupsTable.PROVIDER_ID);
this.dbSystem = dbSystem;
}
@Override
public Collection<PluginGroupsFilter> processResults(ResultSet set) throws SQLException {
Map<String, Map<String, List<String>>> byPlugin = new HashMap<>();
while (set.next()) {
String plugin = set.getString("plugin_name");
String provider = set.getString("provider_name");
String group = set.getString("group_name");
Map<String, List<String>> byProvider = byPlugin.getOrDefault(plugin, new HashMap<>());
List<String> groups = byProvider.getOrDefault(provider, new ArrayList<>());
groups.add(group);
byProvider.put(provider, groups);
byPlugin.put(plugin, byProvider);
}
List<PluginGroupsFilter> filters = new ArrayList<>();
for (Map.Entry<String, Map<String, List<String>>> providersOfPlugin : byPlugin.entrySet()) {
for (Map.Entry<String, List<String>> groupsOfProvider : providersOfPlugin.getValue().entrySet()) {
filters.add(new PluginGroupsFilter(
dbSystem,
providersOfPlugin.getKey(),
groupsOfProvider.getKey(),
groupsOfProvider.getValue()
));
}
}
return filters;
}
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Set;
import java.util.UUID;
@Singleton
public class RegisteredBetweenDateRangeFilter extends DateRangeFilter {
private final DBSystem dbSystem;
@Inject
public RegisteredBetweenDateRangeFilter(DBSystem dbSystem) {
super(dbSystem);
this.dbSystem = dbSystem;
}
@Override
public String getKind() {
return "registeredBetween";
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
long after = getAfter(query);
long before = getBefore(query);
return dbSystem.getDatabase().query(BaseUserQueries.uuidsOfRegisteredBetween(after, before));
}
}

View File

@ -28,10 +28,7 @@ import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
@ -142,4 +139,42 @@ public class BaseUserQueries {
};
}
public static Query<Set<UUID>> uuidsOfRegisteredBetween(long after, long before) {
String sql = SELECT + DISTINCT + UsersTable.USER_UUID +
FROM + UsersTable.TABLE_NAME +
WHERE + UsersTable.REGISTERED + ">=?" +
AND + UsersTable.REGISTERED + "<=?";
return new QueryStatement<Set<UUID>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, after);
statement.setLong(2, before);
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> uuids = new HashSet<>();
while (set.next()) {
uuids.add(UUID.fromString(set.getString(UsersTable.USER_UUID)));
}
return uuids;
}
};
}
public static Query<Optional<Long>> minimumRegisterDate() {
String sql = SELECT + min(UsersTable.REGISTERED) + " as min" +
FROM + UsersTable.TABLE_NAME;
return new QueryAllStatement<Optional<Long>>(sql) {
@Override
public Optional<Long> processResults(ResultSet set) throws SQLException {
if (set.next()) {
long min = set.getLong("min");
if (!set.wasNull()) return Optional.of(min);
}
return Optional.empty();
}
};
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable;
import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
import com.djrapitops.plan.utilities.java.Lists;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -157,6 +158,37 @@ public class GeoInfoQueries {
};
}
public static Query<Map<String, Integer>> networkGeolocationCounts(Collection<UUID> playerUUIDs) {
String subQuery1 = SELECT +
GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED +
FROM + GeoInfoTable.TABLE_NAME +
WHERE + GeoInfoTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')";
String subQuery2 = SELECT +
GeoInfoTable.USER_UUID + ", " +
"MAX(" + GeoInfoTable.LAST_USED + ") as m" +
FROM + GeoInfoTable.TABLE_NAME +
GROUP_BY + GeoInfoTable.USER_UUID;
String sql = SELECT + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM " +
"(" + subQuery1 + ") AS q1" +
INNER_JOIN + "(" + subQuery2 + ") AS q2 ON q1.uuid = q2.uuid" +
WHERE + GeoInfoTable.LAST_USED + "=m" +
GROUP_BY + GeoInfoTable.GEOLOCATION;
return new QueryAllStatement<Map<String, Integer>>(sql) {
@Override
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
Map<String, Integer> geolocationCounts = new HashMap<>();
while (set.next()) {
geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c"));
}
return geolocationCounts;
}
};
}
public static Query<Map<String, Integer>> serverGeolocationCounts(UUID serverUUID) {
String selectGeolocations = SELECT +
GeoInfoTable.USER_UUID + ", " +

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
import com.djrapitops.plan.utilities.java.Maps;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -813,4 +814,93 @@ public class SessionQueries {
}
};
}
public static Query<Set<UUID>> uuidsOfPlayedBetween(long after, long before) {
String sql = SELECT + DISTINCT + SessionsTable.USER_UUID +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_END + ">=?" +
AND + SessionsTable.SESSION_START + "<=?";
return new QueryStatement<Set<UUID>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, after);
statement.setLong(2, before);
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> uuids = new HashSet<>();
while (set.next()) {
uuids.add(UUID.fromString(set.getString(SessionsTable.USER_UUID)));
}
return uuids;
}
};
}
public static Query<Map<String, Long>> summaryOfPlayers(Set<UUID> playerUUIDs, long after, long before) {
String selectAggregates = SELECT +
SessionsTable.USER_UUID + ',' +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + ") as active_playtime," +
"COUNT(1) as session_count" +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_START + ">?" +
AND + SessionsTable.SESSION_END + "<?" +
AND + SessionsTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')" +
GROUP_BY + SessionsTable.USER_UUID;
String sql = SELECT +
"SUM(playtime) as total_playtime," +
"AVG(playtime) as average_playtime," +
"SUM(active_playtime) as total_active_playtime," +
"AVG(active_playtime) as average_active_playtime," +
"SUM(playtime-active_playtime) as total_afk_playtime," +
"AVG(playtime-active_playtime) as average_afk_playtime," +
"AVG(playtime) as average_playtime," +
"SUM(session_count) as total_sessions," +
"AVG(session_count) as average_sessions" +
FROM + "(" + selectAggregates + ") s";
return new QueryStatement<Map<String, Long>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, after);
statement.setLong(2, before);
}
@Override
public Map<String, Long> processResults(ResultSet set) throws SQLException {
if (set.next()) {
long sessionCount = set.getLong("total_sessions");
long playtime = set.getLong("total_playtime");
return Maps.builder(String.class, Long.class)
.put("total_playtime", playtime)
.put("average_playtime", set.getLong("average_playtime"))
.put("total_afk_playtime", set.getLong("total_afk_playtime"))
.put("average_afk_playtime", set.getLong("average_afk_playtime"))
.put("total_active_playtime", set.getLong("total_active_playtime"))
.put("average_active_playtime", set.getLong("average_active_playtime"))
.put("total_sessions", sessionCount)
.put("average_sessions", set.getLong("average_sessions"))
.put("average_session_length", sessionCount != 0 ? playtime / sessionCount : -1L)
.build();
} else {
return Collections.emptyMap();
}
}
};
}
public static Query<Long> earliestSessionStart() {
String sql = SELECT + "MIN(" + SessionsTable.SESSION_START + ") as m" +
FROM + SessionsTable.TABLE_NAME;
return new QueryAllStatement<Long>(sql) {
@Override
public Long processResults(ResultSet set) throws SQLException {
return set.next() ? set.getLong("m") : -1L;
}
};
}
}

View File

@ -47,32 +47,32 @@ public class TPSQueries {
public static Query<List<TPS>> fetchTPSDataOfServer(UUID serverUUID) {
return db -> {
String selectLowestResolution = SELECT +
"MIN(t." + DATE + ") as " + DATE + ',' +
"MIN(t." + TPS + ") as " + TPS + ',' +
"MAX(t." + PLAYERS_ONLINE + ") as " + PLAYERS_ONLINE + ',' +
"MAX(t." + RAM_USAGE + ") as " + RAM_USAGE + ',' +
"MAX(t." + CPU_USAGE + ") as " + CPU_USAGE + ',' +
"MAX(t." + ENTITIES + ") as " + ENTITIES + ',' +
"MAX(t." + CHUNKS + ") as " + CHUNKS + ',' +
"MAX(t." + FREE_DISK + ") as " + FREE_DISK +
min("t." + DATE) + " as " + DATE + ',' +
min("t." + TPS) + " as " + TPS + ',' +
max("t." + PLAYERS_ONLINE) + " as " + PLAYERS_ONLINE + ',' +
max("t." + RAM_USAGE) + " as " + RAM_USAGE + ',' +
max("t." + CPU_USAGE) + " as " + CPU_USAGE + ',' +
max("t." + ENTITIES) + " as " + ENTITIES + ',' +
max("t." + CHUNKS) + " as " + CHUNKS + ',' +
max("t." + FREE_DISK) + " as " + FREE_DISK +
FROM + TABLE_NAME + " t" +
WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID +
AND + DATE + "<?" +
GROUP_BY + "FLOOR(" + DATE + "/?)";
GROUP_BY + floor(DATE + "/?");
String selectLowerResolution = SELECT +
"MIN(t." + DATE + ") as " + DATE + ',' +
"MIN(t." + TPS + ") as " + TPS + ',' +
"MAX(t." + PLAYERS_ONLINE + ") as " + PLAYERS_ONLINE + ',' +
"MAX(t." + RAM_USAGE + ") as " + RAM_USAGE + ',' +
"MAX(t." + CPU_USAGE + ") as " + CPU_USAGE + ',' +
"MAX(t." + ENTITIES + ") as " + ENTITIES + ',' +
"MAX(t." + CHUNKS + ") as " + CHUNKS + ',' +
"MAX(t." + FREE_DISK + ") as " + FREE_DISK +
min("t." + DATE) + " as " + DATE + ',' +
min("t." + TPS) + " as " + TPS + ',' +
max("t." + PLAYERS_ONLINE) + " as " + PLAYERS_ONLINE + ',' +
max("t." + RAM_USAGE) + " as " + RAM_USAGE + ',' +
max("t." + CPU_USAGE) + " as " + CPU_USAGE + ',' +
max("t." + ENTITIES) + " as " + ENTITIES + ',' +
max("t." + CHUNKS) + " as " + CHUNKS + ',' +
max("t." + FREE_DISK) + " as " + FREE_DISK +
FROM + TABLE_NAME + " t" +
WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID +
AND + DATE + ">=?" +
AND + DATE + "<?" +
GROUP_BY + "FLOOR(" + DATE + "/?)";
GROUP_BY + floor(DATE + "/?");
String selectNormalResolution = SELECT +
DATE + ',' + TPS + ',' + PLAYERS_ONLINE + ',' +
RAM_USAGE + ',' + CPU_USAGE + ',' + ENTITIES + ',' + CHUNKS + ',' + FREE_DISK +
@ -154,6 +154,29 @@ public class TPSQueries {
};
}
public static Query<List<DateObj<Integer>>> fetchViewPreviewGraphData(UUID serverUUID) {
String sql = SELECT + min(DATE) + " as " + DATE + ',' +
max(PLAYERS_ONLINE) + " as " + PLAYERS_ONLINE +
FROM + TABLE_NAME +
WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID +
GROUP_BY + floor(DATE + "/?");
return new QueryStatement<List<DateObj<Integer>>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
statement.setLong(2, TimeUnit.MINUTES.toMillis(15));
}
@Override
public List<DateObj<Integer>> processResults(ResultSet set) throws SQLException {
List<DateObj<Integer>> ofServer = new ArrayList<>();
while (set.next()) ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE)));
return ofServer;
}
};
}
public static Query<List<DateObj<Integer>>> fetchPlayersOnlineOfServer(long after, long before, UUID serverUUID) {
String sql = SELECT + ServerTable.SERVER_UUID + ',' + DATE + ',' + PLAYERS_ONLINE +
FROM + TABLE_NAME +

View File

@ -183,4 +183,40 @@ public class UserInfoQueries {
}
};
}
public static Query<Set<UUID>> uuidsOfOperators() {
return getUUIDsForBooleanGroup(UserInfoTable.OP, true);
}
public static Query<Set<UUID>> getUUIDsForBooleanGroup(String column, boolean value) {
String sql = SELECT + UserInfoTable.USER_UUID + FROM + UserInfoTable.TABLE_NAME +
WHERE + column + "=?";
return new QueryStatement<Set<UUID>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, value);
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> uuids = new HashSet<>();
while (set.next()) {
uuids.add(UUID.fromString(set.getString(UserInfoTable.USER_UUID)));
}
return uuids;
}
};
}
public static Query<Set<UUID>> uuidsOfNonOperators() {
return getUUIDsForBooleanGroup(UserInfoTable.OP, false);
}
public static Query<Set<UUID>> uuidsOfBanned() {
return getUUIDsForBooleanGroup(UserInfoTable.BANNED, true);
}
public static Query<Set<UUID>> uuidsOfNotBanned() {
return getUUIDsForBooleanGroup(UserInfoTable.BANNED, false);
}
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.objects;
package com.djrapitops.plan.storage.database.queries.objects.playertable;
import com.djrapitops.plan.delivery.domain.TablePlayer;
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
@ -75,7 +75,7 @@ public class NetworkTablePlayersQuery implements Query<List<TablePlayer>> {
String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' +
"MAX(" + SessionsTable.SESSION_END + ") as last_seen," +
"COUNT(1) as count," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + ") as active_playtime" +
FROM + SessionsTable.TABLE_NAME + " s" +
GROUP_BY + "s." + SessionsTable.USER_UUID;
@ -91,7 +91,7 @@ public class NetworkTablePlayersQuery implements Query<List<TablePlayer>> {
"geoloc." + GeoInfoTable.GEOLOCATION + ',' +
"ses.last_seen," +
"ses.count," +
"ses.playtime," +
"ses.active_playtime," +
"act.activity_index" +
FROM + UsersTable.TABLE_NAME + " u" +
LEFT_JOIN + '(' + selectBanned + ") ban on ban." + UserInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID +
@ -119,7 +119,7 @@ public class NetworkTablePlayersQuery implements Query<List<TablePlayer>> {
.registered(set.getLong(UsersTable.REGISTERED))
.lastSeen(set.getLong("last_seen"))
.sessionCount(set.getInt("count"))
.playtime(set.getLong("playtime"))
.activePlaytime(set.getLong("active_playtime"))
.activityIndex(new ActivityIndex(set.getDouble("activity_index"), date));
if (set.getString("banned") != null) {
player.banned();

View File

@ -0,0 +1,148 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.objects.playertable;
import com.djrapitops.plan.delivery.domain.TablePlayer;
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
import com.djrapitops.plan.storage.database.SQLDB;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries;
import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable;
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Query for displaying players on /query page players table.
*
* @author Rsl1122
*/
public class QueryTablePlayersQuery implements Query<List<TablePlayer>> {
private final Collection<UUID> playerUUIDs;
private final long afterDate;
private final long beforeDate;
private final long activeMsThreshold;
/**
* Create a new query.
*
* @param playerUUIDs UUIDs of the players in the query
* @param beforeDate Date used for Activity Index calculation
* @param activeMsThreshold Playtime threshold for Activity Index calculation
*/
public QueryTablePlayersQuery(Collection<UUID> playerUUIDs, long afterDate, long beforeDate, long activeMsThreshold) {
this.playerUUIDs = playerUUIDs;
this.afterDate = afterDate;
this.beforeDate = beforeDate;
this.activeMsThreshold = activeMsThreshold;
}
@Override
public List<TablePlayer> executeQuery(SQLDB db) {
String uuidsInSet = " IN ('" + new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')";
String selectGeolocations = SELECT + DISTINCT +
GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED +
FROM + GeoInfoTable.TABLE_NAME;
String selectLatestGeolocationDate = SELECT +
GeoInfoTable.USER_UUID + ", " +
"MAX(" + GeoInfoTable.LAST_USED + ") as last_used_g" +
FROM + GeoInfoTable.TABLE_NAME +
GROUP_BY + GeoInfoTable.USER_UUID;
String selectLatestGeolocations = SELECT +
"g1." + GeoInfoTable.GEOLOCATION + ',' +
"g1." + GeoInfoTable.USER_UUID +
FROM + "(" + selectGeolocations + ") AS g1" +
INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS g2 ON g1.uuid = g2.uuid" +
WHERE + GeoInfoTable.LAST_USED + "=last_used_g";
String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' +
"MAX(" + SessionsTable.SESSION_END + ") as last_seen," +
"COUNT(1) as count," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + ") as active_playtime" +
FROM + SessionsTable.TABLE_NAME + " s" +
WHERE + "s." + SessionsTable.SESSION_START + ">=?" +
AND + "s." + SessionsTable.SESSION_END + "<=?" +
AND + "s." + SessionsTable.USER_UUID +
uuidsInSet +
GROUP_BY + "s." + SessionsTable.USER_UUID;
String selectBaseUsers = SELECT +
"u." + UsersTable.USER_UUID + ',' +
"u." + UsersTable.USER_NAME + ',' +
"u." + UsersTable.REGISTERED + ',' +
UserInfoTable.BANNED + ',' +
"geoloc." + GeoInfoTable.GEOLOCATION + ',' +
"ses.last_seen," +
"ses.count," +
"ses.active_playtime," +
"act.activity_index" +
FROM + UsersTable.TABLE_NAME + " u" +
INNER_JOIN + UserInfoTable.TABLE_NAME + " on u." + UsersTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_UUID +
LEFT_JOIN + '(' + selectLatestGeolocations + ") geoloc on geoloc." + GeoInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID +
LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID +
LEFT_JOIN + '(' + NetworkActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + SessionsTable.USER_UUID + "=act." + UserInfoTable.USER_UUID +
WHERE + "u." + UserInfoTable.USER_UUID +
uuidsInSet +
ORDER_BY + "ses.last_seen DESC";
return db.query(new QueryStatement<List<TablePlayer>>(selectBaseUsers, 1000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, afterDate);
statement.setLong(2, beforeDate);
NetworkActivityIndexQueries.setSelectActivityIndexSQLParameters(statement, 3, activeMsThreshold, beforeDate);
}
@Override
public List<TablePlayer> processResults(ResultSet set) throws SQLException {
List<TablePlayer> players = new ArrayList<>();
while (set.next()) {
TablePlayer.Builder player = TablePlayer.builder()
.uuid(UUID.fromString(set.getString(UsersTable.USER_UUID)))
.name(set.getString(UsersTable.USER_NAME))
.geolocation(set.getString(GeoInfoTable.GEOLOCATION))
.registered(set.getLong(UsersTable.REGISTERED))
.lastSeen(set.getLong("last_seen"))
.sessionCount(set.getInt("count"))
.activePlaytime(set.getLong("active_playtime"))
.activityIndex(new ActivityIndex(set.getDouble("activity_index"), beforeDate));
if (set.getBoolean(UserInfoTable.BANNED)) {
player.banned();
}
players.add(player.build());
}
return players;
}
});
}
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.objects;
package com.djrapitops.plan.storage.database.queries.objects.playertable;
import com.djrapitops.plan.delivery.domain.TablePlayer;
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
@ -85,7 +85,7 @@ public class ServerTablePlayersQuery implements Query<List<TablePlayer>> {
String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' +
"MAX(" + SessionsTable.SESSION_END + ") as last_seen," +
"COUNT(1) as count," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + ") as active_playtime" +
FROM + SessionsTable.TABLE_NAME + " s" +
WHERE + "s." + SessionsTable.SERVER_UUID + "=?" +
GROUP_BY + "s." + SessionsTable.USER_UUID;
@ -98,7 +98,7 @@ public class ServerTablePlayersQuery implements Query<List<TablePlayer>> {
"geoloc." + GeoInfoTable.GEOLOCATION + ',' +
"ses.last_seen," +
"ses.count," +
"ses.playtime," +
"ses.active_playtime," +
"act.activity_index" +
FROM + UsersTable.TABLE_NAME + " u" +
INNER_JOIN + UserInfoTable.TABLE_NAME + " on u." + UsersTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_UUID +
@ -128,7 +128,7 @@ public class ServerTablePlayersQuery implements Query<List<TablePlayer>> {
.registered(set.getLong(UsersTable.REGISTERED))
.lastSeen(set.getLong("last_seen"))
.sessionCount(set.getInt("count"))
.playtime(set.getLong("playtime"))
.activePlaytime(set.getLong("active_playtime"))
.activityIndex(new ActivityIndex(set.getDouble("activity_index"), date));
if (set.getBoolean(UserInfoTable.BANNED)) {
player.banned();

View File

@ -49,10 +49,21 @@ public abstract class Sql {
public static final String IS_NULL = " IS NULL";
public static final String IS_NOT_NULL = " IS NOT NULL";
private static final String FLOOR = "FLOOR(";
private static final String MIN = "MIN(";
private static final String MAX = "MAX(";
private static final String VARCHAR = "varchar(";
public static String varchar(int length) {
return "varchar(" + length + ')';
return VARCHAR + length + ')';
}
public static String floor(String expression) {return FLOOR + expression + ')';}
public static String min(String expression) {return MIN + expression + ')';}
public static String max(String expression) {return MAX + expression + ')';}
/**
* Turn day of week to epoch ms.
* <p>

View File

@ -149,4 +149,8 @@ public class PlanFiles implements SubSystem {
}
return Optional.empty();
}
public Path getJSONStorageDirectory() {
return getDataDirectory().resolve("cached_json");
}
}

View File

@ -0,0 +1,137 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.json;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plugin.logging.console.PluginLogger;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* In charge of storing json files on disk for later retrieval.
*
* @author Rsl1122
*/
@Singleton
public class JSONFileStorage implements JSONStorage {
private final PluginLogger logger;
private final Path jsonDirectory;
private final Pattern timestampRegex = Pattern.compile(".*-([0-9]*).json");
private static final String JSON_FILE_EXTENSION = ".json";
@Inject
public JSONFileStorage(PlanFiles files, PluginLogger logger) {
this.logger = logger;
jsonDirectory = files.getJSONStorageDirectory();
}
@Override
public StoredJSON storeJson(String identifier, String json, long timestamp) {
Path writingTo = jsonDirectory.resolve(identifier + '-' + timestamp + JSON_FILE_EXTENSION);
try {
Files.createDirectories(jsonDirectory);
Files.write(writingTo, json.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
} catch (IOException e) {
logger.warn("Could not write a file to " + writingTo.toFile().getAbsolutePath() + ": " + e.getMessage());
}
return new StoredJSON(json, timestamp);
}
@Override
public Optional<StoredJSON> fetchJSON(String identifier) {
File[] stored = jsonDirectory.toFile().listFiles();
if (stored == null) return Optional.empty();
for (File file : stored) {
String fileName = file.getName();
if (fileName.endsWith(JSON_FILE_EXTENSION) && fileName.startsWith(identifier)) {
return Optional.ofNullable(readStoredJSON(file));
}
}
return Optional.empty();
}
private StoredJSON readStoredJSON(File from) {
Matcher timestampMatch = timestampRegex.matcher(from.getName());
if (timestampMatch.find()) {
try (Stream<String> lines = Files.lines(from.toPath())) {
long timestamp = Long.parseLong(timestampMatch.group(1));
StringBuilder json = new StringBuilder();
lines.forEach(json::append);
return new StoredJSON(json.toString(), timestamp);
} catch (IOException e) {
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " file '" + from.getName() + "' could not be read: " + e.getMessage());
} catch (NumberFormatException e) {
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' with improperly formatted -timestamp (could not parse number). This file was not placed there by Plan!");
}
} else {
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' that has no -timestamp. This file was not placed there by Plan!");
}
return null;
}
@Override
public Optional<StoredJSON> fetchExactJson(String identifier, long timestamp) {
File found = jsonDirectory.resolve(identifier + "-" + timestamp + JSON_FILE_EXTENSION).toFile();
if (!found.exists()) return Optional.empty();
return Optional.ofNullable(readStoredJSON(found));
}
@Override
public Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp) {
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) < time);
}
@Override
public Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp) {
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) > time);
}
private Optional<StoredJSON> fetchJSONWithTimestamp(String identifier, long timestamp, BiPredicate<Matcher, Long> timestampComparator) {
File[] stored = jsonDirectory.toFile().listFiles();
if (stored == null) return Optional.empty();
for (File file : stored) {
try {
String fileName = file.getName();
if (fileName.endsWith(JSON_FILE_EXTENSION) && fileName.startsWith(identifier)) {
Matcher timestampMatch = timestampRegex.matcher(fileName);
if (timestampMatch.find() && timestampComparator.test(timestampMatch, timestamp)) {
return Optional.ofNullable(readStoredJSON(file));
}
}
} catch (NumberFormatException e) {
// Ignore this file, malformed timestamp
}
}
return Optional.empty();
}
}

View File

@ -0,0 +1,75 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.json;
import com.google.gson.Gson;
import java.util.Objects;
import java.util.Optional;
/**
* In charge of storing json somewhere for later retrieval.
*
* @author Rsl1122
*/
public interface JSONStorage {
default StoredJSON storeJson(String identifier, String json) {
return storeJson(identifier, json, System.currentTimeMillis());
}
default StoredJSON storeJson(String identifier, Object json) {
return storeJson(identifier, new Gson().toJson(json));
}
StoredJSON storeJson(String identifier, String json, long timestamp);
default StoredJSON storeJson(String identifier, Object json, long timestamp) {
return storeJson(identifier, new Gson().toJson(json), timestamp);
}
Optional<StoredJSON> fetchJSON(String identifier);
Optional<StoredJSON> fetchExactJson(String identifier, long timestamp);
Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp);
Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp);
final class StoredJSON {
public final String json;
public final long timestamp;
public StoredJSON(String json, long timestamp) {
this.json = json;
this.timestamp = timestamp;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StoredJSON that = (StoredJSON) o;
return timestamp == that.timestamp && Objects.equals(json, that.json);
}
@Override
public int hashCode() {
return Objects.hash(json, timestamp);
}
}
}

View File

@ -52,6 +52,8 @@ public class VersionChecker implements SubSystem {
private final RunnableFactory runnableFactory;
private final ErrorLogger errorLogger;
private static final String DOWNLOAD_ICON_HTML = "<i class=\"fa fa-fw fa-download\"></i> ";
private VersionInfo newVersionAvailable;
@Inject
@ -131,7 +133,7 @@ public class VersionChecker implements SubSystem {
"font-size: 0.95rem;" : "";
return "<button class=\"btn bg-white col-plan\" style=\"" + reduceFontSize +
"\" data-target=\"#updateModal\" data-toggle=\"modal\" type=\"button\">" +
"<i class=\"fa fa-fw fa-download\"></i> Update: " + v.getVersion().getVersionString() +
DOWNLOAD_ICON_HTML + locale.getString(PluginLang.VERSION_UPDATE) + ": " + v.getVersion().getVersionString() +
"</button>";
}
);
@ -147,24 +149,24 @@ public class VersionChecker implements SubSystem {
return getNewVersionAvailable()
.map(v -> "<div class=\"modal-header\">" +
"<h5 class=\"modal-title\" id=\"updateModalLabel\">" +
"<i class=\"fa fa-fw fa-download\"></i> Version " + v.getVersion().getVersionString() + " is Available!" +
DOWNLOAD_ICON_HTML + locale.getString(PluginLang.VERSION_UPDATE_AVAILABLE, v.getVersion().getVersionString()) +
"</h5><button aria-label=\"Close\" class=\"close\" data-dismiss=\"modal\" type=\"button\"><span aria-hidden=\"true\">&times;</span></button>" +
"</div>" + // Close modal-header
"<div class=\"modal-body\">" +
"<p>A new version has been released and is now available for download." +
(v.isRelease() ? "" : "<br>This version is a DEV release.") + "</p>" +
"<p>" + locale.getString(PluginLang.VERSION_CURRENT, getCurrentVersion()) + ". " + locale.getString(PluginLang.VERSION_UPDATE_INFO) +
(v.isRelease() ? "" : "<br>" + locale.getString(PluginLang.VERSION_UPDATE_DEV)) + "</p>" +
"<a class=\"btn col-plan\" href=\"" + v.getChangeLogUrl() + "\" rel=\"noopener noreferrer\" target=\"_blank\">" +
"<i class=\"fa fa-fw fa-list\"></i> View Changelog</a>" +
"<i class=\"fa fa-fw fa-list\"></i> " + locale.getString(PluginLang.VERSION_CHANGE_LOG) + "</a>" +
"<a class=\"btn col-plan\" href=\"" + v.getDownloadUrl() + "\" rel=\"noopener noreferrer\" target=\"_blank\">" +
"<i class=\"fa fa-fw fa-download\"></i> Download Plan-" + v.getVersion().getVersionString() + ".jar</a>" +
DOWNLOAD_ICON_HTML + locale.getString(PluginLang.VERSION_DOWNLOAD, v.getVersion().getVersionString()) + "</a>" +
"</div>") // Close modal-body
.orElse("<div class=\"modal-header\">" +
"<h5 class=\"modal-title\" id=\"updateModalLabel\">" +
"<i class=\"far fa-fw fa-check-circle\"></i> You have version " + getCurrentVersion() + "" +
"<i class=\"far fa-fw fa-check-circle\"></i> " + locale.getString(PluginLang.VERSION_CURRENT, getCurrentVersion()) +
"</h5><button aria-label=\"Close\" class=\"close\" data-dismiss=\"modal\" type=\"button\"><span aria-hidden=\"true\">&times;</span></button>" +
"</div>" + // Close modal-header
"<div class=\"modal-body\">" +
"<p>You are running the latest version.</p>" +
"<p>" + locale.getString(PluginLang.VERSION_NEWEST) + "</p>" +
"</div>"); // Close modal-body
}

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || 数据库补丁失败,插
Enable FAIL - GeoDB Write || 保存已下载的 GeoLite2 地理位置数据库时发生问题
Enable FAIL - WebServer (Proxy) || 网页服务器未初始化
Enable FAIL - Wrong Database Type || ${0} 此数据类型不存在。
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || 比较15天前
HTML - COMPARING_60_DAYS || 比较30天前
HTML - COMPARING_7_DAYS || 比较7天前
@ -167,14 +169,18 @@ HTML - KILLED || 被击杀
HTML - LABEL_1ST_WEAPON || 最致命的PVP武器
HTML - LABEL_2ND_WEAPON || 更致命的武器
HTML - LABEL_3RD_WEAPON || 致命的PvP武器
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || 活跃指数
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || 挂机时间
HTML - LABEL_AVG || 平均
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || 平均击杀比
HTML - LABEL_AVG_MOB_KDR || 平均怪物击杀比
HTML - LABEL_AVG_PLAYTIME || 平均游戏时间
HTML - LABEL_AVG_SESSION_LENGTH || 平均时域长度
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || 平均TPS
HTML - LABEL_BANNED || 已封禁
HTML - LABEL_BEST_PEAK || 所有时间峰值
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || 快速检索
HTML - LINK_SERVER_ANALYSIS || 服务器分析
HTML - LINK_WIKI || Plan Wiki,教程 & 文档
HTML - LOCAL_MACHINE || 本机
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || 插件
HTML - NEW_CALENDAR || 新:
HTML - NO_KILLS || 无击杀数
HTML - NO_USER_PRESENT || 用户Cookie未提供
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || 离线
HTML - ONLINE || 在线
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / 天
HTML - PLAYERS_TEXT || 玩家
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || 时域
HTML - SIDE_GEOLOCATIONS || 地理位置
HTML - SIDE_INFORMATION || 信息
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || 全服网络预览
HTML - SIDE_OVERVIEW || 预览
HTML - SIDE_PERFORMANCE || 性能
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || 没有可显示的在线活
HTML - TEXT_NO_SERVERS || 数据库中找不到服务器
HTML - TEXT_PLUGIN_INFORMATION || 插件信息
HTML - TEXT_PREDICTED_RETENTION || 这个数值根据之前的玩家预测得出
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || 有新版本的插件可用.
HTML - TITLE_30_DAYS || 30 天
HTML - TITLE_30_DAYS_AGO || 30 天前
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || 当前玩家
HTML - TITLE_DISK || 磁盘空间
HTML - TITLE_GRAPH_DAY_BY_DAY || 每日
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || 全服网络在线活动
HTML - TITLE_GRAPH_PUNCHCARD || 30天打卡签到
HTML - TITLE_INSIGHTS || 30天Insights
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || 没有数据
HTML - UNIT_THE_PLAYERS || 玩家
HTML - USER_AND_PASS_NOT_SPECIFIED || 未指定用户名与密码
HTML - USER_DOES_NOT_EXIST || 用户不存在
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || 用户名与密码不匹配
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th> 与
HTML ERRORS - ACCESS_DENIED_403 || 拒绝访问
HTML ERRORS - AUTH_FAIL_TIPS_401 || - 确保您已使用 <b>/plan register</b> 注册用户<br>- 检查用户名与密码是否正确<br>- 用户名与密码区分大小写<br><br>若您忘记了密码,请让工作人员删除您的旧密码并重新注册。
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || 网页服务器: 用户验证
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || 网页服务器:未找到证书密钥存储文件:${0}
WebServer - Notify Using Proxy || 网页服务器: 代理模式HTTPS已开启, 请确保您的反代已经配置为HTTPS模式并且Plan的Alternative_IP.Link已经指向代理
WebServer - Notify Using Proxy || 网页服务器: 代理模式HTTPS已开启, 请确保您的反代已经配置为HTTPS模式并且Plan的Alternative_IP.Address已经指向代理
WebServer FAIL - EOF || 网页服务器: 在读取证书文件时出现了EOF异常. (请检查证书文件完整性)
WebServer FAIL - Port Bind || 未成功初始化网页服务器。端口(${0})是否被占用?
WebServer FAIL - SSL Context || 网页服务器SSL 环境初始化失败。

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || Patch databáze selhal, plugi
Enable FAIL - GeoDB Write || Něco se pokazilo při ukládání stažené GeoLite2 Geolocation databáze
Enable FAIL - WebServer (Proxy) || WebServer se nespustil!
Enable FAIL - Wrong Database Type || ${0} není podporovaná databáze
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Srovnání 15 dnů
HTML - COMPARING_60_DAYS || Srovnání 30d zpět do Dnes
HTML - COMPARING_7_DAYS || Srovnání 7 dnů
@ -167,14 +169,18 @@ HTML - KILLED || Zabit
HTML - LABEL_1ST_WEAPON || Nejsmrtelnější PvP zbraň
HTML - LABEL_2ND_WEAPON || 2. PvP Zbraň
HTML - LABEL_3RD_WEAPON || 3. PvP Zbraň
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Index aktivity
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK čas
HTML - LABEL_AVG || Průměr
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Průměr KDR
HTML - LABEL_AVG_MOB_KDR || Průměr Mob KDR
HTML - LABEL_AVG_PLAYTIME || Průměr herní doby
HTML - LABEL_AVG_SESSION_LENGTH || Průměr délky sezení
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Průměr TPS
HTML - LABEL_BANNED || Zabanován
HTML - LABEL_BEST_PEAK || Celková špička
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Rychlé zobrazení
HTML - LINK_SERVER_ANALYSIS || Analýza serveru
HTML - LINK_WIKI || Plan Wiki, Tutoriály & Dokumentace
HTML - LOCAL_MACHINE || Lokální stroj
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Pluginy
HTML - NEW_CALENDAR || Nové:
HTML - NO_KILLS || Žádné zabití
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Offline
HTML - ONLINE || Online
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Den
HTML - PLAYERS_TEXT || Hráči
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Sezení
HTML - SIDE_GEOLOCATIONS || Geolokace
HTML - SIDE_INFORMATION || INFORMACE
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Přehled sítě
HTML - SIDE_OVERVIEW || Přehled
HTML - SIDE_PERFORMANCE || Výkon
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Žádný server pro který lz
HTML - TEXT_NO_SERVERS || Žádné servery nenalezeny v databázi
HTML - TEXT_PLUGIN_INFORMATION || Informace o pluginu
HTML - TEXT_PREDICTED_RETENTION || Tato hodnota je odhad založený na předchozích hráčích
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Byla vydána nová verze a je dostupná ke stažení.
HTML - TITLE_30_DAYS || 30 dní
HTML - TITLE_30_DAYS_AGO || před 30 dny
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Aktuální základna hráčů
HTML - TITLE_DISK || Místo na disku
HTML - TITLE_GRAPH_DAY_BY_DAY || Den po dni
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Síťová online aktivita
HTML - TITLE_GRAPH_PUNCHCARD || Štítek pro 30 dní
HTML - TITLE_INSIGHTS || Postřehy za 30 dní
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Žádná data
HTML - UNIT_THE_PLAYERS || Hráči
HTML - USER_AND_PASS_NOT_SPECIFIED || Uživatel a heslo nespecifikováno
HTML - USER_DOES_NOT_EXIST || Uživatel neexistuje
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || Uživatel a heslo nesouhlasí
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>S
HTML ERRORS - ACCESS_DENIED_403 || Přístup zamítnut
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ujistěte se, že jste zaregistrovaní s uživatelem s <b>/plan register</b><br>- Zkontrolujte zda je jméno a heslo správné<br>- Jméno a heslo jsou citlivá na velká/malá písmena<br><br>Pokud jste zapomněli heslo, zeptejte se člena týmu ke smazání vašeho starého uživatele či nové registraci.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: Autorizace uživat
Webserver - Notify IP Whitelist || Webserver: IP Whitelist je zapnutý.
Webserver - Notify IP Whitelist Block || Webserver: ${0} byl odmítnut přístup na '${1}'. (není na whitelistu)
WebServer - Notify no Cert file || WebServer: Certifikační KeyStore soubor nenalezen: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS zapnut, ujistěte se, že reverse-proxy je funkčí s HTTPS a Plan Alternative_IP.Link míří na proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS zapnut, ujistěte se, že reverse-proxy je funkčí s HTTPS a Plan Alternative_IP.Address míří na proxy
WebServer FAIL - EOF || WebServer: EOF při čtení souboru Certifikátu. (Zkontrolujte, zda soubor není prázdný)
WebServer FAIL - Port Bind || WebServer nebyl úspěšně spuštěn. Je port (${0}) již používán?
WebServer FAIL - SSL Context || WebServer: SSL Context spuštění selhalo.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || Datenbank-Patch ist fehlgesch
Enable FAIL - GeoDB Write || Etwas ist beim Speichern der GeoLite2 Geolocation Datenbank fehlgeschlagen
Enable FAIL - WebServer (Proxy) || Webserver ist nicht geladen!
Enable FAIL - Wrong Database Type || ${0} ist keine gültige Datenbank
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Vergleiche 15 Tage
HTML - COMPARING_60_DAYS || Vergleiche 30 Tage bis Jetzt
HTML - COMPARING_7_DAYS || Vergleiche 7 Tage
@ -167,14 +169,18 @@ HTML - KILLED || Getötet
HTML - LABEL_1ST_WEAPON || Tödlichste PvP Waffe
HTML - LABEL_2ND_WEAPON || 2. PvP Waffe
HTML - LABEL_3RD_WEAPON || 3. PvP Waffe
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Aktivitätsindex
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK Zeit
HTML - LABEL_AVG || Durchschnitt
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Durschnittliche KDR
HTML - LABEL_AVG_MOB_KDR || Durschnittliche Mob KDR
HTML - LABEL_AVG_PLAYTIME || Durschnittliche Spielzeit
HTML - LABEL_AVG_SESSION_LENGTH || Durschnittliche Sitzungslänge
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Durschnittliche TPS
HTML - LABEL_BANNED || Gebannt
HTML - LABEL_BEST_PEAK || Rekord
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Schnellansicht
HTML - LINK_SERVER_ANALYSIS || Server Analyse
HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation
HTML - LOCAL_MACHINE || Lokale Maschine
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || Neu:
HTML - NO_KILLS || Keine Kills
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Offline
HTML - ONLINE || Online
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Tag
HTML - PLAYERS_TEXT || Spieler
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Sitzung
HTML - SIDE_GEOLOCATIONS || Geolocations
HTML - SIDE_INFORMATION || INFORMATION
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Netzwerk Übersicht
HTML - SIDE_OVERVIEW || Übersicht
HTML - SIDE_PERFORMANCE || Leistung
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Keine Server gefunden, um die
HTML - TEXT_NO_SERVERS || Keine Server in der Datenbank gefunden
HTML - TEXT_PLUGIN_INFORMATION || Informationen über das Plugin
HTML - TEXT_PREDICTED_RETENTION || Dieser Wert ist eine Vorraussage, der sich auf, der auf den Spielern basiert
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Eine neue Version steht zum Download bereit
HTML - TITLE_30_DAYS || 30 Tage
HTML - TITLE_30_DAYS_AGO || 30 Tage vorher
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Aktuelle Spielerbasis
HTML - TITLE_DISK || Festplattenspeicher
HTML - TITLE_GRAPH_DAY_BY_DAY || Tag für Tag
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Netzwerk Online Aktivität
HTML - TITLE_GRAPH_PUNCHCARD || Lochkarte für 30 Tage
HTML - TITLE_INSIGHTS || Insights for 30 days
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Keine Daten
HTML - UNIT_THE_PLAYERS || Spieler
HTML - USER_AND_PASS_NOT_SPECIFIED || User und Passwort nicht spezifiziert
HTML - USER_DOES_NOT_EXIST || User existiert nicht
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || User und Password stimmen nicht überein
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Breite
HTML ERRORS - ACCESS_DENIED_403 || Zugriff verweigert
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Stelle sicher, dass du einen Account mit <b>/plan register</b> hinzugefügt hast.<br>- Überprüfe, ob Passwort und Benutzername korrekt sind<br>- Bei Benutzername und Passwort auf Groß- und Kleinschreibung achten! <br><br>- Wenn du dein Passwort vergessen hast, bitte ein Teammitglied deinen Account zu löschen und neu zu erstellen.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: User Authorization
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || WebServer: Zertifikat KeyStore Datei nicht gefunden: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || WebServer wurde nicht erfolgreich initalisiert. Ist der Port (${0}) schon benutzt?
WebServer FAIL - SSL Context || WebServer: SSL Context Initialisierung fehlgeschlagen.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || Database Patching failed, plu
Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database
Enable FAIL - WebServer (Proxy) || WebServer did not initialize!
Enable FAIL - Wrong Database Type || ${0} is not a supported Database
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparing 15 days
HTML - COMPARING_60_DAYS || Comparing 30d ago to Now
HTML - COMPARING_7_DAYS || Comparing 7 days
@ -167,14 +169,18 @@ HTML - KILLED || Killed
HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon
HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon
HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Activity Index
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK Time
HTML - LABEL_AVG || Average
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Average KDR
HTML - LABEL_AVG_MOB_KDR || Average Mob KDR
HTML - LABEL_AVG_PLAYTIME || Average Playtime
HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Average TPS
HTML - LABEL_BANNED || Banned
HTML - LABEL_BEST_PEAK || All Time Peak
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Quick view
HTML - LINK_SERVER_ANALYSIS || Server Analysis
HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation
HTML - LOCAL_MACHINE || Local Machine
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || New:
HTML - NO_KILLS || No Kills
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Offline
HTML - ONLINE || Online
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Day
HTML - PLAYERS_TEXT || Players
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Session
HTML - SIDE_GEOLOCATIONS || Geolocations
HTML - SIDE_INFORMATION || INFORMATION
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Network Overview
HTML - SIDE_OVERVIEW || Overview
HTML - SIDE_PERFORMANCE || Performance
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || No server to display online a
HTML - TEXT_NO_SERVERS || No servers found in the database
HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin
HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || A new version has been released and is now available for download.
HTML - TITLE_30_DAYS || 30 days
HTML - TITLE_30_DAYS_AGO || 30 days ago
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Current Playerbase
HTML - TITLE_DISK || Disk space
HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity
HTML - TITLE_GRAPH_PUNCHCARD || Punchcard for 30 days
HTML - TITLE_INSIGHTS || Insights for 30 days
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || No Data
HTML - UNIT_THE_PLAYERS || Players
HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified
HTML - USER_DOES_NOT_EXIST || User does not exist
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || User and Password did not match
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>With
HTML ERRORS - ACCESS_DENIED_403 || Access Denied
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with <b>/plan register</b><br>- Check that the username and password are correct<br>- Username and password are case-sensitive<br><br>If you have forgotten your password, ask a staff member to delete your old user and re-register.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: User Authorization
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use?
WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || El parcheado de la base de da
Enable FAIL - GeoDB Write || Ha ocurrido un error al intentar descargar la base de datos GeoLite2 Geolocation.
Enable FAIL - WebServer (Proxy) || ¡El servidor web no se ha iniciado!
Enable FAIL - Wrong Database Type || ${0} no es una base de datos sostenible.
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparando 15 dias
HTML - COMPARING_60_DAYS || Comparando desde hace 30d hasta ahora
HTML - COMPARING_7_DAYS || Comparando 7 dias
@ -167,14 +169,18 @@ HTML - KILLED || Muerto
HTML - LABEL_1ST_WEAPON || Arma PvP más mortal
HTML - LABEL_2ND_WEAPON || 2ª arma PvP
HTML - LABEL_3RD_WEAPON || 3ª arma PvP
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Índice de actividad
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || Tiempo AFK
HTML - LABEL_AVG || Promedio
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || KDR promedio
HTML - LABEL_AVG_MOB_KDR || KDR de mobs promedio
HTML - LABEL_AVG_PLAYTIME || Jugabilidad promedio
HTML - LABEL_AVG_SESSION_LENGTH || Duración de sesion promedio
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || TPS promedio
HTML - LABEL_BANNED || Baneado
HTML - LABEL_BEST_PEAK || Mejor pico
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Vista rápida
HTML - LINK_SERVER_ANALYSIS || Análisis de servidor
HTML - LINK_WIKI || Plan Wiki, Tutoriales & Documentación
HTML - LOCAL_MACHINE || Máquina local
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || Nuevo:
HTML - NO_KILLS || Sin muertes
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Desconectado
HTML - ONLINE || Conectado
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Dia
HTML - PLAYERS_TEXT || Jugadores
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Sesión
HTML - SIDE_GEOLOCATIONS || Geolocalizaciones
HTML - SIDE_INFORMATION || INFORMACIÓN
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Vista general de red
HTML - SIDE_OVERVIEW || Vista general
HTML - SIDE_PERFORMANCE || Rendimiento
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Sin un servidor donde registr
HTML - TEXT_NO_SERVERS || Ningun servidor encontrado en la base de datos.
HTML - TEXT_PLUGIN_INFORMATION || Información sobre el plugin.
HTML - TEXT_PREDICTED_RETENTION || Este valor es una predicción que viene dado por jugadores anteriores.
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Una nueva versión se ha lanzado y esta lista para ser descargada.
HTML - TITLE_30_DAYS || 30 días
HTML - TITLE_30_DAYS_AGO || Hace 30 días
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU y RAM
HTML - TITLE_CURRENT_PLAYERBASE || base del jugador actual
HTML - TITLE_DISK || Espacio del disco
HTML - TITLE_GRAPH_DAY_BY_DAY || Día a día
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Actividad online de red
HTML - TITLE_GRAPH_PUNCHCARD || Tarjeta por 30 días
HTML - TITLE_INSIGHTS || Ideas por 30 días
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Sin datos
HTML - UNIT_THE_PLAYERS || Jugadores
HTML - USER_AND_PASS_NOT_SPECIFIED || Usuario y contraseña no especificados
HTML - USER_DOES_NOT_EXIST || El usuario no existe
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || El usuario y la contraseña no coincide
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Con
HTML ERRORS - ACCESS_DENIED_403 || Acceso denegado
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Asegura que has registrado un usuario con <b>/plan register</b><br>- Comprueba que el usuario y la contraseña son correctos.<br>- Se pueden destinguir las minúsculas y mayúsculas del nick y la contraseña.<br><br>Si has olvidado tu contraseña, pídele a un miembro staff que borre la cuenta y te vuelves a registrar.

View File

@ -1,81 +1,81 @@
API - css+ || PageExtension: ${0} added stylesheet(s) to ${1}, ${2}
API - js+ || PageExtension: ${0} added javascript(s) to ${1}, ${2}
API - css+ || PageExtension: ${0} lisäsi tyylejä sivulle ${1}, ${2}
API - js+ || PageExtension: ${0} lisäsi javascriptia sivulle ${1}, ${2}
Cmd - Click Me || Klikkaa minua
Cmd - Link || Linkki
Cmd - Link Network || Network page:
Cmd - Link Player || Player page:
Cmd - Link Player JSON || Player json:
Cmd - Link Players || Players page:
Cmd - Link Register || Register page:
Cmd - Link Server || Server page:
CMD Arg - backup-file || Name of the backup file (case sensitive)
CMD Arg - code || Code used to finalize registration.
CMD Arg - db type backup || Type of the database to backup. Current database is used if not specified.
CMD Arg - db type clear || Type of the database to remove all data from.
CMD Arg - db type hotswap || Type of the database to start using.
CMD Arg - db type move from || Type of the database to move data from.
CMD Arg - db type move to || Type of the database to move data to. Can not be same as previous.
CMD Arg - db type restore || Type of the database to restore to. Current database is used if not specified.
CMD Arg - feature || Name of the feature to disable: ${0}
CMD Arg - player identifier || Name or UUID of a player
CMD Arg - player identifier remove || Identifier for a player that will be removed from current database.
CMD Arg - server identifier || Name, ID or UUID of a server
CMD Arg - subcommand || Use the command without subcommand to see help.
CMD Arg - username || Username of another user. If not specified player linked user is used.
CMD Arg Name - backup-file || backup-file
CMD Arg Name - code || ${code}
CMD Arg Name - export kind || export kind
CMD Arg Name - feature || feature
CMD Arg Name - import kind || import kind
CMD Arg Name - name or uuid || name/uuid
CMD Arg Name - server || server
CMD Arg Name - subcommand || subcommand
CMD Arg Name - username || username
Cmd Confirm - accept || Accept
Cmd Confirm - cancelled, no data change || Cancelled. No data was changed.
Cmd Confirm - cancelled, unregister || Cancelled. '${0}' was not unregistered
Cmd Confirm - clearing db || You are about to remove all Plan-data in ${0}
Cmd Confirm - confirmation || Confirm:
Cmd Confirm - deny || Cancel
Cmd Confirm - Expired || Confirmation expired, use the command again
Cmd Confirm - Fail on accept || The accepted action errored upon execution: ${0}
Cmd Confirm - Fail on deny || The denied action errored upon execution: ${0}
Cmd Confirm - overwriting db || You are about to overwrite data in Plan ${0} with data in ${1}
Cmd Confirm - remove player db || You are about to remove data of ${0} from ${1}
Cmd Confirm - unregister || You are about to unregister '${0}' linked to ${1}
Cmd db - creating backup || Creating a backup file '${0}.db' with contents of ${1}
Cmd db - removal || Removing Plan-data from ${0}..
Cmd db - removal player || Removing data of ${0} from ${1}..
Cmd db - server uninstalled || §aIf the server is still installed, it will automatically set itself as installed in the database.
Cmd db - write || Writing to ${0}..
Cmd - Link Network || Verkoston sivu:
Cmd - Link Player || Pelaajan sivu:
Cmd - Link Player JSON || Pelaajan json:
Cmd - Link Players || Pelaajat sivu:
Cmd - Link Register || Rekisteröitymis sivu:
Cmd - Link Server || Palvelimen sivu:
CMD Arg - backup-file || Varmuuskopiotiedoston nimi (kirjainkooolla on väliä)
CMD Arg - code || Koodi jota käytetään rekisteröitymiseen.
CMD Arg - db type backup || Tietokannan tyyppi joka varmuuskopioidaan. Nykyinen jos ei annettu.
CMD Arg - db type clear || Tietokannan tyyppi josta kaikki tieto poistetaan.
CMD Arg - db type hotswap || Tietokannan tyyppi jota aletaan käyttää.
CMD Arg - db type move from || Tietokannan tyyppi josta tietoa poistetaan.
CMD Arg - db type move to || Tietokannan tyyppi johon tietoa siirretään. Ei voi olla sama kuin edellinen.
CMD Arg - db type restore || Tietokannan tyyppi johon tiedot palautetaan. Nykyiseen jos ei annettu.
CMD Arg - feature || Ominaisuuden nimi joka poistetaan käytöstä: ${0}
CMD Arg - player identifier || Pelaajan UUID tai nimi.
CMD Arg - player identifier remove || Poistettavan pelaajan UUID tai nimi.
CMD Arg - server identifier || Palvelimen nimi, ID tai UUID
CMD Arg - subcommand || Käytä komentoa ilman alikomentoa nähdäksesi käyttöohjeet.
CMD Arg - username || Toisen käyttäjän käyttäjänimi. Jos ei annettu, pelaajaan linkitettyä käyttäjää käytetään.
CMD Arg Name - backup-file || varmuuskopio-tiedosto
CMD Arg Name - code || ${koodi}
CMD Arg Name - export kind || viennin muoto
CMD Arg Name - feature || ominaisuus
CMD Arg Name - import kind || tuonnin muoto
CMD Arg Name - name or uuid || nimi/uuid
CMD Arg Name - server || palvelin
CMD Arg Name - subcommand || alikomento
CMD Arg Name - username || käyttäjänimi
Cmd Confirm - accept || Hyväksy
Cmd Confirm - cancelled, no data change || Peruutettu. Tietoja ei muutettu.
Cmd Confirm - cancelled, unregister || Peruutettu. '${0}' ei poistettu rekisteristä
Cmd Confirm - clearing db || Olet poistamassa kaikki Plan tiedot tietokannasta ${0}
Cmd Confirm - confirmation || Hyväksy:
Cmd Confirm - deny || Peruuta
Cmd Confirm - Expired || Vahvistus vanheni, käytä komentoa uudelleen
Cmd Confirm - Fail on accept || Hyväksytty toiminto antoi virheen: ${0}
Cmd Confirm - Fail on deny || Kielletty toiminto antoi virheen: ${0}
Cmd Confirm - overwriting db || Olet ylikirjoittamassa kaikki Plan tiedot tietokannassa ${0} tiedolla ${1} tietokannasta
Cmd Confirm - remove player db || Olet poistamassa pelaajan ${0} tiedot tietokannasta ${1}
Cmd Confirm - unregister || Olet poistamassa rekisteröitymistiedon '${0}' joka on linkitetty pelaajaan ${1}
Cmd db - creating backup || Luotiin varmuuskopiotiedosto '${0}.db' ${1} tietokannan tiedoista
Cmd db - removal || Poistetaan Plan-tietoja ${0} tietokannasta..
Cmd db - removal player || Poistetaan pelaajan ${0} tietoja ${1} tietokannasta..
Cmd db - server uninstalled || §aJos palvelin on yhä asennettu, se merkkaa itsensä asennetuksi seuraavalla käynnistyksellä.
Cmd db - write || Kirjoitetaan tietoja ${0} tietokantaan..
Cmd Disable - Disabled || §aPlan on nyt poissa päältä. Voit käyttää reload komentoa uudelleenkäynnistykseen.
Cmd FAIL - Accepts only these arguments || Accepts following as ${0}: ${1}
Cmd FAIL - Accepts only these arguments || Hyväksyy seuraavat vaihtoehdot ${0}: ${1}
Cmd FAIL - Database not open || §cTietokanta: ${0} - Yritä uudelleen myöhemmin.
Cmd FAIL - Empty search string || The search string can not be empty
Cmd FAIL - Empty search string || Hakusana ei voi olla tyhjä
Cmd FAIL - Invalid Username || §cPelaajalla ei löytynyt UUID:ta.
Cmd FAIL - No Feature || §eValitse sammutettava osa! (tällähetkellä tuetut: ${0})
Cmd FAIL - No Permission || §cSinulla ei ole lupaa.
Cmd FAIL - No player || Player '${0}' was not found, they have no UUID.
Cmd FAIL - No player register || Player '${0}' was not found in the database.
Cmd FAIL - No server || Server '${0}' was not found from the database.
Cmd FAIL - No player || Pelaajaa '${0}' ei löydetty, heillä ei ole UUID:ta.
Cmd FAIL - No player register || Pelaajaa '${0}' ei löytynyt tietokannasta.
Cmd FAIL - No server || Palvelinta '${0}' ei löytynyt tietokannasta.
Cmd FAIL - Require only one Argument || §cAnna ainakin yksi muuttuja ${1}
Cmd FAIL - Requires Arguments || §cMääritä enemmän muuttujia (${0}) ${1}
Cmd FAIL - see config || see '${0}' in config.yml
Cmd FAIL - see config || katso '${0}' asetustiedostossa config.yml
Cmd FAIL - Unknown Username || §cPelaajaa ei ole nähty tällä palvelimella.
Cmd FAIL - Users not linked || User is not linked to your account and you don't have permission to remove other user's accounts.
Cmd FAIL - Users not linked || Käyttäjä ei ole linkitetty sinun pelaajaasi, eikä sinulla ole lupaa poistaa toisten käyttäjiä.
Cmd FAIL - WebUser does not exists || §cKäyttäjää ei ole olemassa!
Cmd FAIL - WebUser exists || §cKäyttäjä on jo olemassa!
Cmd Footer - Help || §7Hover over command or arguments or use '/${0} ?' to learn more about them.
Cmd Footer - Help || §7Laita hiiri komennon tai vaihtoehtojen päälle tai käytä '/${0} ?' saadaksesi lisätietoa
Cmd Header - Analysis || > §2Analyysin tulokset
Cmd Header - Help || > §2/${0} Help
Cmd Header - Help || > §2/${0} Apu
Cmd Header - Info || > §2Player Analytics
Cmd Header - Inspect || > §2Pelaaja: §f${0}
Cmd Header - Network || > §2Verkoston Sivu
Cmd Header - Players || > §2Pelaajat
Cmd Header - Search || > §2${0} Tulosta haulle §f${1}§2:
Cmd Header - server list || id::name::uuid
Cmd Header - server list || id::nimi::uuid
Cmd Header - Servers || > §2Palvelimet
Cmd Header - web user list || username::linked to::permission level
Cmd Header - web user list || käyttäjänimi::linkitetty pelaajaan::lupa taso
Cmd Header - Web Users || > §2${0} Web Käyttäjät
Cmd Info - Bungee Connection || §2Yhdistetty Proxyyn: §f${0}
Cmd Info - Database || §2Nykyinen Tietokanta: §f${0}
@ -83,13 +83,13 @@ Cmd Info - Reload Complete || §aUudelleenlataus onnistui!
Cmd Info - Reload Failed || §cUudelleenlatauksessa esiintyi ongelmia. Käynnistystä uudelleen suositellaan.
Cmd Info - Update || §2Päivitys saatavilla: §f${0}
Cmd Info - Version || §2Versio: §f${0}
Cmd network - No network || Server is not connected to a network. The link redirects to server page.
Cmd Notify - No Address || §eNo address was available - using localhost as fallback. Set up 'Alternative_IP' settings.
Cmd network - No network || Palvelinta ei ole liitetty verkostoon. Linkki menee palvelimen sivulle.
Cmd Notify - No Address || §eOsoitetta ei ollut saatavilla - käytetään localhost:ia sen sijasta. Aseta 'Alternative_IP' asetukset.
Cmd Notify - No WebUser || Sinulla ei ehkä ole Web Käyttäjää, käytä /plan register <salasana>-komentoa
Cmd Notify - WebUser register || Rekisteröitiin uusi Web Käyttäjä: '${0}' Lupa taso: ${1}
Cmd Qinspect - Active Playtime || §2Active Playtime: §f${0}
Cmd Qinspect - Active Playtime || §2Aktiivinen peliaika: §f${0}
Cmd Qinspect - Activity Index || §2Aktiivisuus Indeksi: §f${0} | ${1}
Cmd Qinspect - AFK Playtime || §2AFK Time: §f${0}
Cmd Qinspect - AFK Playtime || §2AFK aika: §f${0}
Cmd Qinspect - Deaths || §2Kuolemat: §f${0}
Cmd Qinspect - Geolocation || §2Kirjautui sisään maasta: §f${0}
Cmd Qinspect - Last Seen || §2Viimeksi nähty: §f${0}
@ -101,23 +101,23 @@ Cmd Qinspect - Registered || §2Rekisteröitynyt: §f${0
Cmd Qinspect - Times Kicked || §2Potkittu Pellolle: §f${0}
Cmd SUCCESS - Feature disabled || §aSammutettiin '${0}' toistaiseksi, kunnes Plan ladataan uudelleen.
Cmd SUCCESS - WebUser register || §aLisättiin uusi Web Käyttäjä (${0})!
Cmd unregister - unregistering || Unregistering '${0}'..
Cmd unregister - unregistering || Poistetaan '${0}' rekisteristä..
Cmd WARN - Database not open || §eTietokanta: ${0} - Tämä voi viedä hiukan aikaa..
Cmd Web - Permission Levels || >\§70: Pääsy kaikille sivuille\§71: Pääsy '/players' ja pelaajien sivuille\§72: Pääsy pelaajan sivulle, jolla on sama nimi kuin Web Käyttäjällä\§73+: Ei pääsyä
Command Help - /plan db || Manage Plan database
Command Help - /plan db backup || Backup data of a database to a file
Command Help - /plan db clear || Remove ALL Plan data from a database
Command Help - /plan db || Hallitse Plan tietokantoja
Command Help - /plan db backup || Varmuuskopioi tietokanta tiedostoon
Command Help - /plan db clear || Poista KAIKKI Plan tiedot tietokannasta
Command Help - /plan db hotswap || Vaihda tietokantaa lennosta
Command Help - /plan db move || Siirrä tietoa tietokantojen välillä
Command Help - /plan db remove || Remove player's data from Current database
Command Help - /plan db restore || Restore data from a file to a database
Command Help - /plan db uninstalled || Set a server as uninstalled in the database.
Command Help - /plan disable || Disable the plugin or part of it
Command Help - /plan export || Export html or json files manually
Command Help - /plan import || Import data
Command Help - /plan info || Information about the plugin
Command Help - /plan db remove || Poista pelaajan tiedot nykyisestä tietokannasta
Command Help - /plan db restore || Palauta tiedot tiedostosta tietokantaan
Command Help - /plan db uninstalled || Aseta palvelin poistetuksi tietokannassa.
Command Help - /plan disable || Sammuta Plan tai osa siitä
Command Help - /plan export || Vie html tai json tietoja manuaalisesti
Command Help - /plan import || Tuo tietoja
Command Help - /plan info || Tietoa ohjelmasta
Command Help - /plan ingame || Katso pelaajan tietoja pelissä
Command Help - /plan json || View json of Player's raw data.
Command Help - /plan json || Näkymä pelaajan raakadatasta.
Command Help - /plan network || Katso Verkoston sivua
Command Help - /plan player || Katso Pelaajan sivua
Command Help - /plan players || Katso Pelaajat sivua
@ -126,8 +126,8 @@ Command Help - /plan reload || Käynnistä Plan uudelleen
Command Help - /plan search || Etsi pelaajan nimeä
Command Help - /plan server || Katso Palvelimen sivua
Command Help - /plan servers || Listaa tietokannassa olevat palvelimet
Command Help - /plan unregister || Unregister a user of Plan website
Command Help - /plan users || List all web users
Command Help - /plan unregister || Poista Plan sivuston käyttäjä rekisteristä
Command Help - /plan users || Listaa sivuston käyttäjät
Database - Apply Patch || Muutetaan Tietokantaa: ${0}..
Database - Patches Applied || Tietokannan muutokset suoritettu onnistuneesti.
Database - Patches Applied Already || Kaikki tietokannan muutokset oli jo tehty.
@ -141,7 +141,7 @@ Disable - Unsaved Session Save || Tallennetaan päättymättöm
Disable - WebServer || Web palvelin on sammutettu.
Enable || Player Analytics Käynnistetty.
Enable - Database || ${0}-tietokanta yhteys luotu.
Enable - Notify Bad IP || 0.0.0.0 is not a valid address, set up Alternative_IP settings. Incorrect links might be given!
Enable - Notify Bad IP || 0.0.0.0 ei ole toimiva osoite, aseta Alternative_IP asetukset. Linkit ovat virheellisiä!
Enable - Notify Empty IP || IP server.properties tiedostossa on tyhjä & Alternative_IP ei ole käytössä. Linkit ovat virheellisiä!
Enable - Notify Geolocations disabled || Sijaintien keräys ei ole päällä. (Data.Geolocations: false)
Enable - Notify Geolocations Internet Required || Plan Vaatii internetin ensimmäisellä käynnistyskerralla GeoLite2 tietokannan lataamiseen.
@ -152,12 +152,14 @@ Enable FAIL - Database Patch || Tietokannan muutokset epäonn
Enable FAIL - GeoDB Write || Jokin meni pieleen tallentaessa GeoLite2 tietokantaa
Enable FAIL - WebServer (Proxy) || Web Palvelin ei käynnistynyt!
Enable FAIL - Wrong Database Type || ${0} ei ole tuettu Tietokanta
HTML - AND_BUG_REPORTERS || & Bugien ilmoittajat!
HTML - BANNED (Filters) || Pannassa
HTML - COMPARING_15_DAYS || Verrataan 15 päivää
HTML - COMPARING_60_DAYS || Verrataan 30 päivää sitten nykyhetkeen
HTML - COMPARING_7_DAYS || Verrataan 7 päivää
HTML - DATABASE_NOT_OPEN || Tietokanta ei ole auki, tarkista tietokannan status /plan info komennolla
HTML - ERROR || Todennus epäonnistui virheen vuoksi
HTML - EXPIRED_COOKIE || User cookie has expired
HTML - EXPIRED_COOKIE || Käyttäjän kirjautumiseväste vanheni
HTML - INDEX_ACTIVE || Aktiivinen
HTML - INDEX_INACTIVE || Inaktiivinen
HTML - INDEX_IRREGULAR || Epäsäännöllinen
@ -167,18 +169,22 @@ HTML - KILLED || Tappanut
HTML - LABEL_1ST_WEAPON || Tappavin PvP Ase
HTML - LABEL_2ND_WEAPON || 2. PvP Ase
HTML - LABEL_3RD_WEAPON || 3. PvP Ase
HTML - LABEL_ACTIVE_PLAYTIME || Aktiivinen peliaika
HTML - LABEL_ACTIVITY_INDEX || Aktiivisuus Indeksi
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || Aika AFK:ina
HTML - LABEL_AVG || Keskiarvoinen
HTML - LABEL_AVG_KDR || Keskiarvoinen Tapposuhde
HTML - LABEL_AVG_MOB_KDR || Keskiarvoinen Otus-Tapposuhde
HTML - LABEL_AVG_PLAYTIME || Keskiarvoinen peliaika
HTML - LABEL_AVG_SESSION_LENGTH || Keskiarvoinen istunnon pituus
HTML - LABEL_AVG_TPS || Keskiarvoinen TPS
HTML - LABEL_AVG || Keskimäräinen
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Keskimäräinen Aktiivinen peliaika
HTML - LABEL_AVG_AFK_TIME || Keskimäräinen AFK aika
HTML - LABEL_AVG_KDR || Keskimäräinen Tapposuhde
HTML - LABEL_AVG_MOB_KDR || Keskimäräinen Otus-Tapposuhde
HTML - LABEL_AVG_PLAYTIME || Keskimäräinen peliaika
HTML - LABEL_AVG_SESSION_LENGTH || Keskimäräinen istunnon pituus
HTML - LABEL_AVG_SESSIONS || Keskimääräinen Sessiomäärä
HTML - LABEL_AVG_TPS || Keskimäräinen TPS
HTML - LABEL_BANNED || Pannassa
HTML - LABEL_BEST_PEAK || Paras Huippu
HTML - LABEL_DAY_OF_WEEK || Day of the Week
HTML - LABEL_DAY_OF_WEEK || Viikon päivä
HTML - LABEL_DEATHS || Kuolemat
HTML - LABEL_DOWNTIME || Poissa päältä
HTML - LABEL_DURING_LOW_TPS || Matalan TPS:n aikana:
@ -195,8 +201,8 @@ HTML - LABEL_LONE_JOINS || Yksinäiset pelaajien liittym
HTML - LABEL_LONE_NEW_JOINS || Yksinäiset uusien pelaajien liittymiset
HTML - LABEL_LONGEST_SESSION || Pisin istunto
HTML - LABEL_LOW_TPS || Matalan TPS:n piikit
HTML - LABEL_MAX_FREE_DISK || Max Free Disk
HTML - LABEL_MIN_FREE_DISK || Min Free Disk
HTML - LABEL_MAX_FREE_DISK || Maksimi vapaa levytila
HTML - LABEL_MIN_FREE_DISK || Minimi vapaa levytila
HTML - LABEL_MOB_DEATHS || Otusten aiheuttamat Kuolemat
HTML - LABEL_MOB_KDR || Otus-Tapposuhde
HTML - LABEL_MOB_KILLS || Tapetut Otukset
@ -215,10 +221,10 @@ HTML - LABEL_PLAYER_KILLS || Tapetut Pelaajat
HTML - LABEL_PLAYERS_ONLINE || Pelaajia Paikalla
HTML - LABEL_PLAYTIME || Peliaika
HTML - LABEL_REGISTERED || Rekisteröitynyt
HTML - LABEL_REGISTERED_PLAYERS || Registered Players
HTML - LABEL_REGISTERED_PLAYERS || Rekisteröidyt pelaajat
HTML - LABEL_REGULAR || Kantapelaaja
HTML - LABEL_REGULAR_PLAYERS || Kantapelaajia
HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity
HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Verrannollinen liittymis aktiivisuus
HTML - LABEL_RETENTION || Uusien pysyvyys
HTML - LABEL_SERVER_DOWNTIME || Palvelin pois päältä
HTML - LABEL_SERVER_OCCUPIED || Palvelin pelaajien käytössä
@ -228,30 +234,83 @@ HTML - LABEL_TIMES_KICKED || Heitetty pihalle
HTML - LABEL_TOTAL_PLAYERS || Pelaajia
HTML - LABEL_TOTAL_PLAYTIME || Peliaikaa yhteensä
HTML - LABEL_UNIQUE_PLAYERS || Uniikkeja pelaajia
HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
HTML - LABEL_WEEK_DAYS || 'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai', 'Sunnuntai'
HTML - LINK_BACK_NETWORK || Verkosto
HTML - LINK_BACK_SERVER || Palvelin
HTML - LINK_CHANGELOG || Katso muutoslokia
HTML - LINK_DISCORD || Discord tuki
HTML - LINK_DISCORD || Discord-tuki
HTML - LINK_DOWNLOAD || Lataa
HTML - LINK_ISSUES || Ilmoita ongelmista
HTML - LINK_NIGHT_MODE || Yö-tila
HTML - LINK_PLAYER_PAGE || Player Page
HTML - LINK_PLAYER_PAGE || Pelaajan sivu
HTML - LINK_QUICK_VIEW || Pika-katsaus
HTML - LINK_SERVER_ANALYSIS || Palvelimen Analyysi
HTML - LINK_WIKI || Plan Wiki, ohjeet ja dokumentaatio
HTML - LOCAL_MACHINE || Paikallinen laite
HTML - LOGIN_CREATE_ACCOUNT || Luo käyttäjä!
HTML - LOGIN_FAILED || Kirjautuminen epäonnistui:
HTML - LOGIN_FORGOT_PASSWORD || Unohtuiko salasana?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Unohtuiko salasana? Poista käyttäjä ja uudelleen rekisteröidy.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Käytä komentoa pelissä poistaaksesi käyttäjäsi:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Tai konsolia:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || Komennon jälkeen,
HTML - LOGIN_LOGIN || Kirjaudu
HTML - LOGIN_LOGOUT || Kirjaudu ulos
HTML - LOGIN_PASSWORD || "Salasana"
HTML - LOGIN_USERNAME || "Käyttäjänimi"
HTML - NAV_PLUGINS || Lisäosat
HTML - NEW_CALENDAR || Uudet:
HTML - NO_KILLS || Ei Tappoja
HTML - NO_USER_PRESENT || User cookie not present
HTML - NO_USER_PRESENT || Käyttäjän kirjautumisevästettä ei annettu
HTML - NON_OPERATORS (Filters) || Ei-operaattorit
HTML - NOT_BANNED (Filters) || Ei-pannassa
HTML - OFFLINE || Ei Paikalla
HTML - ONLINE || Paikalla
HTML - OPERATORS (Filters) || Operaattorit
HTML - PER_DAY || / Päivä
HTML - PLAYERS_TEXT || Pelaajia
HTML - QUERY || Kysely<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Vastaavien pelaajien aktiivisuus
HTML - QUERY_ACTIVITY_ON || Aktiivisuus <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Lisää suodatin..
HTML - QUERY_AND || ja
HTML - QUERY_ARE || `ovat`
HTML - QUERY_ARE_ACTIVITY_GROUP || ovat Aktiivisuus Luokissa
HTML - QUERY_ARE_PLUGIN_GROUP || ovat ${plugin}:n ${group} Luokissa
HTML - QUERY_LOADING_FILTERS || Ladataan suodattimia..
HTML - QUERY_MAKE || Tee kysely
HTML - QUERY_MAKE_ANOTHER || Tee toinen kysely
HTML - QUERY_OF_PLAYERS || pelaajista ketkä
HTML - QUERY_PERFORM_QUERY || Tee kysely!
HTML - QUERY_PLAYED_BETWEEN || Pelasivat välillä
HTML - QUERY_REGISTERED_BETWEEN || Rekisteröityvät välillä
HTML - QUERY_RESULTS || Kyselyn tulokset
HTML - QUERY_RESULTS_MATCH || vastasi ${resultCount} pelaajaa
HTML - QUERY_SESSIONS_WITHIN_VIEW || Istunnot näkymän sisällä
HTML - QUERY_SHOW_VIEW || Näytä näkymä
HTML - QUERY_TIME_FROM || >tästä</label>
HTML - QUERY_TIME_TO || >tänne</label>
HTML - QUERY_VIEW || Näkymä:
HTML - QUERY_ZERO_RESULTS || Ei tuloksia
HTML - REGISTER || Rekisteröidy
HTML - REGISTER_CHECK_FAILED || Rekisteröitymisen tilan tarkistus epäonnistui:
HTML - REGISTER_COMPLETE || Viimeistele rekisteröinti
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || Voit viimeistellä käyttäjän rekisteröinnin.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Koodi vanhenee 15 minuutissa
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Käytä seuraavaa komentoa pelissä viimeistelläksesi rekisteröinnin:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Tai konsolia:
HTML - REGISTER_CREATE_USER || Luo uusi käyttäjä
HTML - REGISTER_FAILED || Rekisteröinti epäonnistui:
HTML - REGISTER_HAVE_ACCOUNT || Aiempi käyttäjä? Kirjaudu sisään!
HTML - REGISTER_PASSWORD_TIP || Salasana kannattaa olla yli 8 merkkiä, mutta ei ole rajoituksia.
HTML - REGISTER_SPECIFY_PASSWORD || Anna salasana
HTML - REGISTER_SPECIFY_USERNAME || Anna käyttäjänimi
HTML - REGISTER_USERNAME_LENGTH || Käyttäjänimi voi olla enintään 50 merkkiä, sinun on
HTML - REGISTER_USERNAME_TIP || Käyttäjänimi voi olla enintään 50 merkkiä.
HTML - SESSION || Istunto
HTML - SIDE_GEOLOCATIONS || Sijainnit
HTML - SIDE_INFORMATION || TIETOJA
HTML - SIDE_LINKS || LINKIT
HTML - SIDE_NETWORK_OVERVIEW || Verkoston yhteenveto
HTML - SIDE_OVERVIEW || Yhteenveto
HTML - SIDE_PERFORMANCE || Suorituskyky
@ -271,23 +330,24 @@ HTML - TEXT_CONTRIBUTORS_MONEY || Suuret kiitokset rahallisesti
HTML - TEXT_CONTRIBUTORS_THANKS || Myös seuraavat <span class="col-plan">mahtavat ihmiset</span> ovat tukeneet kehitystä:
HTML - TEXT_DEV_VERSION || Tämä versio on KEHITYS versio.
HTML - TEXT_DEVELOPED_BY || on kehittänyt
HTML - TEXT_FIRST_SESSION || First session
HTML - TEXT_LICENSED_UNDER || Player Analytics kehitetään ja käyttää lisenssiä
HTML - TEXT_METRICS || bStats Metrics
HTML - TEXT_FIRST_SESSION || Ensimmäinen sessio
HTML - TEXT_LICENSED_UNDER || Player Analytics:iä kehitetään, ja käyttää lisenssiä
HTML - TEXT_METRICS || bStats Metriikat
HTML - TEXT_NO_EXTENSION_DATA || Ei dataa lisäosista
HTML - TEXT_NO_LOW_TPS || Ei matalan TPS:n piikkejä
HTML - TEXT_NO_SERVER || No server to display online activity for
HTML - TEXT_NO_SERVERS || No servers found in the database
HTML - TEXT_NO_SERVER || Ei palvelinta jolle näyttää aktiivisuutta
HTML - TEXT_NO_SERVERS || Palvelimia ei löytynyt tietokannasta
HTML - TEXT_PLUGIN_INFORMATION || Tietoa lisäosasta
HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players
HTML - TEXT_PREDICTED_RETENTION || Tämä arvo on arvattu ennustus edellisten pelaajien perusteella
HTML - TEXT_SERVER_INSTRUCTIONS || Vaikuttaa että Plan peli-palvelimia ei ole asennettu tai yhdistetty samaan tietokantaan. Katso <a href="https://github.com/plan-player-analytics/Plan/wiki">wikiin</a> lisätietoja varten.
HTML - TEXT_VERSION || Uusi versio on julkaistu ja on nyt ladattavissa.
HTML - TITLE_30_DAYS || 30 päivää
HTML - TITLE_30_DAYS_AGO || 30 päivää sitten
HTML - TITLE_ALL || Kaikki
HTML - TITLE_ALL_TIME || Kaikkien aikojen
HTML - TITLE_AS_NUMBERS || Numeroina
HTML - TITLE_AVG_PING || Keskimääräinen Ping
HTML - TITLE_BEST_PING || Paras Ping
HTML - TITLE_AVG_PING || Keskimääräinen Vasteaika
HTML - TITLE_BEST_PING || Paras Vasteaika
HTML - TITLE_CALENDAR || Kalenteri
HTML - TITLE_CONNECTION_INFO || Yhteyksien tiedot
HTML - TITLE_COUNTRY || Maa
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Nykyiset pelaajat
HTML - TITLE_DISK || Levytila
HTML - TITLE_GRAPH_DAY_BY_DAY || Päivittäinen katsaus
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Tunnittainen katsaus
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Verkoston paikallaolo
HTML - TITLE_GRAPH_PUNCHCARD || Reikäkortti 30 päivälle
HTML - TITLE_INSIGHTS || Katsauksia 30 päivälle
@ -312,7 +373,7 @@ HTML - TITLE_ONLINE_ACTIVITY || Paikallaolo
HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Paikallaolo Numeroina
HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Yhteenveto Paikallaolosta
HTML - TITLE_PERFORMANCE_AS_NUMBERS || Suorituskyky Numeroina
HTML - TITLE_PING || Ping
HTML - TITLE_PING || Vasteaika
HTML - TITLE_PLAYER || Pelaaja
HTML - TITLE_PLAYER_OVERVIEW || Yhteenveto Pelaajasta
HTML - TITLE_PLAYERBASE_DEVELOPMENT || Pelaajakunnan kehitys
@ -337,18 +398,26 @@ HTML - TITLE_VERSION || Versio
HTML - TITLE_WEEK_COMPARISON || Viikkojen vertaus
HTML - TITLE_WORLD || Maailmojen Resurssit
HTML - TITLE_WORLD_PLAYTIME || Maailmakohtainen Peliaika
HTML - TITLE_WORST_PING || Huonoin Ping
HTML - TITLE_WORST_PING || Huonoin Vasteaika
HTML - TOTAL_ACTIVE_TEXT || Aktiivisena
HTML - TOTAL_AFK || AFK
HTML - TOTAL_PLAYERS || Kaikki Pelaajat
HTML - UNIQUE_CALENDAR || Uniikit:
HTML - UNIT_CHUNKS || Chunks
HTML - UNIT_ENTITIES || Entities
HTML - UNIT_CHUNKS || Chunkkia
HTML - UNIT_ENTITIES || Entiteettiä
HTML - UNIT_NO_DATA || Ei tietoa
HTML - UNIT_THE_PLAYERS || Pelaajia
HTML - USER_AND_PASS_NOT_SPECIFIED || Käyttäjää ja salasana vaaditaan.
HTML - USER_DOES_NOT_EXIST || Käyttäjää ei ole olemassa
HTML - USER_INFORMATION_NOT_FOUND || Rekisteröityminen epäonnistui, yritä uudestaan (Koodi vanhenee 15 minuutin jälkeen)
HTML - USER_PASS_MISMATCH || Käyttäjä ja salasana ei täsmää
HTML - Version Change log || Katso muutoslistaa
HTML - Version Current || Sinulla on versio ${0}
HTML - Version Download || Lataa Plan-${0}.jar
HTML - Version Update || Päivitys
HTML - Version Update Available || Versio ${0} on Saatavilla!
HTML - Version Update Dev || Tämä versio on DEV julkaisu.
HTML - Version Update Info || Uusi versio on julkaistu ja on ladattavissa.
HTML - WITH || <th>Millä
HTML ERRORS - ACCESS_DENIED_403 || Pääsy Kielletty
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Varmista että olet rekisteröinyt käyttäjän komennolla <b>/plan register</b><br>- Tarkista että käyttäjänimi ja salaasana ovat oikein<br>- Nimi ja salasana ovat CASE SENSITIVE<br><br>Jos unohdit salasanasi, pyydä valvojia poistamaan käyttäjäsi ja uudelleenrekisteröidy.
@ -361,30 +430,30 @@ HTML ERRORS - PAGE_NOT_FOUND_404 || Sivua ei ole olemassa.
HTML ERRORS - UNAUTHORIZED_401 || Todennusta ei suoritettu loppuun.
HTML ERRORS - UNKNOWN_PAGE_404 || Varmista menneeesi komennon antamaan osoitteeseen, Esim:</p><p>/player/PelaajanNimi<br>/server/PalvelimenNimi</p>
HTML ERRORS - UUID_404 || Pelaajan UUID:ta ei löytynyt tietokannasta.
In Depth Help - /plan db || Use different database subcommands to change the data in some way
In Depth Help - /plan db backup || Uses SQLite to backup the target database to a file.
In Depth Help - /plan db clear || Clears all Plan tables, removing all Plan-data in the process.
In Depth Help - /plan db hotswap || Reloads the plugin with the other database and changes the config to match.
In Depth Help - /plan db move || Overwrites contents in the other database with the contents in another.
In Depth Help - /plan db remove || Removes all data linked to a player from the Current database.
In Depth Help - /plan db restore || Uses SQLite backup file and overwrites contents of the target database.
In Depth Help - /plan db uninstalled || Marks a server in Plan database as uninstalled so that it will not show up in server queries.
In Depth Help - /plan disable || Disable the plugin or part of it until next reload/restart.
In Depth Help - /plan export || Performs an export to export location defined in the config.
In Depth Help - /plan import || Performs an import to load data into the database.
In Depth Help - /plan info || Display the current status of the plugin.
In Depth Help - /plan db || Käytä eri tietokanta alikomentoja vaikuttaaksesi tietokantaan
In Depth Help - /plan db backup || Käyttää SQLiteä varmuuskopioimaan tiedot tiedostoon.
In Depth Help - /plan db clear || Tyhjentää kaikki Plan taulut, poistaen tiedot samalla.
In Depth Help - /plan db hotswap || Käynnistää ohjelman uudelleen toisella tietokannalla ja muuttaa samalla asetustiedoston tiedot.
In Depth Help - /plan db move || Korvaa tiedot tietokannassa toisen tietokannan tiedoilla.
In Depth Help - /plan db remove || Poistaa kaikki pelaajaan liitetyt tiedot nykyisestä tietokannasta.
In Depth Help - /plan db restore || Käyttää SQLiteä palauttamaan tiedot tiedostosta ylikirjoittaen tietokannan tiedot.
In Depth Help - /plan db uninstalled || Merkitsee palvelimen poistetuksi jotta se ei näy palvelin-kyselyissä.
In Depth Help - /plan disable || Sammuta ohjelma tai osa siitä seuraavaan käynnistykseen asti.
In Depth Help - /plan export || Toimittaa viennin asetuksissa olevaan sijaintiin
In Depth Help - /plan import || Tuo tietoja tietokantaan
In Depth Help - /plan info || Näyttää tietoja ohjelmasta
In Depth Help - /plan ingame || Näyttää tietoja pelaajasta pelin sisällä.
In Depth Help - /plan json || Allows you to download a player's data in json format. All of it.
In Depth Help - /plan network || Obtain a link to the /network page, only does so on networks.
In Depth Help - /plan player || Obtain a link to the /player page of a specific player, or the current player.
In Depth Help - /plan players || Obtain a link to the /players page to see a list of players.
In Depth Help - /plan register || Use without arguments to get link to register page. Use --code [code] after registration to get a user.
In Depth Help - /plan reload || Disable and enable the plugin to reload any changes in config.
In Depth Help - /plan search || List all matching player names to given part of a name.
In Depth Help - /plan server || Obtain a link to the /server page of a specific server, or the current server if no arguments are given.
In Depth Help - /plan servers || List ids, names and uuids of servers in the database.
In Depth Help - /plan unregister || Use without arguments to unregister player linked user, or with username argument to unregister another user.
In Depth Help - /plan users || Lists web users as a table.
In Depth Help - /plan json || Antaa lataa pelaajasta tiedot json-muodossa.
In Depth Help - /plan network || Hanki linkki /network sivulle, toimii vain verkostoissa
In Depth Help - /plan player || Hanki linkki annetun tai nykyisen pelaajan /player sivulle.
In Depth Help - /plan players || Hanki linkki /players sivulle nähdäksesi pelaajalistan
In Depth Help - /plan register || Käytä ilman argumentteja rekisteröimissivua varten. Käytä --code [code] rekisteröidäksesi käyttäjän.
In Depth Help - /plan reload || Sammuta ja käynnistä ohjelma uudelleen asetusten uudelleenlataamiseksi.
In Depth Help - /plan search || Etsi pelaajia joiden nimessä on haettu teksti
In Depth Help - /plan server || Hanki linkki annetun tai nykyisen palvelimen /server sivulle
In Depth Help - /plan servers || Listaa id:t, nimet ja uuid:t tietokannassa olevista palvelimista.
In Depth Help - /plan unregister || Käytä ilman argumentteja poistaaksesi nykyiseen pelaajaan linkitetty käyttäjä, tai anna käyttäjänimi joka poistaa
In Depth Help - /plan users || Listaa sivuston käyttäjät.
Manage - Confirm Overwrite || Tiedot ${0}:ssa ylikirjoitetaan!
Manage - Confirm Removal || Tiedot ${0}:ssa poistetaan!
Manage - Fail || > §cJokin meni vikaan: ${0}
@ -397,14 +466,14 @@ Manage - Fail Same Database || > §cEi voi käyttää samaa
Manage - Fail Same server || Ei voi merkitä tätä palvelinta poistetuksi (Olet siellä)
Manage - Fail, Confirmation || > §cLisää '-a' vahvistaaksesi: ${0}
Manage - List Importers || Tuojat:
Manage - Progress || ${0} / ${1} processed..
Manage - Progress || ${0} / ${1} muutettu..
Manage - Remind HotSwap || §eMuista vaihtaa tietokantaa (/plan db hotswap ${0}) & käynnistä Plan uudelleen.
Manage - Start || > §2Prosessoidaan tietoa..
Manage - Start || > §2Muutetaan tietoa..
Manage - Success || > §aOnnistui!
Negative || Ei
Positive || Kyllä
Today || 'Tänään'
Unavailable || Unavailable
Unavailable || Ei saatavilla
Unknown || Tuntematon
Version - DEV || Tämä on KEHITYS julkaisu.
Version - Latest || Käytät uusinta versiota.
@ -415,13 +484,13 @@ Version FAIL - Read versions.txt || Uuden version tarkistus epäo
Web User Listing || §2${0} §7: §f${1}
WebServer - Notify HTTP || Web Palvelin: Ei Sertifikaattia -> Käytetään HTTP-Palvelinta.
WebServer - Notify HTTP User Auth || Web Palvelin: Käyttäjien varmennus ei käytössä! (Ei turvallista HTTP-protokollalla)
WebServer - Notify HTTPS User Auth || WebServer: User Authorization Disabled! (Disabled in config)
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify HTTPS User Auth || Web Palvelin: Käyttäjien varmennus ei käytössä! (Sammutettu asetuksista)
Webserver - Notify IP Whitelist || Web Palvelin: IP sallimislista käytössä.
Webserver - Notify IP Whitelist Block || Web Palvelin: ${0} kiellettiin pääsy osoitteeseen '${1}'. (ei sallimislistalla)
WebServer - Notify no Cert file || Web Palvelin: Sertifikaatti AvainKirjasto tiedostoa ei löytynyt: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer - Notify Using Proxy || Web Palvelin: Proxy-tolan HTTPS käytössä, varmista että reverse-proxy käyttää HTTPS ja että Plan Alternative_IP.Address osoittaa Proxy-palvelimeen
WebServer FAIL - EOF || Web Palvelin: EOF lukiessa Sertifikaattia. (Tarkista että tiedosto ei ole tyhjä)
WebServer FAIL - Port Bind || Web Palvelin ei käynnistynyt. Onko portti (${0}) käytössä?
WebServer FAIL - SSL Context || Web Palvelin: SSL Kontekstin käynnistys ei onnistunut.
WebServer FAIL - SSL Context || Web Palvelin: SSL Viitepalvelun käynnistys ei onnistunut.
WebServer FAIL - Store Load || Web Palvelin: SSL Sertifikaatin lataus ei onnistunut.
Yesterday || 'Eilen'

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || L'application de correctifs p
Enable FAIL - GeoDB Write || Une erreur s'est produite lors de l'enregistrement de la base de données 'GeoLite2 Geolocation' téléchargée précédemment.
Enable FAIL - WebServer (Proxy) || Le serveur Web n'a pas été initialisé !
Enable FAIL - Wrong Database Type || ${0} n'est pas une base de données prise en charge.
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparaison des 15 derniers jours
HTML - COMPARING_60_DAYS || Comparaison des 60 derniers jours
HTML - COMPARING_7_DAYS || Comparaison des 7 derniers jours
@ -167,14 +169,18 @@ HTML - KILLED || Tué
HTML - LABEL_1ST_WEAPON || 1ère Arme de combat (la plus mortelle)
HTML - LABEL_2ND_WEAPON || 2ème Arme de combat
HTML - LABEL_3RD_WEAPON || 3ème Arme de combat
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Indice d'activité
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || Temps AFK
HTML - LABEL_AVG || Moyen(ne)
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Ratio Kills / Morts moyen
HTML - LABEL_AVG_MOB_KDR || Ratio Kills / Morts de Mobs moyen
HTML - LABEL_AVG_PLAYTIME || Temps de jeu moyen
HTML - LABEL_AVG_SESSION_LENGTH || Durée moyenne d'une session
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || TPS moyen
HTML - LABEL_BANNED || Banni(e)
HTML - LABEL_BEST_PEAK || Pic maximal de joueurs connectés
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Aperçu rapide
HTML - LINK_SERVER_ANALYSIS || Analyse du serveur
HTML - LINK_WIKI || Wiki, Documentation & Tutoriaux de Plan
HTML - LOCAL_MACHINE || Machine locale
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || Nouveau :
HTML - NO_KILLS || Pas de Kills
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Hors-ligne
HTML - ONLINE || En ligne
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Jour
HTML - PLAYERS_TEXT || Joueurs
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Session
HTML - SIDE_GEOLOCATIONS || Géolocalisation
HTML - SIDE_INFORMATION || INFORMATION
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Aperçu du réseau
HTML - SIDE_OVERVIEW || Vue d'ensemble
HTML - SIDE_PERFORMANCE || Performance
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Aucun serveur pour afficher l
HTML - TEXT_NO_SERVERS || Il n'y a pas de serveur dans la base de données.
HTML - TEXT_PLUGIN_INFORMATION || Informations concernant le plugin
HTML - TEXT_PREDICTED_RETENTION || Cette valleur est une prédiction basé sur les anciennes données du joueur.
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Une nouvelle version a été publiée et est maintenant disponible en téléchargement.
HTML - TITLE_30_DAYS || 30 jours
HTML - TITLE_30_DAYS_AGO || Il y a 30 jours
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Base de joueurs actuelle
HTML - TITLE_DISK || Espace disque
HTML - TITLE_GRAPH_DAY_BY_DAY || Jour par Jour
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Activité en ligne du réseau
HTML - TITLE_GRAPH_PUNCHCARD || Carte perforée sur 30 jours
HTML - TITLE_INSIGHTS || Perspectives sur 30 jours
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Aucune donnée
HTML - UNIT_THE_PLAYERS || Joueurs
HTML - USER_AND_PASS_NOT_SPECIFIED || Utilisateur et mot de passe non spécifiés
HTML - USER_DOES_NOT_EXIST || Cet utilisateur n'existe pas
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || L'utilisateur et le mot de passe ne correspondent pas
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Avec
HTML ERRORS - ACCESS_DENIED_403 || Accès refusé.
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Assurez-vous d'avoir enregistré un utilisateur avec :<b>'/plan register'.</b><br>- Vérifiez que le nom d'utilisateur et le mot de passe soient corrects.<br>- Le nom d'utilisateur et le mot de passe sont sensibles au format majuscule/minuscule.<br><br>Si vous avez oublié votre mot de passe, demandez à un membre du staff de supprimer votre ancien utilisateur puis de vous réinscrire.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || Serveur Web : Authentificatio
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || Serveur Web : Fichier KeyStore du certificat introuvable : ${0}
WebServer - Notify Using Proxy || Serveur Web : le Proxy-mode HTTPS est activé, assurez-vous que votre proxy inversé est routé en utilisant HTTPS et Plan Alternative_IP.Link.
WebServer - Notify Using Proxy || Serveur Web : le Proxy-mode HTTPS est activé, assurez-vous que votre proxy inversé est routé en utilisant HTTPS et Plan Alternative_IP.Address.
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || Le Serveur Web n'a pas été initialisé avec succès. Le port (${0}) est-il actuellement utilisé ?
WebServer FAIL - SSL Context || Serveur Web : Échec d'initialisation du contexte SSL.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || Upgrade del Database fallito,
Enable FAIL - GeoDB Write || Qualcosa è andato storto durante lo scaricamento del database GeoLite2
Enable FAIL - WebServer (Proxy) || WebServer non avviato!
Enable FAIL - Wrong Database Type || ${0} non è un Database supportato
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparazione di 15 giorni
HTML - COMPARING_60_DAYS || Comparazione di 30g fa a Ora
HTML - COMPARING_7_DAYS || Comparazione di 7 giorni
@ -167,14 +169,18 @@ HTML - KILLED || Ucciso
HTML - LABEL_1ST_WEAPON || Arma PvP Preferita
HTML - LABEL_2ND_WEAPON || 2° Arma PvP Preferita
HTML - LABEL_3RD_WEAPON || 3° Arma PvP Preferita
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Indice Inattività
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || Tempo AFK
HTML - LABEL_AVG || Media
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Media KDR
HTML - LABEL_AVG_MOB_KDR || Media Mob KDR
HTML - LABEL_AVG_PLAYTIME || Media Tempo di Gioco
HTML - LABEL_AVG_SESSION_LENGTH || Media Lunghezza Sessione
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Media TPS
HTML - LABEL_BANNED || Bannato
HTML - LABEL_BEST_PEAK || Record Migliore
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Vista Rapida
HTML - LINK_SERVER_ANALYSIS || Analisi Server
HTML - LINK_WIKI || Plan Wiki, Tutorial e Documentazione
HTML - LOCAL_MACHINE || Macchina Locale
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || Nuovo:
HTML - NO_KILLS || Nessuna Uccisione
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Offline
HTML - ONLINE || Online
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Giorno
HTML - PLAYERS_TEXT || Giocatori
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Session
HTML - SIDE_GEOLOCATIONS || Geolocalizazione
HTML - SIDE_INFORMATION || INFORMAZIONE
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Informazioni sul Network
HTML - SIDE_OVERVIEW || Informazioni
HTML - SIDE_PERFORMANCE || Performance
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Nessun server con cui mostare
HTML - TEXT_NO_SERVERS || Nessun server trovato in questo database
HTML - TEXT_PLUGIN_INFORMATION || Informazioni su questo plugin
HTML - TEXT_PREDICTED_RETENTION || Questo valore è una previsione basata sui giocatori precedenti
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Una nuova versione è stata rilasciata ed è ora disponibile da scaricare.
HTML - TITLE_30_DAYS || 30 giorni
HTML - TITLE_30_DAYS_AGO || 30 giorni fa
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Playerbase Corrente
HTML - TITLE_DISK || Spazio sul Disco
HTML - TITLE_GRAPH_DAY_BY_DAY || Giorno Per Giorno
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Attività Online Network
HTML - TITLE_GRAPH_PUNCHCARD || Attività in 30 giorni
HTML - TITLE_INSIGHTS || Grafico in 30 giorni
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Nessun Dato
HTML - UNIT_THE_PLAYERS || Giocatori
HTML - USER_AND_PASS_NOT_SPECIFIED || Utente e Password non specificati
HTML - USER_DOES_NOT_EXIST || Utente non esistente
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || Utente e Password non corrispondono
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Con
HTML ERRORS - ACCESS_DENIED_403 || Acccesso Bloccato
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Assicurati di registare un nuovo utente usando <b>/plan register</b><br>- Controlla che il nome e password siano corretti<br>- Nome e password sono case-sensitive<br><br>Se hai dimenticato la tua passwrod, chiedi a uno staff di rimuovere i tuoi dati e registrarti di nuovo.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: User Authorization
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || WebServer: Chiave del Certfiicato non trovato: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || WebServer non avviato. La porta (${0}) è già occupata?
WebServer FAIL - SSL Context || WebServer: Inilizazione Contenuto SSL Fallito.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || データベースのパッ
Enable FAIL - GeoDB Write || ダウンロードした「GeoLite2」の位置情報データベースを保存中に何らかのエラーが発生しました
Enable FAIL - WebServer (Proxy) || ウェブサーバーの初期化に失敗しました!
Enable FAIL - Wrong Database Type || 「${0}」はサポートされていないデータベースです
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || 直近15日との比較
HTML - COMPARING_60_DAYS || 30日前との比較
HTML - COMPARING_7_DAYS || 直近1週間との比較
@ -167,14 +169,18 @@ HTML - KILLED || 殺した人
HTML - LABEL_1ST_WEAPON || 最もPvPで使用されている武器
HTML - LABEL_2ND_WEAPON || 2番目にPvPで使用されている武器
HTML - LABEL_3RD_WEAPON || 3番目にPvPで使用されている武器
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || 活動指数
HTML - LABEL_AFK || 離席
HTML - LABEL_AFK_TIME || 離席時間
HTML - LABEL_AVG || 平均
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || 平均KDR
HTML - LABEL_AVG_MOB_KDR || モブに対してのKDR
HTML - LABEL_AVG_PLAYTIME || 平均プレイ時間
HTML - LABEL_AVG_SESSION_LENGTH || 平均接続時間
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || 平均TPS
HTML - LABEL_BANNED || BAN履歴
HTML - LABEL_BEST_PEAK || 全体のピークタイム
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || クイックビュー
HTML - LINK_SERVER_ANALYSIS || サーバーの分析結果
HTML - LINK_WIKI || 「Plan」のwiki、チュートリアルとドキュメント
HTML - LOCAL_MACHINE || ローカルマシン
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || プラグイン
HTML - NEW_CALENDAR || New:
HTML - NO_KILLS || プレイヤーキルなし
HTML - NO_USER_PRESENT || ユーザーのクッキーが存在しません
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || オフライン
HTML - ONLINE || オンライン
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || /日
HTML - PLAYERS_TEXT || プレイヤー
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || オンライン
HTML - SIDE_GEOLOCATIONS || 地域
HTML - SIDE_INFORMATION || インフォメーション
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || ネットワーク概要
HTML - SIDE_OVERVIEW || 概要
HTML - SIDE_PERFORMANCE || パフォーマンス
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || オンラインアクティ
HTML - TEXT_NO_SERVERS || データーベース内に登録されたサーバーが見つかりません
HTML - TEXT_PLUGIN_INFORMATION || プラグインに関する情報
HTML - TEXT_PREDICTED_RETENTION || これは以前のプレーヤーから基づいた予測値です
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || 新バージョンがリリースされており、ダウンロード可能です
HTML - TITLE_30_DAYS || 1ヶ月
HTML - TITLE_30_DAYS_AGO || 1ヶ月前
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPUとメモリー
HTML - TITLE_CURRENT_PLAYERBASE || ログインプレイヤー
HTML - TITLE_DISK || ドライブの容量
HTML - TITLE_GRAPH_DAY_BY_DAY || 詳細情報
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || ネットワーク内の接続状況
HTML - TITLE_GRAPH_PUNCHCARD || 1ヶ月のパンチボード
HTML - TITLE_INSIGHTS || 1ヶ月のパンチボード
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || データなし
HTML - UNIT_THE_PLAYERS || プレイヤー
HTML - USER_AND_PASS_NOT_SPECIFIED || ユーザーとパスワードが入力されてません
HTML - USER_DOES_NOT_EXIST || 入力されたユーザーは存在しません
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || 入力されたユーザー名とパスワードが間違っています
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>死亡原因
HTML ERRORS - ACCESS_DENIED_403 || アクセスが拒否されました
HTML ERRORS - AUTH_FAIL_TIPS_401 || - 登録したユーザーを<b>「/plan register 」</b>で確認できます。<br>- 入力したユーザー名とパスワードが正しいことを確認して下さい<br>- ユーザー名とパスワードは大文字と小文字が区別されています<br><br>パスワードを忘れた場合は、管理者に古いユーザーを削除して新しくユーザーを再登録するよう依頼して下さい
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || Webサーバー: ユーザー
Webserver - Notify IP Whitelist || Webサーバー: IPのホワイトリストが有効になっています
Webserver - Notify IP Whitelist Block || Webサーバー: 「${0}」の「${1}」へのアクセスが拒否されました(ホワイトリストには未登録です)
WebServer - Notify no Cert file || Webサーバー: 次のパスに保存された認証キーファイルが存在しません: ${0}
WebServer - Notify Using Proxy || Webサーバー: Proxy-mode HTTPSが有効になっています、リバースプロキシがHTTPSを使用してルーティングされていることと、PlanのAlternative_IP.Linkがプロキシを指定していることを確認してください。
WebServer - Notify Using Proxy || Webサーバー: Proxy-mode HTTPSが有効になっています、リバースプロキシがHTTPSを使用してルーティングされていることと、PlanのAlternative_IP.Addressがプロキシを指定していることを確認してください。
WebServer FAIL - EOF || Webサーバー: 証明書ファイルをロード中にEOFエラーが発生しました (ファイルが殻になっていないか確認してください)
WebServer FAIL - Port Bind || Webサーバー: 初期化が正常に終了しませんでした。ポート番号(${0})は使用されていませんか?
WebServer FAIL - SSL Context || Webサーバー: SSLコンテキストの初期化に失敗しました。

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || 데이터베이스 패치에
Enable FAIL - GeoDB Write || 다운로드 한 GeoLite2 Geolocation 데이터베이스를 저장하는 도중에 문제가 발생했습니다.
Enable FAIL - WebServer (Proxy) || 웹 서버가 초기화되지 않았습니다!
Enable FAIL - Wrong Database Type || ${0} 해당 데이터베이스는 지원되지 않습니다.
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || 지난 15일 비교
HTML - COMPARING_60_DAYS || 지난 30일 비교
HTML - COMPARING_7_DAYS || 지난 7일 비교
@ -167,14 +169,18 @@ HTML - KILLED || Killed
HTML - LABEL_1ST_WEAPON || 치명적인 PvP 무기
HTML - LABEL_2ND_WEAPON || 2nd PvP 무기
HTML - LABEL_3RD_WEAPON || 3rd PvP 무기
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || 활동 색인
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK 시간
HTML - LABEL_AVG || 평균
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || 평균 사망 횟수(KDR)
HTML - LABEL_AVG_MOB_KDR || 평균 몬스터 사망 횟수(Mob KDR)
HTML - LABEL_AVG_PLAYTIME || 평균 플레이 타임
HTML - LABEL_AVG_SESSION_LENGTH || 평균 접속 시간(세션 길이)
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || 평균 TPS
HTML - LABEL_BANNED || Banned
HTML - LABEL_BEST_PEAK || 최고의 피크
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || 퀵 뷰
HTML - LINK_SERVER_ANALYSIS || 서버 분석
HTML - LINK_WIKI || Plan 위키, 튜토리얼 & 문서
HTML - LOCAL_MACHINE || 로컬 머신
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || 플러그인
HTML - NEW_CALENDAR || New:
HTML - NO_KILLS || 살인 없음
HTML - NO_USER_PRESENT || 사용자 쿠키가 없습니다.
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || 오프라인
HTML - ONLINE || 온라인
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Day
HTML - PLAYERS_TEXT || 플레이어들
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || 세션
HTML - SIDE_GEOLOCATIONS || 지리적 위치
HTML - SIDE_INFORMATION || 정보
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || 네트워크 개요
HTML - SIDE_OVERVIEW || 개요
HTML - SIDE_PERFORMANCE || 성능
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || 온라인 활동을 표시
HTML - TEXT_NO_SERVERS || 데이터베이스에 서버가 없습니다.
HTML - TEXT_PLUGIN_INFORMATION || 플러그인에 대한 정보
HTML - TEXT_PREDICTED_RETENTION || 이 값은 기존 플레이어를 기반으로 한 예측입니다.
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || 새 버전이 출시되었으며 이제 다운로드 할 수 있습니다.
HTML - TITLE_30_DAYS || 30일
HTML - TITLE_30_DAYS_AGO || 30일 전
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || 현재 플레이어 베이스
HTML - TITLE_DISK || 디스크 공간
HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || 네트워크 온라인 활동
HTML - TITLE_GRAPH_PUNCHCARD || 30일 동안의 펀치 카드
HTML - TITLE_INSIGHTS || 30일 동안의 인사이트
@ -350,6 +411,13 @@ HTML - USER_AND_PASS_NOT_SPECIFIED || 사용자 및 암호가 지
HTML - USER_DOES_NOT_EXIST || 사용자가 존재하지 않습니다
HTML - USER_INFORMATION_NOT_FOUND || 등록 실패, 다시 시도 (코드는 15분 후에 만료 됨)
HTML - USER_PASS_MISMATCH || 사용자와 비밀번호가 일치하지 않습니다
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>With
HTML ERRORS - ACCESS_DENIED_403 || Access Denied 403
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with <b>/plan register</b><br>- Check that the username and password are correct<br>- Username and password are case-sensitive<br><br>If you have forgotten your password, ask a staff member to delete your old user and re-register.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || O patch do banco de dados fal
Enable FAIL - GeoDB Write || Algo deu errado ao salvar o banco de dados de geolocalização do GeoLite2
Enable FAIL - WebServer (Proxy) || O servidor web não pode ser inicializado!
Enable FAIL - Wrong Database Type || ${0} não é um banco de dados suportado
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparing 15 days
HTML - COMPARING_60_DAYS || Comparing 30d ago to Now
HTML - COMPARING_7_DAYS || Comparing 7 days
@ -167,14 +169,18 @@ HTML - KILLED || Assassinou
HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon
HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon
HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Índice de Atividade
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK Time
HTML - LABEL_AVG || Average
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Average KDR
HTML - LABEL_AVG_MOB_KDR || Average Mob KDR
HTML - LABEL_AVG_PLAYTIME || Average Playtime
HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Average TPS
HTML - LABEL_BANNED || Banido
HTML - LABEL_BEST_PEAK || Pico Máximo
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Quick view
HTML - LINK_SERVER_ANALYSIS || Análise do Servidor
HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation
HTML - LOCAL_MACHINE || Máquina Local
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Plugins
HTML - NEW_CALENDAR || Novo:
HTML - NO_KILLS || Sem Kills
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Offline
HTML - ONLINE || Online
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Dia
HTML - PLAYERS_TEXT || Jogadores
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Sessão
HTML - SIDE_GEOLOCATIONS || Geolocalizações
HTML - SIDE_INFORMATION || INFORMATION
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Network Overview
HTML - SIDE_OVERVIEW || Visão Global
HTML - SIDE_PERFORMANCE || Desempenho
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || No server to display online a
HTML - TEXT_NO_SERVERS || No servers found in the database
HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin
HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || A new version has been released and is now available for download.
HTML - TITLE_30_DAYS || 30 days
HTML - TITLE_30_DAYS_AGO || 30 days ago
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Base de Jogadores Atual
HTML - TITLE_DISK || Espaço de disco
HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity
HTML - TITLE_GRAPH_PUNCHCARD || Cartão Perfurado for 30 days
HTML - TITLE_INSIGHTS || Insights for 30 days
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || No Data
HTML - UNIT_THE_PLAYERS || Players
HTML - USER_AND_PASS_NOT_SPECIFIED || Usuário e Senha não específicado
HTML - USER_DOES_NOT_EXIST || Usuário não existe
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || Usuário e Senha não coincidem
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Com
HTML ERRORS - ACCESS_DENIED_403 || Acesso Negado
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Certifique-se de ter registrado um usuário com <b>/plan register</b><br>- Verifique se o nome de usuário e a senha estão corretos<br>- O nome de usuário e senha fazem distinção entre maiúsculas e minúsculas, verifique se escreveu corretamente<br><br>Se você esqueceu sua senha, peça para um staff que exclua seu antigo usuário e registre um novo.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: User Authorization
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || Servidor Web: Arquivo KeyStore não encontrado: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || O servidor web não foi inicializado. A porta (${0}) já está em uso?
WebServer FAIL - SSL Context || Servidor Web: Falha ao inicializar certificado SSL.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || Ошибка обновлен
Enable FAIL - GeoDB Write || Что-то пошло не так при сохранении загруженной базы геолокации GeoLite2
Enable FAIL - WebServer (Proxy) || Веб-сервер не инициализирован!
Enable FAIL - Wrong Database Type || ${0} неподдерживаемая база данных
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Сравнение 15 дней
HTML - COMPARING_60_DAYS || Сравнение 30 дней назад и сейчас
HTML - COMPARING_7_DAYS || Сравнение 7 дней
@ -167,14 +169,18 @@ HTML - KILLED || Убит
HTML - LABEL_1ST_WEAPON || Самое смертоносное оружие в PvP
HTML - LABEL_2ND_WEAPON || 2-е PvP оружие
HTML - LABEL_3RD_WEAPON || 3-е PvP оружие
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Индекс активности
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || Время AFK
HTML - LABEL_AVG || Сред.
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Сред. KDR
HTML - LABEL_AVG_MOB_KDR || Сред. моб KDR
HTML - LABEL_AVG_PLAYTIME || Среднее время игры
HTML - LABEL_AVG_SESSION_LENGTH || Средняя продолжительность сеанса
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Средний TPS
HTML - LABEL_BANNED || Забанен
HTML - LABEL_BEST_PEAK || Максимальный пик
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Быстрый просмот
HTML - LINK_SERVER_ANALYSIS || Анализ сервера
HTML - LINK_WIKI || Plan Вики, туториалы и документация
HTML - LOCAL_MACHINE || Локальная машина
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Плагины
HTML - NEW_CALENDAR || Новое:
HTML - NO_KILLS || Нет убийств
HTML - NO_USER_PRESENT || Пользовательский файл cookie отсутствует
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Не в сети
HTML - ONLINE || В сети
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / День
HTML - PLAYERS_TEXT || Игроки
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Сессия
HTML - SIDE_GEOLOCATIONS || Геолокация
HTML - SIDE_INFORMATION || ИНФОРМАЦИЯ
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Обзор сети
HTML - SIDE_OVERVIEW || Обзор
HTML - SIDE_PERFORMANCE || Производительность
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || Нет сервера для
HTML - TEXT_NO_SERVERS || В базе данных не найдено ни одного сервера
HTML - TEXT_PLUGIN_INFORMATION || Информация о плагине
HTML - TEXT_PREDICTED_RETENTION || Это значение является прогнозом на основе предыдущих игроков
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || Новая версия была выпущена и теперь доступна для скачивания.
HTML - TITLE_30_DAYS || 30 дней
HTML - TITLE_30_DAYS_AGO || 30 дней назад
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || ЦПУ & ОЗУ
HTML - TITLE_CURRENT_PLAYERBASE || Текущая база игроков
HTML - TITLE_DISK || Дисковое пространство
HTML - TITLE_GRAPH_DAY_BY_DAY || День за днем
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Сетевая активность
HTML - TITLE_GRAPH_PUNCHCARD || Перфокарта на 30 дней
HTML - TITLE_INSIGHTS || Статистика за 30 дней
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || Нет данных
HTML - UNIT_THE_PLAYERS || Игроки
HTML - USER_AND_PASS_NOT_SPECIFIED || Имя пользователя и пароль не указаны
HTML - USER_DOES_NOT_EXIST || Пользователь не существует
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || Пользователь и пароль не совпадают
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>С
HTML ERRORS - ACCESS_DENIED_403 || Доступ закрыт
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Убедитесь, что вы зарегистрировали пользователя с помощью <b>/plan register</b><br>- Проверьте правильность имени пользователя и пароля<br>- Имя пользователя и пароль чувствительны к регистру<br><br>Если вы забыли ваш пароль, попросите сотрудника удалить вашего старого пользователя и перерегистрировать вас.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || Веб-сервер: авто
Webserver - Notify IP Whitelist || Веб-сервер: Белый список IP адресов включён.
Webserver - Notify IP Whitelist Block || Веб-сервер: ${0} было отказано в доступе к '${1}'. (не в белом списке)
WebServer - Notify no Cert file || Веб-сервер: файл хранилища ключей сертификата не найден: ${0}
WebServer - Notify Using Proxy || Веб-сервер: HTTPS в режиме прокси включен, убедитесь, что ваш обратный прокси-сервер выполняет маршрутизацию с использованием HTTPS, и Plan Alternative_IP.Link указывает на прокси
WebServer - Notify Using Proxy || Веб-сервер: HTTPS в режиме прокси включен, убедитесь, что ваш обратный прокси-сервер выполняет маршрутизацию с использованием HTTPS, и Plan Alternative_IP.Address указывает на прокси
WebServer FAIL - EOF || Веб-сервер: EOF при чтении файла сертификата. (Убедитесь, что файл не пустой)
WebServer FAIL - Port Bind || Веб-сервер не был успешно инициализирован. Порт (${0}) используется?
WebServer FAIL - SSL Context || Веб-сервер: сбой инициализации контекста SSL.

View File

@ -152,6 +152,8 @@ Enable FAIL - Database Patch || VeriTabanı yaması başarıs
Enable FAIL - GeoDB Write || İndirilen GeoLite2 Geolocation veritabanını kaydederken bir şeyler ters gitti.
Enable FAIL - WebServer (Proxy) || WebServer başlatılmadı!
Enable FAIL - Wrong Database Type || ${0} Desteklenmeyen bir veritabanı
HTML - AND_BUG_REPORTERS || & Bug reporters!
HTML - BANNED (Filters) || Banned
HTML - COMPARING_15_DAYS || Comparing 15 days
HTML - COMPARING_60_DAYS || Comparing 30d ago to Now
HTML - COMPARING_7_DAYS || Comparing 7 days
@ -167,14 +169,18 @@ HTML - KILLED || Öldürülen
HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon
HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon
HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon
HTML - LABEL_ACTIVE_PLAYTIME || Active Playtime
HTML - LABEL_ACTIVITY_INDEX || Aktivite göstergesi
HTML - LABEL_AFK || AFK
HTML - LABEL_AFK_TIME || AFK Time
HTML - LABEL_AVG || Average
HTML - LABEL_AVG_ACTIVE_PLAYTIME || Average Active Playtime
HTML - LABEL_AVG_AFK_TIME || Average AFK Time
HTML - LABEL_AVG_KDR || Average KDR
HTML - LABEL_AVG_MOB_KDR || Average Mob KDR
HTML - LABEL_AVG_PLAYTIME || Average Playtime
HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length
HTML - LABEL_AVG_SESSIONS || Average Sessions
HTML - LABEL_AVG_TPS || Average TPS
HTML - LABEL_BANNED || Yasaklanmış
HTML - LABEL_BEST_PEAK || Tüm Zamanların Zirvesi
@ -241,17 +247,70 @@ HTML - LINK_QUICK_VIEW || Quick view
HTML - LINK_SERVER_ANALYSIS || Sunucu analizi
HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation
HTML - LOCAL_MACHINE || Yerel makine
HTML - LOGIN_CREATE_ACCOUNT || Create an Account!
HTML - LOGIN_FAILED || Login failed:
HTML - LOGIN_FORGOT_PASSWORD || Forgot Password?
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_1 || Forgot password? Unregister and register again.
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_2 || Use the following command in game to remove your current user:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_3 || Or using console:
HTML - LOGIN_FORGOT_PASSWORD_INSTRUCTIONS_4 || After using the command,
HTML - LOGIN_LOGIN || Login
HTML - LOGIN_LOGOUT || Logout
HTML - LOGIN_PASSWORD || "Password"
HTML - LOGIN_USERNAME || "Username"
HTML - NAV_PLUGINS || Pluginler
HTML - NEW_CALENDAR || Yeni:
HTML - NO_KILLS || Öldürmesi yok
HTML - NO_USER_PRESENT || User cookie not present
HTML - NON_OPERATORS (Filters) || Non operators
HTML - NOT_BANNED (Filters) || Not banned
HTML - OFFLINE || Çevrimdışı
HTML - ONLINE || Çevrimiçi
HTML - OPERATORS (Filters) || Operators
HTML - PER_DAY || / Gün
HTML - PLAYERS_TEXT || Oyuncular
HTML - QUERY || Query<
HTML - QUERY_ACTIVITY_OF_MATCHED_PLAYERS || Activity of matched players
HTML - QUERY_ACTIVITY_ON || Activity on <span id="activity-date"></span>
HTML - QUERY_ADD_FILTER || Add a filter..
HTML - QUERY_AND || and
HTML - QUERY_ARE || `are`
HTML - QUERY_ARE_ACTIVITY_GROUP || are in Activity Groups
HTML - QUERY_ARE_PLUGIN_GROUP || are in ${plugin}'s ${group} Groups
HTML - QUERY_LOADING_FILTERS || Loading filters..
HTML - QUERY_MAKE || Make a query
HTML - QUERY_MAKE_ANOTHER || Make another query
HTML - QUERY_OF_PLAYERS || of Players who
HTML - QUERY_PERFORM_QUERY || Perform Query!
HTML - QUERY_PLAYED_BETWEEN || Played between
HTML - QUERY_REGISTERED_BETWEEN || Registered between
HTML - QUERY_RESULTS || Query Results
HTML - QUERY_RESULTS_MATCH || matched ${resultCount} players
HTML - QUERY_SESSIONS_WITHIN_VIEW || Sessions within view
HTML - QUERY_SHOW_VIEW || Show a view
HTML - QUERY_TIME_FROM || >from</label>
HTML - QUERY_TIME_TO || >to</label>
HTML - QUERY_VIEW || View:
HTML - QUERY_ZERO_RESULTS || Query produced 0 results
HTML - REGISTER || Register
HTML - REGISTER_CHECK_FAILED || Checking registration status failed:
HTML - REGISTER_COMPLETE || Complete Registration
HTML - REGISTER_COMPLETE_INSTRUCTIONS_1 || You can now finish registering the user.
HTML - REGISTER_COMPLETE_INSTRUCTIONS_2 || Code expires in 15 minutes
HTML - REGISTER_COMPLETE_INSTRUCTIONS_3 || Use the following command in game to finish registration:
HTML - REGISTER_COMPLETE_INSTRUCTIONS_4 || Or using console:
HTML - REGISTER_CREATE_USER || Create a new user
HTML - REGISTER_FAILED || Registration failed:
HTML - REGISTER_HAVE_ACCOUNT || Already have an account? Login!
HTML - REGISTER_PASSWORD_TIP || Password should be more than 8 characters, but there are no limitations.
HTML - REGISTER_SPECIFY_PASSWORD || You need to specify a Password
HTML - REGISTER_SPECIFY_USERNAME || You need to specify a Username
HTML - REGISTER_USERNAME_LENGTH || Username can be up to 50 characters, yours is
HTML - REGISTER_USERNAME_TIP || Username can be up to 50 characters.
HTML - SESSION || Oturum
HTML - SIDE_GEOLOCATIONS || Coğrafi Konumlar
HTML - SIDE_INFORMATION || INFORMATION
HTML - SIDE_LINKS || LINKS
HTML - SIDE_NETWORK_OVERVIEW || Network Overview
HTML - SIDE_OVERVIEW || Genel Bakış
HTML - SIDE_PERFORMANCE || Performans
@ -280,6 +339,7 @@ HTML - TEXT_NO_SERVER || No server to display online a
HTML - TEXT_NO_SERVERS || No servers found in the database
HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin
HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players
HTML - TEXT_SERVER_INSTRUCTIONS || It appears that Plan is not installed on any game servers or not connected to the same database. See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.
HTML - TEXT_VERSION || A new version has been released and is now available for download.
HTML - TITLE_30_DAYS || 30 days
HTML - TITLE_30_DAYS_AGO || 30 days ago
@ -295,6 +355,7 @@ HTML - TITLE_CPU_RAM || CPU & RAM
HTML - TITLE_CURRENT_PLAYERBASE || Current Playerbase
HTML - TITLE_DISK || Disk Space
HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day
HTML - TITLE_GRAPH_HOUR_BY_HOUR || Hour by Hour
HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity
HTML - TITLE_GRAPH_PUNCHCARD || Punchcard for 30 days
HTML - TITLE_INSIGHTS || Insights for 30 days
@ -348,7 +409,15 @@ HTML - UNIT_NO_DATA || No Data
HTML - UNIT_THE_PLAYERS || Players
HTML - USER_AND_PASS_NOT_SPECIFIED || Kullanıcı ve Şifre belirtilmedi
HTML - USER_DOES_NOT_EXIST || Böyle Bir Kullanıcı Yok
HTML - USER_INFORMATION_NOT_FOUND || Registration failed, try again (The code expires after 15 minutes)
HTML - USER_PASS_MISMATCH || Kullanıcı adı ve şifre uyuşmuyor
HTML - Version Change log || View Changelog
HTML - Version Current || You have version ${0}
HTML - Version Download || Download Plan-${0}.jar
HTML - Version Update || Update
HTML - Version Update Available || Version ${0} is Available!
HTML - Version Update Dev || This version is a DEV release.
HTML - Version Update Info || A new version has been released and is now available for download.
HTML - WITH || <th>Birlikte
HTML ERRORS - ACCESS_DENIED_403 || Giriş reddedildi
HTML ERRORS - AUTH_FAIL_TIPS_401 || - Bir kullanıcıyı <b>/plan register</b><br>- ile kayıt ettiğinize emin olun ismin ve şifrenin doğru olup olmadığını kontol edin <br>- Kullanıcı adı ve şifre büyük / küçük harf duyarlıdır<br><br>Eğer şifreni unuttuysan, Yetkiliden sizi tekrar kayıt etmesini isteyin.
@ -419,7 +488,7 @@ WebServer - Notify HTTPS User Auth || WebServer: User Authorization
Webserver - Notify IP Whitelist || Webserver: IP Whitelist is enabled.
Webserver - Notify IP Whitelist Block || Webserver: ${0} was denied access to '${1}'. (not whitelisted)
WebServer - Notify no Cert file || WebServer: Setrifikası Dosyası Bulunamadı: ${0}
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Link points to the Proxy
WebServer - Notify Using Proxy || WebServer: Proxy-mode HTTPS enabled, make sure that your reverse-proxy is routing using HTTPS and Plan Alternative_IP.Address points to the Proxy
WebServer FAIL - EOF || WebServer: EOF when reading Certificate file. (Check that the file is not empty)
WebServer FAIL - Port Bind || Web Sunucusu başarıyla başlatılmadı. Bu (${0}) port mu kullanılıyor ?
WebServer FAIL - SSL Context || WebServer: SSL İçeriği Başlatma Başarısız Oldu.

View File

@ -0,0 +1,3 @@
.query-buttons {
display: inline-block !important;
}

View File

@ -1235,6 +1235,16 @@ body.sidebar-hidden .navbar-nav {
display: none;
}
.filter-remover {
height: 2.4rem;
width: 2.4rem;
padding: 0;
}
.query-buttons {
display: none;
}
.contributors li {
padding: 0;
}

View File

@ -31,12 +31,10 @@
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img alt="logo" class="w-22" src="img/Flaticon_circle.png">
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button active">
@ -45,7 +43,6 @@
<span>to main page</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="ml-3 align-items-center justify-content-between">
@ -71,7 +68,6 @@
<!-- Main Content -->
<div id="content" style="display: flex;">
<!-- Begin Player Overview Tab -->
<div class="tab" id="information" style="width: 100%;">
<div class="container-fluid mt-4">
<!-- Page Heading -->
@ -80,7 +76,6 @@
</div>
<div class="row">
<!-- Card -->
<div class="col-xs-12 col-sm-12 col-lg-11">
<div class="card shadow mb-4">
<div class="card-body" id="data_player_info">
@ -151,8 +146,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -180,8 +174,8 @@
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i> Plan Wiki,
Tutorials & Documentation</a>
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"

View File

@ -468,6 +468,7 @@
Highcharts.setOptions(Highcharts.theme);
updateGraphs();
} catch (e) {
console.error(e);
}
}

View File

@ -0,0 +1,5 @@
function insertElementBefore(elementSelector, createElementFunction) {
const placeBefore = document.querySelector(elementSelector);
const element = createElementFunction();
placeBefore.insertAdjacentElement('beforebegin', element);
}

View File

@ -0,0 +1,196 @@
class Filter {
constructor(kind) {
this.kind = kind;
}
render(filterCount) {
return 'Unimplemented render function'
}
toObject() {
return {kind: this.kind}
}
}
class MultipleChoiceFilter extends Filter {
constructor(
id, kind, label, options
) {
super(kind);
this.id = id;
this.label = label;
this.options = options;
}
render(filterCount) {
const select = filterCount === 0 ? "of Players who " : "and ";
let html =
`<div id="${this.id}" class="mt-2 input-group input-row">
<div class="col-12">
<label for="${this.id}">${select}${this.label}:</label>
<button class="filter-remover btn btn-outline-secondary float-right"
onclick="removeFilter('${this.id}')"><i class="far fa-fw fa-trash-alt"></i></button>
<select class="form-control" multiple style="margin-bottom: 0.5rem;">`;
for (const option of this.options.options) {
html += `<option>${option}</option>`;
}
html += `</select></div></div>`;
return html;
}
toObject() {
let selected = [];
for (let option of document.querySelector(`#${this.id} select`).options) {
if (option.selected) selected.push(option.text);
}
selected = JSON.stringify(selected);
return {
kind: this.kind,
parameters: {selected}
}
}
}
class ActivityIndexFilter extends MultipleChoiceFilter {
constructor(
id, options
) {
super(id, "activityIndexNow", `are in Activity Groups`, options);
}
}
class BannedFilter extends MultipleChoiceFilter {
constructor(
id, options
) {
super(id, "banned", `are`, options);
}
}
class OperatorsFilter extends MultipleChoiceFilter {
constructor(
id, options
) {
super(id, "operators", `are`, options);
}
}
class PluginGroupsFilter extends MultipleChoiceFilter {
constructor(
id, plugin, group, options
) {
super(id, `pluginGroups: ${plugin} ${group}`, `are in ${plugin}'s ${group} Groups`, options);
}
}
class BetweenDateFilter extends Filter {
constructor(id, kind, label, options) {
super(kind);
this.id = id;
this.label = label;
this.afterDate = options.after[0];
this.afterTime = options.after[1];
this.beforeDate = options.before[0];
this.beforeTime = options.before[1];
}
render(filterCount) {
const id = this.id;
const select = filterCount === 0 ? "of Players who " : "and ";
return (
`<div id="${id}">
<label class="ml-2 mt-0 mb-0">${select}${this.label}:</label>
<div class="mt-2 input-group input-row">
<div class="col-3">
<div class="input-group mb-2">
<div class="input-group-prepend"><div class="input-group-text">
<i class="far fa-calendar"></i>
</div></div>
<input id="${id}-afterdate" class="form-control" placeholder="${this.afterDate}" type="text"
onkeyup="setFilterOption('${id}', '${id}-afterdate', 'afterDate', isValidDate, correctDate)">
</div>
</div>
<div class="col-2">
<div class="input-group mb-2">
<div class="input-group-prepend"><div class="input-group-text">
<i class="far fa-clock"></i>
</div></div>
<input id="${id}-aftertime" class="form-control" placeholder="${this.afterTime}" type="text"
onkeyup="setFilterOption('${id}', '${id}-aftertime', 'afterTime', isValidTime, correctTime)">
</div>
</div>
<div class="col-auto">
<label class="mt-2 mb-0" for="inlineFormCustomSelectPref">&</label>
</div>
<div class="col-3">
<div class="input-group mb-2">
<div class="input-group-prepend"><div class="input-group-text">
<i class="far fa-calendar"></i>
</div></div>
<input id="${id}-beforedate" class="form-control" placeholder="${this.beforeDate}" type="text"
onkeyup="setFilterOption('${id}', '${id}-beforedate', 'beforeDate', isValidDate, correctDate)">
</div>
</div>
<div class="col-2">
<div class="input-group mb-2">
<div class="input-group-prepend"><div class="input-group-text">
<i class="far fa-clock"></i>
</div></div>
<input id="${id}-beforetime" class="form-control" placeholder="${this.beforeTime}" type="text"
onkeyup="setFilterOption('${id}', '${id}-beforetime', 'beforeTime', isValidTime, correctTime)">
</div>
</div>
<button class="filter-remover btn btn-outline-secondary float-right"
style="position: absolute;right: 0.8rem;"
onclick="removeFilter('${this.id}')"><i class="far fa-fw fa-trash-alt"></i></button>
</div>
</div>`
);
}
toObject() {
return {
kind: this.kind,
parameters: {
afterDate: this.afterDate,
afterTime: this.afterTime,
beforeDate: this.beforeDate,
beforeTime: this.beforeTime
}
}
}
}
class PlayedBetweenFilter extends BetweenDateFilter {
constructor(id, options) {
super(id, "playedBetween", "Played between", options);
}
}
class RegisteredBetweenFilter extends BetweenDateFilter {
constructor(id, options) {
super(id, "registeredBetween", "Registered between", options);
}
}
function createFilter(filter, id) {
switch (filter.kind) {
case "activityIndexNow":
return new ActivityIndexFilter(id, filter.options);
case "banned":
return new BannedFilter(id, filter.options);
case "operators":
return new OperatorsFilter(id, filter.options);
case "pluginGroups":
return new PluginGroupsFilter(id, filter.plugin, filter.group, filter.options);
case "playedBetween":
return new PlayedBetweenFilter(id, filter.options);
case "registeredBetween":
return new RegisteredBetweenFilter(id, filter.options);
default:
throw new Error("Unsupported filter kind: '" + filter.kind + "'");
}
}

View File

@ -192,15 +192,16 @@ function mapToDataSeries(performanceData) {
chunks: [],
disk: []
};
for (const entry of performanceData) {
for (let i = 0; i < performanceData.length; i++) {
const entry = performanceData[i];
const date = entry[0];
dataSeries.playersOnline.push([date, entry[1]]);
dataSeries.tps.push([date, entry[2]]);
dataSeries.cpu.push([date, entry[3]]);
dataSeries.ram.push([date, entry[4]]);
dataSeries.entities.push([date, entry[5]]);
dataSeries.chunks.push([date, entry[6]]);
dataSeries.disk.push([date, entry[7]]);
dataSeries.playersOnline[i] = [date, entry[1]];
dataSeries.tps[i] = [date, entry[2]];
dataSeries.cpu[i] = [date, entry[3]];
dataSeries.ram[i] = [date, entry[4]];
dataSeries.entities[i] = [date, entry[5]];
dataSeries.chunks[i] = [date, entry[6]];
dataSeries.disk[i] = [date, entry[7]];
}
return dataSeries;
}

View File

@ -0,0 +1,506 @@
const loadedFilters = [];
const queryState = {
filterCount: 0,
filters: [],
view: {
afterDate: null,
afterTime: null,
beforeDate: null,
beforeTime: null
},
invalidFormFields: {
ids: [],
setAsInvalid: function (id) {
if (this.ids.includes(id)) return;
this.ids.push(id);
queryState.updateQueryButton();
},
setAsValid: function (id) {
this.ids = this.ids.filter(invalidID => invalidID !== id);
queryState.updateQueryButton();
},
},
updateQueryButton: function () {
const queryButton = document.getElementById('query-button');
if (this.invalidFormFields.ids.length === 0) {
queryButton.removeAttribute('disabled');
queryButton.classList.remove('disabled');
} else {
queryButton.setAttribute('disabled', 'true');
queryButton.classList.add('disabled');
}
}
}
let timestamp = undefined;
function loadView(json) {
queryState.view = json.view;
document.getElementById('viewFromDateField').setAttribute('placeholder', json.view.afterDate);
document.getElementById('viewFromTimeField').setAttribute('placeholder', json.view.afterTime);
document.getElementById('viewToDateField').setAttribute('placeholder', json.view.beforeDate);
document.getElementById('viewToTimeField').setAttribute('placeholder', json.view.beforeTime);
const playersOnlineSeries = {
name: 'Players Online', type: 'areaspline', tooltip: {valueDecimals: 0},
data: json.viewPoints, color: '#9E9E9E', yAxis: 0
}
graphs.push(Highcharts.stockChart('viewChart', {
rangeSelector: {
selected: 3,
buttons: linegraphButtons
},
yAxis: {
softMax: 2,
softMin: 0
},
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
}
},
series: [playersOnlineSeries],
xAxis: {
events: {
afterSetExtremes: function (event) {
if (this) {
const afterDate = Highcharts.dateFormat('%d/%m/%Y', this.min);
const afterTime = Highcharts.dateFormat('%H:%M', this.min);
const beforeDate = Highcharts.dateFormat('%d/%m/%Y', this.max);
const beforeTime = Highcharts.dateFormat('%H:%M', this.max);
document.getElementById('viewFromDateField').value = afterDate;
document.getElementById('viewFromTimeField').value = afterTime;
document.getElementById('viewToDateField').value = beforeDate;
document.getElementById('viewToTimeField').value = beforeTime;
const dontUpdateGraph = true;
setViewOption('viewFromDateField', 'afterDate', isValidDate, correctDate, dontUpdateGraph);
setViewOption('viewFromTimeField', 'afterTime', isValidTime, correctTime, dontUpdateGraph);
setViewOption('viewToDateField', 'beforeDate', isValidDate, correctDate, dontUpdateGraph);
setViewOption('viewToTimeField', 'beforeTime', isValidTime, correctTime, dontUpdateGraph);
}
}
}
}
}));
}
function loadFilters(json) {
loadedFilters.push(...json.filters);
let filterElements = '';
for (let i = 0; i < loadedFilters.length; i++) {
filterElements += createFilterSelector('#filters', i, loadedFilters[i]);
}
document.getElementById('filter-dropdown').innerHTML = filterElements;
}
function addFilter(parentSelector, filterIndex) {
const id = "f" + queryState.filterCount;
const filter = createFilter(loadedFilters[filterIndex], id);
queryState.filters.push(filter);
const inserting = document.createElement("template");
inserting.innerHTML = filter.render(queryState.filterCount);
document.querySelector(parentSelector).appendChild(inserting.content);
queryState.filterCount++;
}
function removeFilter(filterIndex) {
document.getElementById(filterIndex).remove();
queryState.filters = queryState.filters.filter(f => f.id !== filterIndex);
}
function createFilterSelector(parent, index, filter) {
return `<a class="dropdown-item" href="javascript:void(0)" onclick="addFilter('${parent}', ${index})">${filter.kind}</a>`;
}
function isValidDate(value) {
if (!value) return true;
const d = value.match(
/^(0\d|\d{2})[\/|\-]?(0\d|\d{2})[\/|\-]?(\d{4,5})$/
);
if (!d) return false;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
const parsedDay = Number(d[1]);
const parsedMonth = Number(d[2]) - 1; // 0=January, 11=December
const parsedYear = Number(d[3]);
return new Date(parsedYear, parsedMonth, parsedDay);
}
function correctDate(value) {
const d = value.match(
/^(0\d|\d{2})[\/|\-]?(0\d|\d{2})[\/|\-]?(\d{4,5})$/
);
if (!d) return value;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
const parsedDay = Number(d[1]);
const parsedMonth = Number(d[2]) - 1; // 0=January, 11=December
const parsedYear = Number(d[3]);
const date = d ? new Date(parsedYear, parsedMonth, parsedDay) : null;
const day = `${date.getDate()}`;
const month = `${date.getMonth() + 1}`;
const year = `${date.getFullYear()}`;
return (
(day.length === 1 ? `0${day}` : day) +
"/" +
(month.length === 1 ? `0${month}` : month) +
"/" +
year
);
}
function isValidTime(value) {
if (!value) return true;
const regex = /^[0-2][0-9]:[0-5][0-9]$/;
return regex.test(value);
}
function correctTime(value) {
const d = value.match(/^(0\d|\d{2}):?(0\d|\d{2})$/);
if (!d) return value;
let hour = Number(d[1]);
while (hour > 23) hour--;
let minute = Number(d[2]);
while (minute > 59) minute--;
return (hour < 10 ? "0" + hour : hour) + ":" + (minute < 10 ? "0" + minute : minute);
}
function setViewOption(
elementId,
propertyName,
isValidFunction,
correctionFunction,
dontUpdateGraph
) {
const view = queryState.view;
const element = document.getElementById(elementId);
let value = element.value;
value = correctionFunction.apply(element, [value]);
element.value = value;
const isValid = isValidFunction.apply(element, [value]);
if (isValid) {
element.classList.remove("is-invalid");
view[propertyName] = value;
queryState.invalidFormFields.setAsValid(elementId);
if (!dontUpdateGraph) updateViewGraph();
} else {
element.classList.add("is-invalid");
queryState.invalidFormFields.setAsInvalid(elementId);
}
}
function setFilterOption(
id,
elementId,
propertyName,
isValidFunction,
correctionFunction,
dontUpdateGraph
) {
const query = queryState.filters.find(function (f) {
return f.id === id;
});
const element = document.getElementById(elementId);
let value = element.value;
value = correctionFunction.apply(element, [value]);
element.value = value;
const isValid = isValidFunction.apply(element, [value]);
if (isValid) {
element.classList.remove("is-invalid");
query[propertyName] = value;
queryState.invalidFormFields.setAsValid(elementId);
} else {
element.classList.add("is-invalid");
queryState.invalidFormFields.setAsInvalid(elementId);
}
}
function updateViewGraph() {
function parseTime(dateString, timeString) {
const d = dateString.match(
/^(0\d|\d{2})[\/|\-]?(0\d|\d{2})[\/|\-]?(\d{4,5})$/
);
const t = timeString.match(/^(0\d|\d{2}):?(0\d|\d{2})$/);
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
const parsedDay = Number(d[1]);
const parsedMonth = Number(d[2]) - 1; // 0=January, 11=December
const parsedYear = Number(d[3]);
let hour = Number(t[1]);
let minute = Number(t[2]);
const date = new Date(parsedYear, parsedMonth, parsedDay, hour, minute);
return date.getTime() - (date.getTimezoneOffset() * 60000);
}
const graph = graphs[0];
const min = parseTime(queryState.view.afterDate, queryState.view.afterTime);
const max = parseTime(queryState.view.beforeDate, queryState.view.beforeTime);
for (const axis of graph.xAxis) {
axis.setExtremes(min, max);
}
}
let query = [];
function performNewQuery() {
for (let filter of queryState.filters) {
query.push(filter.toObject());
}
runQuery();
}
function getQueryAddress() {
if (timestamp) return `./v1/query?timestamp=${timestamp}`;
const encodedQuery = encodeURIComponent(JSON.stringify(query));
const encodedView = encodeURIComponent(JSON.stringify(queryState.view));
return `./v1/query?q=${encodedQuery}&view=${encodedView}`;
}
function runQuery() {
const queryButton = document.getElementById('query-button');
queryButton.setAttribute('disabled', 'true');
queryButton.classList.add('disabled');
if (timestamp) {
document.querySelector('#content .tab').innerHTML =
`<div class="page-loader">
<span class="loader"></span>
<p class="loader-text">Loading..</p>
</div>`;
} else {
const icon = document.createElement('template');
icon.innerHTML = '<span class="loader"></span>'
queryButton.querySelector('.fa').replaceWith(icon.content);
}
jsonRequest(getQueryAddress(), function (json, error) {
const previousPath = document.getElementById('result-path');
if (previousPath) previousPath.remove();
if (json) {
if (json.data) {
displayResults(json);
displayResultPath(json);
} else if (json.path) {
// filters resulted in 0 players matched
displayResultPath(json);
// Reset query
queryButton.removeAttribute('disabled');
queryButton.classList.remove('disabled');
const icon = document.createElement('template');
icon.innerHTML = '<i class="fa fa-search"></i>'
queryButton.querySelector('.loader').replaceWith(icon.content);
query.splice(0, query.length);
} else {
// Cached query expired
window.history.replaceState({}, '', `${location.pathname}?error=${encodeURIComponent('Cached query has expired')}`);
location.reload();
}
} else if (error) {
window.history.replaceState({}, '', `${location.pathname}?error=${encodeURIComponent(error)}`);
location.reload();
}
});
}
function displayResultPath(json) {
const gotResults = Boolean(json.data);
let pathHtml = ``;
for (let i = 0; i < json.path.length; i++) {
const step = json.path[i];
pathHtml += `<p class="m-0">`;
for (let j = 0; j < i * 4; j++) {
pathHtml += "&nbsp;";
}
pathHtml += `<i class="fa fa-fw fa-filter"></i> ${step.kind} matched ${step.size} players</p>`
}
insertElementBefore('.tab .row .card', () => {
const element = document.createElement('div');
element.id = "result-path"
element.classList.add("alert", gotResults ? "alert-success" : "alert-warning", "shadow");
element.innerHTML = pathHtml;
return element;
});
window.scrollTo(0, 0); // Scroll to top
}
function displayResults(json) {
displayDataResultScreen(json.data.players.data.length, json.view ? json.view : {});
// Set URL so that the query result can be shared
window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`);
// Player table
$('.player-table').DataTable({
responsive: true,
columns: json.data.players.columns,
data: json.data.players.data,
order: [[5, "desc"]]
});
const activityIndexHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(2)");
const lastSeenHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(6)");
activityIndexHeader.innerHTML += ` (${json.view.beforeDate})`
lastSeenHeader.innerHTML += ` (view)`
// Activity graphs
const activity_data = json.data.activity;
activityPie('activityPie', {
name: 'Players', colorByPoint: true, data: activity_data.activity_pie_series
});
stackChart('activityStackGraph', activity_data.activity_labels, activity_data.activity_series, 'Players');
document.querySelector("#activity-date").innerHTML = json.view.beforeDate;
// Geolocations
const geolocation_data = json.data.geolocation;
const geolocationSeries = {
name: 'Players',
type: 'map',
mapData: Highcharts.maps['custom/world'],
data: geolocation_data.geolocation_series,
joinBy: ['iso-a3', 'code']
};
const geolocationBarSeries = {
color: geolocation_data.colors.bars,
name: 'Players',
data: geolocation_data.geolocation_bar_series.map(function (bar) {
return bar.value
})
};
const geolocationBarCategories = geolocation_data.geolocation_bar_series.map(function (bar) {
return bar.label
});
worldMap('worldMap', geolocation_data.colors.low, geolocation_data.colors.high, geolocationSeries);
horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], 'Players');
const session_data = json.data.sessions;
document.querySelector("#data_total_playtime").innerHTML = session_data.total_playtime;
document.querySelector("#data_average_playtime").innerHTML = session_data.average_playtime;
document.querySelector("#data_total_afk_playtime").innerHTML = session_data.total_afk_playtime;
document.querySelector("#data_average_afk_playtime").innerHTML = session_data.average_afk_playtime;
document.querySelector("#data_total_active_playtime").innerHTML = session_data.total_active_playtime;
document.querySelector("#data_average_active_playtime").innerHTML = session_data.average_active_playtime;
document.querySelector("#data_total_sessions").innerHTML = session_data.total_sessions;
document.querySelector("#data_average_sessions").innerHTML = session_data.average_sessions;
document.querySelector("#data_average_session_length").innerHTML = session_data.average_session_length;
}
function displayDataResultScreen(resultCount, view) {
const afterDate = queryState.view.afterDate ? queryState.view.afterDate : view.afterDate;
const beforeDate = queryState.view.beforeDate ? queryState.view.beforeDate : view.beforeDate;
const afterTime = queryState.view.afterTime ? queryState.view.afterTime : view.afterTime;
const beforeTime = queryState.view.beforeTime ? queryState.view.beforeTime : view.beforeTime;
graphs.splice(0, graphs.length); // Remove view graph from state as it is removed from DOM
document.querySelector('#content .tab').innerHTML =
`<div class="container-fluid mt-4">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800"><i class="sidebar-toggler fa fa-fw fa-bars" onclick="toggleSidebar()"></i>Plan &middot;
Query Results</h1>
<p class="mb-0 text-gray-800">(matched ${resultCount} players)</p>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-lg-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold col-black" title=" ${afterDate} ${afterTime} - ${beforeDate} ${beforeTime}"><i
class="fas fa-fw fa-users col-black"></i>
View: ${afterDate} - ${beforeDate}</h6>
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover player-table dataTable">
<tr>
<td>Loading..</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-8 col-lg-8 col-sm-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold col-black"><i
class="fas fa-fw fa-chart-line col-amber"></i>
Activity of matched players</h6>
</div>
<div class="chart-area" id="activityStackGraph"></div>
</div>
</div>
<div class="col-xl-4 col-lg-4 col-sm-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold col-black"><i
class="fa fa-fw fa-users col-amber"></i>
Activity on <span id="activity-date"></span></h6>
</div>
<div class="chart-area" id="activityPie"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-3 col-lg-3 col-sm-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold col-black"><i class="col-teal far fa-calendar"></i> Sessions within view</h6>
</div>
<div class="card-body" id="data_players">
<p><i class="col-teal far fa-fw fa-calendar-check"></i> Sessions<span
class="float-right"><b id="data_total_sessions"></b></span></p>
<p><i class="col-teal far fa-fw fa-calendar-check"></i> Average Sessions / Player<span
class="float-right"><b id="data_average_sessions"></b></span></p>
<p><i class="col-teal far fa-fw fa-clock"></i> Average Session Length<span
class="float-right" id="data_average_session_length"></span></p>
<hr>
<p><i class="col-green far fa-fw fa-clock"></i> Playtime<span
class="float-right" id="data_total_playtime"></span></p>
<p><i class="col-green far fa-fw fa-clock"></i> Active Playtime<span
class="float-right" id="data_total_active_playtime"></span></p>
<p><i class="col-grey far fa-fw fa-clock"></i> AFK Time<span
class="float-right" id="data_total_afk_playtime"></span></p>
<hr>
<p><i class="col-green far fa-fw fa-clock"></i> Average Playtime / Player<span
class="float-right" id="data_average_playtime"></span></p>
<p><i class="col-green far fa-fw fa-clock"></i> Average Active Playtime / Player<span
class="float-right" id="data_average_active_playtime"></span></p>
<p><i class="col-grey far fa-fw fa-clock"></i> Average AFK Time / Player<span
class="float-right" id="data_average_afk_playtime"></span></p>
</div>
</div>
</div>
<div class="col-xl-9 col-lg-9 col-sm-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold col-black"><i
class="fas fa-fw fa-globe col-green"></i>
Geolocations</h6>
</div>
<div class="chart-area row" style="height: 100%;">
<div class="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div id="countryBarChart"></div>
</div>
<div class="col-xs-12 col-sm-12 col-md-9 col-lg-9">
<div id="worldMap"></div>
</div>
</div>
</div>
</div>
</div>
</div>`;
}

View File

@ -19,7 +19,7 @@ function openTab(openIndex) {
const tabWidthPercent = -100 / tabCount;
const verticalScrollPercent = slideIndex * tabWidthPercent;
content.style.transition = "0.5s";
content.style.transform = "translate3d(" + verticalScrollPercent + "%,0px,0)";
content.style.transform = `translate3d(${verticalScrollPercent}%,0px,0)`;
}
function openPage() {
@ -43,10 +43,10 @@ function openPage() {
// Prepare tabs for display
content.style.transform = "translate3d(0px,0px,0)";
content.style.width = (tabCount * 100) + "%";
content.style.width = (Math.max(100, tabCount * 100)) + "%";
content.style.opacity = "1";
for (let tab of tabs) {
tab.style.width = (100 / tabCount) + "%";
tab.style.width = `${100 / tabCount}%`;
}
window.addEventListener('hashchange', openPage);
@ -81,17 +81,19 @@ function reduceSidebar() {
return;
}
const $body = $('body')
const closeModal = $('.sidebar-close-modal');
const body = document.querySelector('body');
const closeModal = document.querySelector('.sidebar-close-modal');
const isSidebarHidden = body.classList.contains('sidebar-hidden');
const isModalCloserHidden = closeModal.classList.contains('hidden');
if ($(window).width() < 1350) {
if (!$body.hasClass('sidebar-hidden')) $body.addClass('sidebar-hidden');
if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden');
if (!isSidebarHidden) body.classList.add('sidebar-hidden');
if (!isModalCloserHidden) closeModal.classList.add('hidden');
// Close any open menu accordions when window is resized
$('.sidebar .collapse').collapse('hide');
} else if ($(window).width() > 1400 && $body.hasClass('sidebar-hidden')) {
$body.removeClass('sidebar-hidden');
if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden');
} else if ($(window).width() > 1400 && isSidebarHidden) {
body.classList.remove('sidebar-hidden');
if (!isModalCloserHidden) closeModal.classList.add('hidden');
}
oldWidth = newWidth;
}
@ -100,15 +102,16 @@ reduceSidebar();
$(window).resize(reduceSidebar);
function toggleSidebar() {
$('body').toggleClass('sidebar-hidden');
document.querySelector('body').classList.toggle('sidebar-hidden');
$('.sidebar .collapse').collapse('hide');
const closeModal = $('.sidebar-close-modal');
const closeModal = document.querySelector('.sidebar-close-modal');
if ($(window).width() < 900) {
closeModal.toggleClass('hidden');
} else {
if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden');
closeModal.classList.toggle('hidden');
} else if (!closeModal.classList.contains('hidden')) {
closeModal.classList.add('hidden');
}
}
$('.sidebar-toggler,.sidebar-close-modal').on('click', toggleSidebar);
document.querySelectorAll('.sidebar-toggler,.sidebar-close-modal')
.forEach(element => element.addEventListener('click', toggleSidebar));

View File

@ -27,7 +27,7 @@
<script>
function login() {
msg.addClass('hidden');
errorElement.classList.add('hidden');
const user = $('#inputUser').val();
if (!user || user.length < 1) {
return displayError('You need to specify a Username');
@ -58,7 +58,6 @@
}
document.addEventListener('keydown', (event) => {
console.log(event.key);
if (event.key === 'Enter') login();
});
</script>
@ -200,8 +199,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -225,14 +223,14 @@
login();
});
const msg = $(`#fail-msg`);
const errorElement = document.getElementById("fail-msg");
function displayError(message) {
msg.text(message);
msg.removeClass('hidden');
errorElement.innerText = message;
errorElement.classList.remove('hidden');
}
$('#forgot-button').click(() => $('#forgotModal').modal());
document.getElementById('forgot-button').addEventListener('click', () => $('#forgotModal').modal());
</script>
</body>

View File

@ -40,28 +40,23 @@
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img alt="logo" class="w-22" src="./img/Flaticon_circle.png">
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<!-- Nav Item - Dashboard -->
<li class="nav-item nav-button active">
<a class="nav-link" href="#tab-network-overview">
<i class="fas fa-fw fa-info-circle"></i>
<span>Network Overview</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Information
</div>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a aria-controls="serverPages" aria-expanded="true" class="nav-link collapsed" data-target="#serverPages"
data-toggle="collapse" href="#">
@ -79,7 +74,7 @@
</div>
</div>
</li>
<!-- Nav Item - Pages Collapse Menu -->
<li class="nav-item">
<a aria-controls="playerbasePages" aria-expanded="true" class="nav-link collapsed"
data-target="#playerbasePages"
@ -100,20 +95,29 @@
</div>
</div>
</li>
<!-- Nav Item - Pages Collapse Menu -->
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Plugins
</div>
${navPluginsTabs}
<!-- Divider -->
<hr class="sidebar-divider">
<div class="query-buttons">
<div class="sidebar-heading">
LINKS
</div>
<li class="nav-item nav-button">
<a class="nav-link" href="./query">
<i class="fas fa-fw fa-search"></i>
<span>Make a query</span></a>
</li>
<hr class="sidebar-divider">
</div>
<div class="ml-3 align-items-center justify-content-between">
<button class="btn bg-plan" data-target="#colorChooserModal" data-toggle="modal" type="button">
<i class="fa fa-palette"></i>
@ -295,8 +299,8 @@
<td><span id="data_regular_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Player
</td>
<td id="data_average_playtime_before"></td>
<td id="data_average_playtime_after"></td>
@ -511,43 +515,40 @@
<td><span id="data_regular_players_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Player
</td>
<td id="data_playtime_avg_then"></td>
<td id="data_playtime_avg_now"></td>
<td><span id="data_playtime_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-grey far fa-fw fa-clock"></i> AFK /
Player
<td><i class="col-grey far fa-fw fa-clock"></i>
AFK / Player
</td>
<td id="data_afk_then"></td>
<td id="data_afk_now"></td>
<td><span id="data_afk_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Regular
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Regular Player
</td>
<td id="data_regular_playtime_avg_then"></td>
<td id="data_regular_playtime_avg_now"></td>
<td><span id="data_regular_playtime_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-teal far fa-fw fa-clock"></i> Average Session Length /
Regular
Player
<td><i class="col-teal far fa-fw fa-clock"></i>
Average Session Length / Regular Player
</td>
<td id="data_regular_session_avg_then"></td>
<td id="data_regular_session_avg_now"></td>
<td><span id="data_regular_session_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-grey far fa-fw fa-clock"></i> AFK /
Regular
Player
<td><i class="col-grey far fa-fw fa-clock"></i>
AFK / Regular Player
</td>
<td id="data_regular_afk_then"></td>
<td id="data_regular_afk_now"></td>
@ -704,8 +705,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -733,8 +733,8 @@
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i> Plan Wiki,
Tutorials & Documentation</a>
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"
@ -946,7 +946,7 @@
joinBy: ['iso-a3', 'code']
};
var geolocationBarSeries = {
color: '#4CAF50',
color: json.colors.bars,
name: 'Players',
data: json.geolocation_bar_series.map(function (bar) {
return bar.value
@ -955,7 +955,7 @@
var geolocationBarCategories = json.geolocation_bar_series.map(function (bar) {
return bar.label
});
worldMap('worldMap', v.colors.geolocationsLow, v.colors.geolocationsHigh, geolocationSeries);
worldMap('worldMap', json.colors.low, json.colors.high, geolocationSeries);
horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], 'Players');
} else if (error) {
$('#worldMap').text("Failed to load graph data: " + error);

View File

@ -40,12 +40,10 @@
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img alt="logo" class="w-22" src="../img/Flaticon_circle.png">
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button active">
@ -54,7 +52,6 @@
<span>Player Overview</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="sidebar-heading">
@ -73,7 +70,6 @@
<span>PvP & PvE</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="sidebar-heading">
@ -99,7 +95,6 @@
</div>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="ml-3 align-items-center justify-content-between">
@ -611,8 +606,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -640,8 +634,8 @@
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i> Plan Wiki,
Tutorials & Documentation</a>
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"

View File

@ -31,12 +31,10 @@
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img alt="logo" class="w-22" src="img/Flaticon_circle.png">
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button active">
@ -45,9 +43,21 @@
<span>to main page</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="query-buttons">
<div class="sidebar-heading">
LINKS
</div>
<li class="nav-item nav-button">
<a class="nav-link" href="./query">
<i class="fas fa-fw fa-search"></i>
<span>Make a query</span></a>
</li>
<hr class="sidebar-divider">
</div>
<div class="ml-3 align-items-center justify-content-between">
<button class="btn bg-plan" data-target="#colorChooserModal" data-toggle="modal" type="button">
<i class="fa fa-palette"></i>
@ -161,8 +171,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -190,8 +199,8 @@
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i> Plan Wiki,
Tutorials & Documentation</a>
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"

View File

@ -0,0 +1,362 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="Player Analytics, query player data" name="description">
<meta content="Rsl1122" name="author">
<title>Plan | Query</title>
<!-- Custom fonts for this template-->
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet">
<link crossorigin="anonymous" href="https://fonts.googleapis.com/css?family=Nunito:300,400,600,700,800,900"
rel="stylesheet">
<!-- Custom styles for this template-->
<link href="css/sb-admin-2.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
</head>
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img class="w-22" src="img/Flaticon_circle.png">
</a>
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button">
<a class="nav-link" href="./query">
<i class="fas fa-fw fa-undo"></i>
<span>Make another query</span>
</a>
</li>
<li class="nav-item nav-button">
<a class="nav-link" href="/">
<i class="far fa-fw fa-hand-point-left"></i>
<span>to main page</span></a>
</li>
<hr class="sidebar-divider">
<div class="ml-3 align-items-center justify-content-between">
<button class="btn bg-plan" data-target="#colorChooserModal" data-toggle="modal" type="button">
<i class="fa fa-palette"></i>
</button>
<button class="btn bg-plan" data-target="#informationModal" data-toggle="modal" type="button">
<i class="fa fa-fw fa-question-circle"></i>
</button>
</div>
<div class="ml-3 align-items-center justify-content-between">
${version}
</div>
</ul>
<!-- End of Sidebar -->
<!-- Content Wrapper -->
<div class="d-flex flex-column" id="content-wrapper">
<div class="sidebar-close-modal hidden"></div>
<!-- Main Content -->
<div id="content" style="display: flex;">
<div class="tab" style="width: 100%;">
<div class="container-fluid mt-4">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800"><i class="sidebar-toggler fa fa-fw fa-bars"></i>Plan &middot;
Query</h1>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-lg-12">
<div class="card shadow mb-4">
<div class="card-body" id="data_player_info">
<div class="hidden alert alert-danger" id="fail-msg">
Failed
</div>
<label class="mt-2 mb-0" for="viewFromDateField">Show a view</label>
<div class="mt-2 input-group input-row">
<div class="col-auto">
<label class="mt-2 mb-0" for="viewFromDateField">from</label>
</div>
<div class="col-3">
<label class="sr-only" for="viewFromDateField">Date: DD/MM/YYYY</label>
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="far fa-calendar"></i></div>
</div>
<input class="form-control" id="viewFromDateField"
onkeyup="setViewOption('viewFromDateField', 'afterDate', isValidDate, correctDate)"
placeholder="31/12/2016"
type="text">
</div>
</div>
<div class="col-2">
<label class="sr-only" for="viewFromTimeField">Time: H H : M M</label>
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="far fa-clock"></i></div>
</div>
<input class="form-control" id="viewFromTimeField"
onkeyup="setViewOption('viewFromTimeField', 'afterTime', isValidTime, correctTime)"
placeholder="23:59"
type="text">
</div>
</div>
<div class="col-auto">
<label class="mt-2 mb-0">to</label>
</div>
<div class="col-3">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="far fa-calendar"></i></div>
</div>
<input class="form-control" id="viewToDateField"
onkeyup="setViewOption('viewToDateField', 'beforeDate', isValidDate, correctDate)"
placeholder="23/03/2020"
type="text">
</div>
</div>
<div class="col-2">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="far fa-clock"></i></div>
</div>
<input class="form-control" id="viewToTimeField"
onkeyup="setViewOption('viewToTimeField', 'beforeTime', isValidTime, correctTime)"
placeholder="21:26"
type="text">
</div>
</div>
</div>
<div class="chart-area" id="viewChart"><span class="loader"></span></div>
<hr>
<div id="filters"></div>
<div class="mt-2 dropdown" id="addFilter">
<button aria-expanded="false" aria-haspopup="true" class="btn dropdown-toggle"
data-toggle="dropdown" id="filterDropdown" type="button">
<i class="fa fa-plus"></i> Add a filter..
</button>
<div aria-labelledby="filterDropdown" class="dropdown-menu"
id="filter-dropdown">
<a class="dropdown-item" id="dropdown-loading">Loading filters..</a>
</div>
</div>
</div>
<button class="m-2 btn bg-plan" id="query-button" onclick="performNewQuery()"><i
class="fa fa-search"></i> Perform Query!
</button>
</div>
</div>
</div>
</div>
</div> <!-- /.container-fluid -->
</div> <!-- End of tab -->
</div> <!-- End of Main Content -->
</div><!-- End of Content Wrapper -->
<!-- Color Chooser Modal -->
<div aria-hidden="true" aria-labelledby="colorChooserModalLabel" class="modal fade" id="colorChooserModal"
role="dialog" tabindex="-1">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="colorChooserModalLabel"><i class="fa fa-palette"></i> Theme Select
</h5>
<button aria-label="Close" class="close" data-dismiss="modal" type="button">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<button class="btn color-chooser" id="choose-plan" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-red" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-pink" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-purple" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-deep-purple" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-indigo" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-blue" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-light-blue" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-cyan" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-teal" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-green" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-light-green" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-lime" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-yellow" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-amber" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-orange" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-deep-orange" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-brown" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-grey" type="button"><i
class="fa fa-palette"></i></button>
<button class="btn color-chooser" id="choose-blue-grey" type="button"><i
class="fa fa-palette"></i></button>
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
</div>
</div>
</div>
<!-- Information Modal -->
<div aria-hidden="true" aria-labelledby="informationModalLabel" class="modal fade" id="informationModal"
role="dialog" tabindex="-1">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="informationModalLabel"><i class="fa fa-fw fa-question-circle"></i>
Information about the plugin
</h5>
<button aria-label="Close" class="close" data-dismiss="modal" type="button">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Player Analytics is developed and licensed under <a
href="https://opensource.org/licenses/LGPL-3.0" rel="noopener noreferrer"
target="_blank">Lesser General Public License
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"
target="_blank"><i class="fab fa-fw fa-discord"></i> General Support on Discord</a>
<hr>
<p>Player Analytics is developed by Rsl1122.</p>
<p>In addition following <span class="col-plan">awesome people</span> have contributed:</p>
<ul class="row contributors">
${contributors}
<li>& Bug reporters!</li>
</ul>
<small><i class="fa fa-fw fa-code"></i> code contributor <i class="fa fa-fw fa-language"></i>
translator
</small>
<hr>
<p class="col-plan">Extra special thanks to those who have monetarily supported the development.
<i class="fa fa-fw fa-star col-amber"></i></p>
<hr>
<h6>bStats Metrics</h6>
<a class="btn col-plan" href="https://bstats.org/plugin/bukkit/Plan" rel="noopener noreferrer"
target="_blank"><i class="fa fa-fw fa-chart-area"></i> Bukkit</a>
<a class="btn col-plan" href="https://bstats.org/plugin/bungeecord/Plan"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-chart-area"></i>
BungeeCord</a>
<a class="btn col-plan" href="https://bstats.org/plugin/sponge/plan" rel="noopener noreferrer"
target="_blank"><i class="fa fa-fw fa-chart-area"></i> Sponge</a>
</div>
<div class="modal-footer">
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
</div>
</div>
</div>
<!-- Update Modal -->
<div aria-hidden="true" aria-labelledby="updateModalLabel" class="modal fade" id="updateModal" role="dialog"
tabindex="-1">
<div class="modal-dialog" role="document">
<div class="modal-content">
${updateModal}
<div class="modal-footer">
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
</div>
</div>
</div>
<!-- End of Page Wrapper -->
<!-- Bootstrap core JavaScript-->
<script src="./vendor/jquery/jquery.min.js"></script>
<script src="./vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Page level plugins -->
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
<script src="./vendor/highcharts/highstock.js"></script>
<script src="./vendor/highcharts/map.js"></script>
<script src="./vendor/highcharts/world.js"></script>
<!-- Custom scripts for all pages-->
<script src="./js/sb-admin-2.js"></script>
<script src="./js/xmlhttprequests.js"></script>
<script src="./js/color-selector.js"></script>
<script src="./js/domUtils.js"></script>
<!-- Page level custom scripts -->
<script src="./js/graphs.js"></script>
<script src='./js/filters.js'></script>
<script src='./js/query.js'></script>
<script id="mainScript">
if (location.search.includes("error=")) {
insertElementBefore('.tab .row .card div', () => {
const element = document.createElement('alert');
element.classList.add("alert", "alert-danger", "alert-dismissable", "show");
element.innerHTML = `<span id="error-text"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>`
return element;
});
document.getElementById('error-text').innerText = new URLSearchParams(location.search).get("error");
}
if (location.search.includes('timestamp=')) {
const parameters = new URLSearchParams(location.search);
timestamp = parameters.get('timestamp');
runQuery();
} else {
jsonRequest("./v1/filters", function (json, error) {
if (json) {
loadFilters(json);
loadView(json);
} else if (error) {
const errorElement = document.getElementById("fail-msg");
errorElement.innerText = error;
errorElement.classList.remove('hidden');
}
});
}
</script>
</body>
</html>

View File

@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<meta content="Player Analytics, login page" name="description">
<meta content="Player Analytics, register page" name="description">
<meta content="Rsl1122" name="author">
<meta content="noindex, nofollow" name="robots">
@ -99,8 +99,7 @@
</button>
</div>
<div class="modal-body bg-white">
<p>You can now finish registering the user. The registration should be completed now or within 15
minutes of closing this page.</p>
<p>You can now finish registering the user. Code expires in 15 minutes.</p>
<p>Use the following command in game to finish registration:</p>
<p><code>/${command} register --code <span class="register-code"></span></code></p>
<p>Or using console:</p>
@ -166,8 +165,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -184,11 +182,11 @@
<script src="js/color-selector.js"></script>
<script id="mainScript">
const msg = $(`#fail-msg`);
const errorElement = document.getElementById("fail-msg");
function displayError(message) {
msg.text(message);
msg.removeClass('hidden');
errorElement.innerText = message;
errorElement.classList.remove('hidden');
}
function checkIfRegistered(code) {
@ -204,8 +202,8 @@
})
}
$('#register-button').click(() => {
msg.addClass('hidden');
document.getElementById('register-button').addEventListener('click', () => {
errorElement.classList.add('hidden');
const user = $('#inputUser').val();
if (!user || user.length < 1) {
return displayError('You need to specify a Username');

View File

@ -38,12 +38,10 @@
<!-- Sidebar -->
<ul class="navbar-nav bg-plan sidebar sidebar-dark accordion" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center">
<img alt="logo" class="w-22" src="../img/Flaticon_circle.png">
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button active">
@ -52,7 +50,6 @@
<span>Server Overview</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="sidebar-heading">
@ -109,7 +106,6 @@
<span>Performance</span></a>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<div class="sidebar-heading">
@ -117,9 +113,21 @@
</div>
${navPluginsTabs}
<!-- Divider -->
<hr class="sidebar-divider">
<div class="query-buttons">
<div class="sidebar-heading">
LINKS
</div>
<li class="nav-item nav-button">
<a class="nav-link" href="../query">
<i class="fas fa-fw fa-search"></i>
<span>Make a query</span></a>
</li>
<hr class="sidebar-divider">
</div>
<div class="ml-3 align-items-center justify-content-between">
<button class="btn bg-plan" data-target="#colorChooserModal" data-toggle="modal" type="button">
<i class="fa fa-palette"></i>
@ -276,8 +284,8 @@
<td><span id="data_regular_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Player
</td>
<td id="data_average_playtime_before"></td>
<td id="data_average_playtime_after"></td>
@ -349,8 +357,8 @@
<li class="nav-item">
<a aria-controls="contact" aria-selected="false" class="nav-link col-black"
data-toggle="tab" href="#punchcard-tab" id="online-punchcard-tab"
role="tab"><i class="fa fa-fw fa-braille col-black"></i> Punchcard
for 30 days</a>
role="tab"><i class="fa fa-fw fa-braille col-black"></i>
Punchcard for 30 days</a>
</li>
</ul>
<div class="tab-content" id="onlineActivityTabContent">
@ -425,16 +433,16 @@
<td id="data_new_players_24h"></td>
</tr>
<tr>
<td><i class="col-light-green fa fa-fw fa-user-plus"></i> New Players /
Day
<td><i class="col-light-green fa fa-fw fa-user-plus"></i>
New Players / Day
</td>
<td id="data_new_players_30d_avg"></td>
<td id="data_new_players_7d_avg"></td>
<td id="data_new_players_24h_avg"></td>
</tr>
<tr>
<td><i class="col-light-green fa fa-fw fa-user-circle"></i> New Player
Retention
<td><i class="col-light-green fa fa-fw fa-user-circle"></i>
New Player Retention
</td>
<td id="data_new_players_retention_30d"></td>
<td id="data_new_players_retention_7d"></td>
@ -449,8 +457,8 @@
<td id="data_playtime_24h"></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Day
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Day
</td>
<td id="data_playtime_30d_avg"></td>
<td id="data_playtime_7d_avg"></td>
@ -482,16 +490,15 @@
Insights for 30 days</h6>
</div>
<div class="card-body" id="data_insights">
<p><i class="col-light-green fa fa-fw fa-user-friends"></i> Players online on
first
join<span class="float-right"><b><span
id="data_players_first_join_avg"></span></b></span></p>
<p><i class="col-light-green fa fa-fw fa-user-clock"></i> Average First session
length<span class="float-right"><b><span
id="data_first_session_length_avg"></span></b></span></p>
<p><i class="col-light-green fa fa-fw fa-user-clock"></i> Median First session
length<span class="float-right"><b><span
id="data_first_session_length_median"></span></b></span></p>
<p><i class="col-light-green fa fa-fw fa-user-friends"></i>
Players online on first join<span class="float-right"><b>
<span id="data_players_first_join_avg"></span></b></span></p>
<p><i class="col-light-green fa fa-fw fa-user-clock"></i>
Average First session length<span class="float-right"><b>
<span id="data_first_session_length_avg"></span></b></span></p>
<p><i class="col-light-green fa fa-fw fa-user-clock"></i>
Median First session length<span class="float-right"><b>
<span id="data_first_session_length_median"></span></b></span></p>
<p><i class="col-teal far fa-fw fa-calendar"></i> Lone joins<span
class="float-right"><b><span id="data_lone_joins"></span></b>
</span></p>
@ -765,43 +772,40 @@
<td><span id="data_regular_players_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Player
</td>
<td id="data_playtime_avg_then"></td>
<td id="data_playtime_avg_now"></td>
<td><span id="data_playtime_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-grey far fa-fw fa-clock"></i> AFK /
Player
<td><i class="col-grey far fa-fw fa-clock"></i>
AFK / Player
</td>
<td id="data_afk_then"></td>
<td id="data_afk_now"></td>
<td><span id="data_afk_trend"></span></td>
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Regular
Player
<td><i class="col-green far fa-fw fa-clock"></i>
Average Playtime / Regular Player
</td>
<td id="data_regular_playtime_avg_then"></td>
<td id="data_regular_playtime_avg_now"></td>
<td><span id="data_regular_playtime_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-teal far fa-fw fa-clock"></i> Average Session Length /
Regular
Player
<td><i class="col-teal far fa-fw fa-clock"></i>
Average Session Length / Regular Player
</td>
<td id="data_regular_session_avg_then"></td>
<td id="data_regular_session_avg_now"></td>
<td><span id="data_regular_session_avg_trend"></span></td>
</tr>
<tr>
<td><i class="col-grey far fa-fw fa-clock"></i> AFK /
Regular
Player
<td><i class="col-grey far fa-fw fa-clock"></i>
AFK / Regular Player
</td>
<td id="data_regular_afk_then"></td>
<td id="data_regular_afk_now"></td>
@ -958,15 +962,14 @@
<a aria-controls="contact" aria-selected="false" class="nav-link col-black"
data-toggle="tab" href="#cpuram" id="performance-hardware-tab"
role="tab"><i
class="fa fa-fw fa-microchip col-light-green"></i> CPU
& RAM</a>
class="fa fa-fw fa-microchip col-light-green"></i>
CPU & RAM</a>
</li>
<li class="nav-item">
<a aria-controls="contact" aria-selected="false" class="nav-link col-black"
data-toggle="tab" href="#worldload" id="performance-minecraft-tab"
role="tab"><i class="fa fa-fw fa-map col-purple"></i>
World
Load</a>
World Load</a>
</li>
<li class="nav-item">
<a aria-controls="contact" aria-selected="false" class="nav-link col-black"
@ -1012,7 +1015,7 @@
</div>
<div class="row">
<!-- Online Activity as Numbers -->
<!-- Performance as Numbers -->
<div class="col-lg-8 mb-8 col-sm-12">
<div class="card shadow mb-4">
<div class="card-header py-3">
@ -1029,16 +1032,16 @@
</thead>
<tbody>
<tr>
<td><i class="fa fa-fw fa-exclamation-circle col-red"></i> Low TPS
Spikes
<td><i class="fa fa-fw fa-exclamation-circle col-red"></i>
Low TPS Spikes
</td>
<td id="data_low_tps_spikes_30d"></td>
<td id="data_low_tps_spikes_7d"></td>
<td id="data_low_tps_spikes_24h"></td>
</tr>
<tr>
<td><i class="fa fa-fw fa-power-off col-red"></i> Server Downtime (No
Data)
<td><i class="fa fa-fw fa-power-off col-red"></i>
Server Downtime (No Data)
</td>
<td id="data_server_downtime_30d"></td>
<td id="data_server_downtime_7d"></td>
@ -1089,7 +1092,7 @@
</tbody>
</table>
</div>
</div> <!-- End of Online Activity as numbers-->
</div> <!-- End of Performance as numbers-->
<!-- Insights -->
<div class="col-lg-4 mb-4 col-sm-12">
<div class="card shadow mb-4">
@ -1176,8 +1179,7 @@
</div>
<div class="modal-footer">
<button class="btn" id="night-mode-toggle" type="button"><i class="fa fa-fw fa-cloud-moon"></i>
Night
Mode
Night Mode
</button>
<button class="btn bg-plan" data-dismiss="modal" type="button">OK</button>
</div>
@ -1205,8 +1207,8 @@
v3.0</a></p>
<hr>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i> Plan Wiki,
Tutorials & Documentation</a>
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-graduation-cap"></i>
Plan Wiki, Tutorials & Documentation</a>
<a class="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"><i class="fa fa-fw fa-bug"></i> Report Issues</a>
<a class="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"
@ -1302,7 +1304,7 @@
});
// HighCharts Series
var s = {
const s = {
name: {
playersOnline: 'Players Online',
uniquePlayers: 'Unique Players',
@ -1539,7 +1541,7 @@
// Wrapping this in a 0ms setTimeout waits for all other event handlers
// to finish. We need this because if the calendar is rendered
// immediately, it renders for a width of 0.
setTimeout(function() {
setTimeout(function () {
window.calendars.online_activity.render();
}, 0);
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/*
Highcharts JS v7.1.2 (2019-06-03)
Highcharts JS v8.2.2 (2020-10-22)
Highcharts Drilldown module
@ -7,24 +7,26 @@
License: www.highcharts.com/license
*/
(function(f){"object"===typeof module&&module.exports?(f["default"]=f,module.exports=f):"function"===typeof define&&define.amd?define("highcharts/modules/drilldown",["highcharts"],function(n){f(n);f.Highcharts=n;return f}):f("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(f){function n(d,f,n,u){d.hasOwnProperty(f)||(d[f]=u.apply(null,n))}f=f?f._modules:{};n(f,"modules/drilldown.src.js",[f["parts/Globals.js"]],function(d){var f=d.animObject,n=d.noop,u=d.color,x=d.defaultOptions,q=d.extend,
C=d.format,y=d.objectEach,t=d.pick,m=d.Chart,p=d.seriesTypes,z=p.pie,p=p.column,A=d.Tick,v=d.fireEvent,B=1;q(x.lang,{drillUpText:"\u25c1 Back to {series.name}"});x.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#003399",fontWeight:"bold",textDecoration:"underline"},activeDataLabelStyle:{cursor:"pointer",color:"#003399",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};d.SVGRenderer.prototype.Element.prototype.fadeIn=
function(a){this.attr({opacity:.1,visibility:"inherit"}).animate({opacity:t(this.newOpacity,1)},a||{duration:250})};m.prototype.addSeriesAsDrilldown=function(a,b){this.addSingleSeriesAsDrilldown(a,b);this.applyDrilldown()};m.prototype.addSingleSeriesAsDrilldown=function(a,b){var c=a.series,g=c.xAxis,e=c.yAxis,k,h=[],r=[],l,f,m;m=this.styledMode?{colorIndex:t(a.colorIndex,c.colorIndex)}:{color:a.color||c.color};this.drilldownLevels||(this.drilldownLevels=[]);l=c.options._levelNumber||0;(f=this.drilldownLevels[this.drilldownLevels.length-
1])&&f.levelNumber!==l&&(f=void 0);b=q(q({_ddSeriesId:B++},m),b);k=c.points.indexOf(a);c.chart.series.forEach(function(a){a.xAxis!==g||a.isDrilling||(a.options._ddSeriesId=a.options._ddSeriesId||B++,a.options._colorIndex=a.userOptions._colorIndex,a.options._levelNumber=a.options._levelNumber||l,f?(h=f.levelSeries,r=f.levelSeriesOptions):(h.push(a),r.push(a.options)))});a=q({levelNumber:l,seriesOptions:c.options,levelSeriesOptions:r,levelSeries:h,shapeArgs:a.shapeArgs,bBox:a.graphic?a.graphic.getBBox():
{},color:a.isNull?(new d.Color(u)).setOpacity(0).get():u,lowerSeriesOptions:b,pointOptions:c.options.data[k],pointIndex:k,oldExtremes:{xMin:g&&g.userMin,xMax:g&&g.userMax,yMin:e&&e.userMin,yMax:e&&e.userMax},resetZoomButton:this.resetZoomButton},m);this.drilldownLevels.push(a);g&&g.names&&(g.names.length=0);b=a.lowerSeries=this.addSeries(b,!1);b.options._levelNumber=l+1;g&&(g.oldPos=g.pos,g.userMin=g.userMax=null,e.userMin=e.userMax=null);c.type===b.type&&(b.animate=b.animateDrilldown||n,b.options.animation=
!0)};m.prototype.applyDrilldown=function(){var a=this.drilldownLevels,b;a&&0<a.length&&(b=a[a.length-1].levelNumber,this.drilldownLevels.forEach(function(a){a.levelNumber===b&&a.levelSeries.forEach(function(a){a.options&&a.options._levelNumber===b&&a.remove(!1)})}));this.resetZoomButton&&(this.resetZoomButton.hide(),delete this.resetZoomButton);this.pointer.reset();this.redraw();this.showDrillUpButton();v(this,"afterDrilldown")};m.prototype.getDrilldownBackText=function(){var a=this.drilldownLevels;
if(a&&0<a.length)return a=a[a.length-1],a.series=a.seriesOptions,C(this.options.lang.drillUpText,a)};m.prototype.showDrillUpButton=function(){var a=this,b=this.getDrilldownBackText(),c=a.options.drilldown.drillUpButton,g,e;this.drillUpButton?this.drillUpButton.attr({text:b}).align():(e=(g=c.theme)&&g.states,this.drillUpButton=this.renderer.button(b,null,null,function(){a.drillUp()},g,e&&e.hover,e&&e.select).addClass("highcharts-drillup-button").attr({align:c.position.align,zIndex:7}).add().align(c.position,
!1,c.relativeTo||"plotBox"))};m.prototype.drillUp=function(){if(this.drilldownLevels&&0!==this.drilldownLevels.length){for(var a=this,b=a.drilldownLevels,c=b[b.length-1].levelNumber,g=b.length,e=a.series,k,h,d,l,f=function(b){var c;e.forEach(function(a){a.options._ddSeriesId===b._ddSeriesId&&(c=a)});c=c||a.addSeries(b,!1);c.type===d.type&&c.animateDrillupTo&&(c.animate=c.animateDrillupTo);b===h.seriesOptions&&(l=c)};g--;)if(h=b[g],h.levelNumber===c){b.pop();d=h.lowerSeries;if(!d.chart)for(k=e.length;k--;)if(e[k].options.id===
h.lowerSeriesOptions.id&&e[k].options._levelNumber===c+1){d=e[k];break}d.xData=[];h.levelSeriesOptions.forEach(f);v(a,"drillup",{seriesOptions:h.seriesOptions});l.type===d.type&&(l.drilldownLevel=h,l.options.animation=a.options.drilldown.animation,d.animateDrillupFrom&&d.chart&&d.animateDrillupFrom(h));l.options._levelNumber=c;d.remove(!1);l.xAxis&&(k=h.oldExtremes,l.xAxis.setExtremes(k.xMin,k.xMax,!1),l.yAxis.setExtremes(k.yMin,k.yMax,!1));h.resetZoomButton&&(a.resetZoomButton=h.resetZoomButton,
a.resetZoomButton.show())}this.redraw();0===this.drilldownLevels.length?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align();this.ddDupes.length=[];v(a,"drillupall")}};m.prototype.callbacks.push(function(){var a=this;a.drilldown={update:function(b,c){d.merge(!0,a.options.drilldown,b);t(c,!0)&&a.redraw()}}});d.addEvent(m,"beforeShowResetZoom",function(){if(this.drillUpButton)return!1});d.addEvent(m,"render",function(){(this.xAxis||[]).forEach(function(a){a.ddPoints=
{};a.series.forEach(function(b){var c,g=b.xData||[],e=b.points,d;for(c=0;c<g.length;c++)d=b.options.data[c],"number"!==typeof d&&(d=b.pointClass.prototype.optionsToObject.call({series:b},d),d.drilldown&&(a.ddPoints[g[c]]||(a.ddPoints[g[c]]=[]),a.ddPoints[g[c]].push(e?e[c]:!0)))});y(a.ticks,A.prototype.drillable)})});p.prototype.animateDrillupTo=function(a){if(!a){var b=this,c=b.drilldownLevel;this.points.forEach(function(a){var b=a.dataLabel;a.graphic&&a.graphic.hide();b&&(b.hidden="hidden"===b.attr("visibility"),
b.hidden||(b.hide(),a.connector&&a.connector.hide()))});d.syncTimeout(function(){b.points&&b.points.forEach(function(a,b){b=b===(c&&c.pointIndex)?"show":"fadeIn";var g="show"===b?!0:void 0,d=a.dataLabel;if(a.graphic)a.graphic[b](g);d&&!d.hidden&&(d.fadeIn(),a.connector&&a.connector.fadeIn())})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=n}};p.prototype.animateDrilldown=function(a){var b=this,c=this.chart,d=c.drilldownLevels,e,k=f(c.options.drilldown.animation),h=
this.xAxis,r=c.styledMode;a||(d.forEach(function(a){b.options._ddSeriesId===a.lowerSeriesOptions._ddSeriesId&&(e=a.shapeArgs,r||(e.fill=a.color))}),e.x+=t(h.oldPos,h.pos)-h.pos,this.points.forEach(function(a){var c=a.shapeArgs;r||(c.fill=a.color);a.graphic&&a.graphic.attr(e).animate(q(a.shapeArgs,{fill:a.color||b.color}),k);a.dataLabel&&a.dataLabel.fadeIn(k)}),this.animate=null)};p.prototype.animateDrillupFrom=function(a){var b=f(this.chart.options.drilldown.animation),c=this.group,g=c!==this.chart.columnGroup,
e=this;e.trackerGroups.forEach(function(a){if(e[a])e[a].on("mouseover")});g&&delete this.group;this.points.forEach(function(k){var h=k.graphic,f=a.shapeArgs,l=function(){h.destroy();c&&g&&(c=c.destroy())};h&&(delete k.graphic,e.chart.styledMode||(f.fill=a.color),b.duration?h.animate(f,d.merge(b,{complete:l})):(h.attr(f),l()))})};z&&q(z.prototype,{animateDrillupTo:p.prototype.animateDrillupTo,animateDrillupFrom:p.prototype.animateDrillupFrom,animateDrilldown:function(a){var b=this.chart.drilldownLevels[this.chart.drilldownLevels.length-
1],c=this.chart.options.drilldown.animation,g=b.shapeArgs,e=g.start,k=(g.end-e)/this.points.length,f=this.chart.styledMode;a||(this.points.forEach(function(a,h){var l=a.shapeArgs;f||(g.fill=b.color,l.fill=a.color);if(a.graphic)a.graphic.attr(d.merge(g,{start:e+h*k,end:e+(h+1)*k}))[c?"animate":"attr"](l,c)}),this.animate=null)}});d.Point.prototype.doDrilldown=function(a,b,c){var d=this.series.chart,e=d.options.drilldown,f=(e.series||[]).length,h;d.ddDupes||(d.ddDupes=[]);for(;f--&&!h;)e.series[f].id===
this.drilldown&&-1===d.ddDupes.indexOf(this.drilldown)&&(h=e.series[f],d.ddDupes.push(this.drilldown));v(d,"drilldown",{point:this,seriesOptions:h,category:b,originalEvent:c,points:void 0!==b&&this.series.xAxis.getDDPoints(b).slice(0)},function(b){var c=b.point.series&&b.point.series.chart,d=b.seriesOptions;c&&d&&(a?c.addSingleSeriesAsDrilldown(b.point,d):c.addSeriesAsDrilldown(b.point,d))})};d.Axis.prototype.drilldownCategory=function(a,b){y(this.getDDPoints(a),function(c){c&&c.series&&c.series.visible&&
c.doDrilldown&&c.doDrilldown(!0,a,b)});this.chart.applyDrilldown()};d.Axis.prototype.getDDPoints=function(a){return this.ddPoints&&this.ddPoints[a]};A.prototype.drillable=function(){var a=this.pos,b=this.label,c=this.axis,g="xAxis"===c.coll&&c.getDDPoints,e=g&&c.getDDPoints(a),f=c.chart.styledMode;g&&(b&&e&&e.length?(b.drillable=!0,b.basicStyles||f||(b.basicStyles=d.merge(b.styles)),b.addClass("highcharts-drilldown-axis-label").on("click",function(b){c.drilldownCategory(a,b)}),f||b.css(c.chart.options.drilldown.activeAxisLabelStyle)):
b&&b.drillable&&(f||(b.styles={},b.css(b.basicStyles)),b.on("click",null),b.removeClass("highcharts-drilldown-axis-label")))};d.addEvent(d.Point,"afterInit",function(){var a=this,b=a.series;a.drilldown&&d.addEvent(a,"click",function(c){b.xAxis&&!1===b.chart.options.drilldown.allowPointDrilldown?b.xAxis.drilldownCategory(a.x,c):a.doDrilldown(void 0,void 0,c)});return a});d.addEvent(d.Series,"afterDrawDataLabels",function(){var a=this.chart.options.drilldown.activeDataLabelStyle,b=this.chart.renderer,
c=this.chart.styledMode;this.points.forEach(function(d){var e=d.options.dataLabels,f=t(d.dlOptions,e&&e.style,{});d.drilldown&&d.dataLabel&&("contrast"!==a.color||c||(f.color=b.getContrast(d.color||this.color)),e&&e.color&&(f.color=e.color),d.dataLabel.addClass("highcharts-drilldown-data-label"),c||d.dataLabel.css(a).css(f))},this)});var w=function(a,b,c,d){a[c?"addClass":"removeClass"]("highcharts-drilldown-point");d||a.css({cursor:b})};d.addEvent(d.Series,"afterDrawTracker",function(){var a=this.chart.styledMode;
this.points.forEach(function(b){b.drilldown&&b.graphic&&w(b.graphic,"pointer",!0,a)})});d.addEvent(d.Point,"afterSetState",function(){var a=this.series.chart.styledMode;this.drilldown&&this.series.halo&&"hover"===this.state?w(this.series.halo,"pointer",!0,a):this.series.halo&&w(this.series.halo,"auto",!1,a)})});n(f,"masters/modules/drilldown.src.js",[],function(){})});
//# sourceMappingURL=drilldown.js.map
(function(c){"object"===typeof module&&module.exports?(c["default"]=c,module.exports=c):"function"===typeof define&&define.amd?define("highcharts/modules/drilldown",["highcharts"],function(m){c(m);c.Highcharts=m;return c}):c("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(c){function m(c,m,n,y){c.hasOwnProperty(m)||(c[m]=y.apply(null,n))}c=c?c._modules:{};m(c,"Extensions/Drilldown.js",[c["Core/Animation/AnimationUtilities.js"],c["Core/Axis/Axis.js"],c["Core/Chart/Chart.js"],c["Core/Color/Color.js"],
c["Core/Globals.js"],c["Core/Options.js"],c["Core/Series/Point.js"],c["Core/Renderer/SVG/SVGRenderer.js"],c["Core/Axis/Tick.js"],c["Core/Utilities.js"]],function(c,m,n,y,w,p,z,E,B,k){var C=c.animObject,F=w.noop;c=p.defaultOptions;var q=k.addEvent,G=k.removeEvent,t=k.extend,x=k.fireEvent,H=k.format,u=k.merge,I=k.objectEach,v=k.pick,J=k.syncTimeout;p=w.seriesTypes;k=p.pie;p=p.column;var D=1;t(c.lang,{drillUpText:"\u25c1 Back to {series.name}"});c.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#003399",
fontWeight:"bold",textDecoration:"underline"},activeDataLabelStyle:{cursor:"pointer",color:"#003399",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};E.prototype.Element.prototype.fadeIn=function(a){this.attr({opacity:.1,visibility:"inherit"}).animate({opacity:v(this.newOpacity,1)},a||{duration:250})};n.prototype.addSeriesAsDrilldown=function(a,b){this.addSingleSeriesAsDrilldown(a,b);this.applyDrilldown()};n.prototype.addSingleSeriesAsDrilldown=
function(a,b){var d=a.series,e=d.xAxis,f=d.yAxis,g=[],r=[],h;var l=this.styledMode?{colorIndex:v(a.colorIndex,d.colorIndex)}:{color:a.color||d.color};this.drilldownLevels||(this.drilldownLevels=[]);var c=d.options._levelNumber||0;(h=this.drilldownLevels[this.drilldownLevels.length-1])&&h.levelNumber!==c&&(h=void 0);b=t(t({_ddSeriesId:D++},l),b);var k=d.points.indexOf(a);d.chart.series.forEach(function(a){a.xAxis!==e||a.isDrilling||(a.options._ddSeriesId=a.options._ddSeriesId||D++,a.options._colorIndex=
a.userOptions._colorIndex,a.options._levelNumber=a.options._levelNumber||c,h?(g=h.levelSeries,r=h.levelSeriesOptions):(g.push(a),a.purgedOptions=u({_ddSeriesId:a.options._ddSeriesId,_levelNumber:a.options._levelNumber,selected:a.options.selected},a.userOptions),r.push(a.purgedOptions)))});a=t({levelNumber:c,seriesOptions:d.options,seriesPurgedOptions:d.purgedOptions,levelSeriesOptions:r,levelSeries:g,shapeArgs:a.shapeArgs,bBox:a.graphic?a.graphic.getBBox():{},color:a.isNull?(new y(l.color)).setOpacity(0).get():
l.color,lowerSeriesOptions:b,pointOptions:d.options.data[k],pointIndex:k,oldExtremes:{xMin:e&&e.userMin,xMax:e&&e.userMax,yMin:f&&f.userMin,yMax:f&&f.userMax},resetZoomButton:this.resetZoomButton},l);this.drilldownLevels.push(a);e&&e.names&&(e.names.length=0);b=a.lowerSeries=this.addSeries(b,!1);b.options._levelNumber=c+1;e&&(e.oldPos=e.pos,e.userMin=e.userMax=null,f.userMin=f.userMax=null);d.type===b.type&&(b.animate=b.animateDrilldown||F,b.options.animation=!0)};n.prototype.applyDrilldown=function(){var a=
this.drilldownLevels;if(a&&0<a.length){var b=a[a.length-1].levelNumber;this.drilldownLevels.forEach(function(a){a.levelNumber===b&&a.levelSeries.forEach(function(a){a.options&&a.options._levelNumber===b&&a.remove(!1)})})}this.resetZoomButton&&(this.resetZoomButton.hide(),delete this.resetZoomButton);this.pointer.reset();this.redraw();this.showDrillUpButton();x(this,"afterDrilldown")};n.prototype.getDrilldownBackText=function(){var a=this.drilldownLevels;if(a&&0<a.length)return a=a[a.length-1],a.series=
a.seriesOptions,H(this.options.lang.drillUpText,a)};n.prototype.showDrillUpButton=function(){var a=this,b=this.getDrilldownBackText(),d=a.options.drilldown.drillUpButton,e;if(this.drillUpButton)this.drillUpButton.attr({text:b}).align();else{var f=(e=d.theme)&&e.states;this.drillUpButton=this.renderer.button(b,null,null,function(){a.drillUp()},e,f&&f.hover,f&&f.select).addClass("highcharts-drillup-button").attr({align:d.position.align,zIndex:7}).add().align(d.position,!1,d.relativeTo||"plotBox")}};
n.prototype.drillUp=function(){if(this.drilldownLevels&&0!==this.drilldownLevels.length){for(var a=this,b=a.drilldownLevels,d=b[b.length-1].levelNumber,e=b.length,f=a.series,g,c,h,l,k=function(b){f.forEach(function(a){a.options._ddSeriesId===b._ddSeriesId&&(d=a)});var d=d||a.addSeries(b,!1);d.type===h.type&&d.animateDrillupTo&&(d.animate=d.animateDrillupTo);b===c.seriesPurgedOptions&&(l=d)};e--;)if(c=b[e],c.levelNumber===d){b.pop();h=c.lowerSeries;if(!h.chart)for(g=f.length;g--;)if(f[g].options.id===
c.lowerSeriesOptions.id&&f[g].options._levelNumber===d+1){h=f[g];break}h.xData=[];c.levelSeriesOptions.forEach(k);x(a,"drillup",{seriesOptions:c.seriesPurgedOptions||c.seriesOptions});l.type===h.type&&(l.drilldownLevel=c,l.options.animation=a.options.drilldown.animation,h.animateDrillupFrom&&h.chart&&h.animateDrillupFrom(c));l.options._levelNumber=d;h.remove(!1);l.xAxis&&(g=c.oldExtremes,l.xAxis.setExtremes(g.xMin,g.xMax,!1),l.yAxis.setExtremes(g.yMin,g.yMax,!1));c.resetZoomButton&&(a.resetZoomButton=
c.resetZoomButton,a.resetZoomButton.show())}this.redraw();0===this.drilldownLevels.length?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align();this.ddDupes.length=[];x(a,"drillupall")}};q(n,"afterInit",function(){var a=this;a.drilldown={update:function(b,d){u(!0,a.options.drilldown,b);v(d,!0)&&a.redraw()}}});q(n,"beforeShowResetZoom",function(){if(this.drillUpButton)return!1});q(n,"render",function(){(this.xAxis||[]).forEach(function(a){a.ddPoints=
{};a.series.forEach(function(b){var d,e=b.xData||[],f=b.points;for(d=0;d<e.length;d++){var c=b.options.data[d];"number"!==typeof c&&(c=b.pointClass.prototype.optionsToObject.call({series:b},c),c.drilldown&&(a.ddPoints[e[d]]||(a.ddPoints[e[d]]=[]),a.ddPoints[e[d]].push(f?f[d]:!0)))}});I(a.ticks,B.prototype.drillable)})});p.prototype.animateDrillupTo=function(a){if(!a){var b=this,d=b.drilldownLevel;this.points.forEach(function(a){var b=a.dataLabel;a.graphic&&a.graphic.hide();b&&(b.hidden="hidden"===
b.attr("visibility"),b.hidden||(b.hide(),a.connector&&a.connector.hide()))});J(function(){if(b.points){var a=[];b.data.forEach(function(b){a.push(b)});b.nodes&&(a=a.concat(b.nodes));a.forEach(function(a,b){b=b===(d&&d.pointIndex)?"show":"fadeIn";var c="show"===b?!0:void 0,e=a.dataLabel;if(a.graphic)a.graphic[b](c);e&&!e.hidden&&(e.fadeIn(),a.connector&&a.connector.fadeIn())})}},Math.max(this.chart.options.drilldown.animation.duration-50,0));delete this.animate}};p.prototype.animateDrilldown=function(a){var b=
this,d=this.chart,e=d.drilldownLevels,c,g=C(d.options.drilldown.animation),r=this.xAxis,h=d.styledMode;a||(e.forEach(function(a){b.options._ddSeriesId===a.lowerSeriesOptions._ddSeriesId&&(c=a.shapeArgs,h||(c.fill=a.color))}),c.x+=v(r.oldPos,r.pos)-r.pos,this.points.forEach(function(a){var d=a.shapeArgs;h||(d.fill=a.color);a.graphic&&a.graphic.attr(c).animate(t(a.shapeArgs,{fill:a.color||b.color}),g);a.dataLabel&&a.dataLabel.fadeIn(g)}),delete this.animate)};p.prototype.animateDrillupFrom=function(a){var b=
C(this.chart.options.drilldown.animation),d=this.group,c=d!==this.chart.columnGroup,f=this;f.trackerGroups.forEach(function(a){if(f[a])f[a].on("mouseover")});c&&delete this.group;this.points.forEach(function(e){var g=e.graphic,h=a.shapeArgs,l=function(){g.destroy();d&&c&&(d=d.destroy())};g&&h&&(delete e.graphic,f.chart.styledMode||(h.fill=a.color),b.duration?g.animate(h,u(b,{complete:l})):(g.attr(h),l()))})};k&&t(k.prototype,{animateDrillupTo:p.prototype.animateDrillupTo,animateDrillupFrom:p.prototype.animateDrillupFrom,
animateDrilldown:function(a){var b=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],d=this.chart.options.drilldown.animation;this.is("item")&&(d.duration=0);if(this.center){var c=b.shapeArgs,f=c.start,g=(c.end-f)/this.points.length,k=this.chart.styledMode;a||(this.points.forEach(function(a,e){var h=a.shapeArgs;k||(c.fill=b.color,h.fill=a.color);if(a.graphic)a.graphic.attr(u(c,{start:f+e*g,end:f+(e+1)*g}))[d?"animate":"attr"](h,d)}),delete this.animate)}}});z.prototype.doDrilldown=function(a,
b,d){var c=this.series.chart,f=c.options.drilldown,g=(f.series||[]).length;c.ddDupes||(c.ddDupes=[]);for(;g--&&!k;)if(f.series[g].id===this.drilldown&&-1===c.ddDupes.indexOf(this.drilldown)){var k=f.series[g];c.ddDupes.push(this.drilldown)}x(c,"drilldown",{point:this,seriesOptions:k,category:b,originalEvent:d,points:"undefined"!==typeof b&&this.series.xAxis.getDDPoints(b).slice(0)},function(b){var d=b.point.series&&b.point.series.chart,c=b.seriesOptions;d&&c&&(a?d.addSingleSeriesAsDrilldown(b.point,
c):d.addSeriesAsDrilldown(b.point,c))})};m.prototype.drilldownCategory=function(a,b){this.getDDPoints(a).forEach(function(d){d&&d.series&&d.series.visible&&d.doDrilldown&&d.doDrilldown(!0,a,b)});this.chart.applyDrilldown()};m.prototype.getDDPoints=function(a){return this.ddPoints&&this.ddPoints[a]||[]};B.prototype.drillable=function(){var a=this.pos,b=this.label,d=this.axis,c="xAxis"===d.coll&&d.getDDPoints,f=c&&d.getDDPoints(a),g=d.chart.styledMode;c&&(b&&f&&f.length?(b.drillable=!0,b.basicStyles||
g||(b.basicStyles=u(b.styles)),b.addClass("highcharts-drilldown-axis-label"),b.removeOnDrillableClick&&G(b.element,"click"),b.removeOnDrillableClick=q(b.element,"click",function(b){b.preventDefault();d.drilldownCategory(a,b)}),g||b.css(d.chart.options.drilldown.activeAxisLabelStyle)):b&&b.drillable&&b.removeOnDrillableClick&&(g||(b.styles={},b.css(b.basicStyles)),b.removeOnDrillableClick(),b.removeClass("highcharts-drilldown-axis-label")))};q(z,"afterInit",function(){var a=this,b=a.series;a.drilldown&&
q(a,"click",function(c){b.xAxis&&!1===b.chart.options.drilldown.allowPointDrilldown?b.xAxis.drilldownCategory(a.x,c):a.doDrilldown(void 0,void 0,c)});return a});q(w.Series,"afterDrawDataLabels",function(){var a=this.chart.options.drilldown.activeDataLabelStyle,b=this.chart.renderer,c=this.chart.styledMode;this.points.forEach(function(d){var f=d.options.dataLabels,e=v(d.dlOptions,f&&f.style,{});d.drilldown&&d.dataLabel&&("contrast"!==a.color||c||(e.color=b.getContrast(d.color||this.color)),f&&f.color&&
(e.color=f.color),d.dataLabel.addClass("highcharts-drilldown-data-label"),c||d.dataLabel.css(a).css(e))},this)});var A=function(a,b,c,e){a[c?"addClass":"removeClass"]("highcharts-drilldown-point");e||a.css({cursor:b})};q(w.Series,"afterDrawTracker",function(){var a=this.chart.styledMode;this.points.forEach(function(b){b.drilldown&&b.graphic&&A(b.graphic,"pointer",!0,a)})});q(z,"afterSetState",function(){var a=this.series.chart.styledMode;this.drilldown&&this.series.halo&&"hover"===this.state?A(this.series.halo,
"pointer",!0,a):this.series.halo&&A(this.series.halo,"auto",!1,a)})});m(c,"masters/modules/drilldown.src.js",[],function(){})});
//# sourceMappingURL=drilldown.js.map

View File

@ -1,151 +1,171 @@
/*
Highcharts JS v7.1.2 (2019-06-03)
Highcharts JS v8.2.2 (2020-10-22)
(c) 2009-2018 Torstein Honsi
License: www.highcharts.com/license
*/
(function(u){"object"===typeof module&&module.exports?(u["default"]=u,module.exports=u):"function"===typeof define&&define.amd?define("highcharts/highcharts-more",["highcharts"],function(y){u(y);u.Highcharts=y;return u}):u("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(u){function y(a,q,e,m){a.hasOwnProperty(q)||(a[q]=m.apply(null,e))}u=u?u._modules:{};y(u,"parts-more/Pane.js",[u["parts/Globals.js"]],function(a){function q(b,f){this.init(b,f)}var e=a.CenteredSeriesMixin,m=a.extend,
n=a.merge,b=a.splat;m(q.prototype,{coll:"pane",init:function(b,f){this.chart=f;this.background=[];f.pane.push(this);this.setOptions(b)},setOptions:function(b){this.options=n(this.defaultOptions,this.chart.angular?{background:{}}:void 0,b)},render:function(){var a=this.options,f=this.options.background,d=this.chart.renderer;this.group||(this.group=d.g("pane-group").attr({zIndex:a.zIndex||0}).add());this.updateCenter();if(f)for(f=b(f),a=Math.max(f.length,this.background.length||0),d=0;d<a;d++)f[d]&&
this.axis?this.renderBackground(n(this.defaultBackgroundOptions,f[d]),d):this.background[d]&&(this.background[d]=this.background[d].destroy(),this.background.splice(d,1))},renderBackground:function(b,f){var d="animate",a={"class":"highcharts-pane "+(b.className||"")};this.chart.styledMode||m(a,{fill:b.backgroundColor,stroke:b.borderColor,"stroke-width":b.borderWidth});this.background[f]||(this.background[f]=this.chart.renderer.path().add(this.group),d="attr");this.background[f][d]({d:this.axis.getPlotBandPath(b.from,
b.to,b)}).attr(a)},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"#cccccc",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#ffffff"],[1,"#e6e6e6"]]},from:-Number.MAX_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"},updateCenter:function(b){this.center=(b||this.axis||{}).center=e.getCenter.call(this)},update:function(b,a){n(!0,this.options,b);n(!0,this.chart.options.pane,b);this.setOptions(this.options);
this.render();this.chart.axes.forEach(function(b){b.pane===this&&(b.pane=null,b.update({},a))},this)}});a.Pane=q});y(u,"parts-more/RadialAxis.js",[u["parts/Globals.js"]],function(a){var q=a.addEvent,e=a.Axis,m=a.extend,n=a.merge,b=a.noop,g=a.pick,f=a.pInt,d=a.Tick,v=a.wrap,t=a.correctFloat,x,c,k=e.prototype,p=d.prototype;x={getOffset:b,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:b,setCategories:b,setTitle:b};c={defaultRadialGaugeOptions:{labels:{align:"center",x:0,
y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,distance:15,x:0,y:null,style:{textOverflow:"none"}},maxPadding:0,minPadding:0,showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(c){c=
this.options=n(this.defaultOptions,this.defaultRadialOptions,c);c.plotBands||(c.plotBands=[]);a.fireEvent(this,"afterSetOptions")},getOffset:function(){k.getOffset.call(this);this.chart.axisOffset[this.side]=0},getLinePath:function(c,l){c=this.center;var h=this.chart,r=g(l,c[2]/2-this.offset);this.isCircular||void 0!==l?(l=this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],r,r,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0}),l.xBounds=[this.left+c[0]],l.yBounds=[this.top+
c[1]-r]):(l=this.postTranslate(this.angleRad,r),l=["M",c[0]+h.plotLeft,c[1]+h.plotTop,"L",l.x,l.y]);return l},setAxisTranslation:function(){k.setAxisTranslation.call(this);this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset:0)},beforeSetTickPositions:function(){if(this.autoConnect=this.isCircular&&void 0===g(this.userMax,this.options.max)&&t(this.endAngleRad-
this.startAngleRad)===t(2*Math.PI))this.max+=this.categories&&1||this.pointRange||this.closestPointRange||0},setAxisSize:function(){k.setAxisSize.call(this);this.isRadial&&(this.pane.updateCenter(this),this.isCircular&&(this.sector=this.endAngleRad-this.startAngleRad),this.len=this.width=this.height=this.center[2]*g(this.sector,1)/2)},getPosition:function(c,l){return this.postTranslate(this.isCircular?this.translate(c):this.angleRad,g(this.isCircular?l:this.translate(c),this.center[2]/2)-this.offset)},
postTranslate:function(c,l){var h=this.chart,r=this.center;c=this.startAngleRad+c;return{x:h.plotLeft+r[0]+Math.cos(c)*l,y:h.plotTop+r[1]+Math.sin(c)*l}},getPlotBandPath:function(c,l,h){var r=this.center,b=this.startAngleRad,w=r[2]/2,k=[g(h.outerRadius,"100%"),h.innerRadius,g(h.thickness,10)],p=Math.min(this.offset,0),d=/%$/,a,t;a=this.isCircular;"polygon"===this.options.gridLineInterpolation?k=this.getPlotLinePath({value:c}).concat(this.getPlotLinePath({value:l,reverse:!0})):(c=Math.max(c,this.min),
l=Math.min(l,this.max),a||(k[0]=this.translate(c),k[1]=this.translate(l)),k=k.map(function(h){d.test(h)&&(h=f(h,10)*w/100);return h}),"circle"!==h.shape&&a?(c=b+this.translate(c),l=b+this.translate(l)):(c=-Math.PI/2,l=1.5*Math.PI,t=!0),k[0]-=p,k[2]-=p,k=this.chart.renderer.symbols.arc(this.left+r[0],this.top+r[1],k[0],k[0],{start:Math.min(c,l),end:Math.max(c,l),innerR:g(k[1],k[0]-k[2]),open:t}),a&&(a=(l+c)/2,p=this.left+r[0]+r[2]/2*Math.cos(a),k.xBounds=a>-Math.PI/2&&a<Math.PI/2?[p,this.chart.plotWidth]:
[0,p],k.yBounds=[this.top+r[1]+r[2]/2*Math.sin(a)],k.yBounds[0]+=a>-Math.PI&&0>a||a>Math.PI?-10:10));return k},getPlotLinePath:function(c){var l=this,h=l.center,r=l.chart,b=c.value;c=c.reverse;var w=l.getPosition(b),k,p,a;l.isCircular?a=["M",h[0]+r.plotLeft,h[1]+r.plotTop,"L",w.x,w.y]:"circle"===l.options.gridLineInterpolation?(b=l.translate(b),a=l.getLinePath(0,b)):(r.xAxis.forEach(function(h){h.pane===l.pane&&(k=h)}),a=[],b=l.translate(b),h=k.tickPositions,k.autoConnect&&(h=h.concat([h[0]])),c&&
(h=[].concat(h).reverse()),h.forEach(function(h,c){p=k.getPosition(h,b);a.push(c?"L":"M",p.x,p.y)}));return a},getTitlePosition:function(){var c=this.center,l=this.chart,h=this.options.title;return{x:l.plotLeft+c[0]+(h.x||0),y:l.plotTop+c[1]-{high:.5,middle:.25,low:0}[h.align]*c[2]+(h.y||0)}}};q(e,"init",function(b){var l=this,h=this.chart,r=h.angular,k=h.polar,w=this.isXAxis,p=r&&w,a,d=h.options;b=b.userOptions.pane||0;b=this.pane=h.pane&&h.pane[b];if(r){if(m(this,p?x:c),a=!w)this.defaultRadialOptions=
this.defaultRadialGaugeOptions}else k&&(m(this,c),this.defaultRadialOptions=(a=w)?this.defaultRadialXOptions:n(this.defaultYAxisOptions,this.defaultRadialYOptions));r||k?(this.isRadial=!0,h.inverted=!1,d.chart.zoomType=null,h.labelCollectors.push(function(){if(l.isRadial&&l.tickPositions&&!0!==l.options.labels.allowOverlap)return l.tickPositions.map(function(h){return l.ticks[h]&&l.ticks[h].label}).filter(function(h){return!!h})})):this.isRadial=!1;b&&a&&(b.axis=this);this.isCircular=a});q(e,"afterInit",
function(){var c=this.chart,l=this.options,h=this.pane,r=h&&h.options;c.angular&&this.isXAxis||!h||!c.angular&&!c.polar||(this.angleRad=(l.angle||0)*Math.PI/180,this.startAngleRad=(r.startAngle-90)*Math.PI/180,this.endAngleRad=(g(r.endAngle,r.startAngle+360)-90)*Math.PI/180,this.offset=l.offset||0)});q(e,"autoLabelAlign",function(c){this.isRadial&&(c.align=void 0,c.preventDefault())});q(d,"afterGetPosition",function(c){this.axis.getPosition&&m(c.pos,this.axis.getPosition(this.pos))});q(d,"afterGetLabelPosition",
function(c){var l=this.axis,h=this.label,r=h.getBBox(),b=l.options.labels,k=b.y,w,p=20,d=b.align,f=(l.translate(this.pos)+l.startAngleRad+Math.PI/2)/Math.PI*180%360,t=Math.round(f),m="end",v=0>t?t+360:t,n=v,q=0,x=0,e=null===b.y?.3*-r.height:0;if(l.isRadial){w=l.getPosition(this.pos,l.center[2]/2+a.relativeLength(g(b.distance,-25),l.center[2]/2,-l.center[2]/2));"auto"===b.rotation?h.attr({rotation:f}):null===k&&(k=l.chart.renderer.fontMetrics(h.styles&&h.styles.fontSize).b-r.height/2);null===d&&(l.isCircular?
(r.width>l.len*l.tickInterval/(l.max-l.min)&&(p=0),d=f>p&&f<180-p?"left":f>180+p&&f<360-p?"right":"center"):d="center",h.attr({align:d}));if("auto"===d&&2===l.tickPositions.length&&l.isCircular){90<v&&180>v?v=180-v:270<v&&360>=v&&(v=540-v);180<n&&360>=n&&(n=360-n);if(l.pane.options.startAngle===t||l.pane.options.startAngle===t+360||l.pane.options.startAngle===t-360)m="start";d=-90<=t&&90>=t||-360<=t&&-270>=t||270<=t&&360>=t?"start"===m?"right":"left":"start"===m?"left":"right";70<n&&110>n&&(d="center");
15>v||180<=v&&195>v?q=.3*r.height:15<=v&&35>=v?q="start"===m?0:.75*r.height:195<=v&&215>=v?q="start"===m?.75*r.height:0:35<v&&90>=v?q="start"===m?.25*-r.height:r.height:215<v&&270>=v&&(q="start"===m?r.height:.25*-r.height);15>n?x="start"===m?.15*-r.height:.15*r.height:165<n&&180>=n&&(x="start"===m?.15*r.height:.15*-r.height);h.attr({align:d});h.translate(x,q+e)}c.pos.x=w.x+b.x;c.pos.y=w.y+k}});v(p,"getMarkPath",function(c,l,h,b,k,p,a){var r=this.axis;r.isRadial?(c=r.getPosition(this.pos,r.center[2]/
2+b),l=["M",l,h,"L",c.x,c.y]):l=c.call(this,l,h,b,k,p,a);return l})});y(u,"parts-more/AreaRangeSeries.js",[u["parts/Globals.js"]],function(a){var q=a.pick,e=a.extend,m=a.isArray,n=a.defined,b=a.seriesType,g=a.seriesTypes,f=a.Series.prototype,d=a.Point.prototype;b("arearange","area",{lineWidth:1,threshold:null,tooltip:{pointFormat:'\x3cspan style\x3d"color:{series.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.low}\x3c/b\x3e - \x3cb\x3e{point.high}\x3c/b\x3e\x3cbr/\x3e'},trackByArea:!0,
dataLabels:{align:null,verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}},{pointArrayMap:["low","high"],toYData:function(b){return[b.low,b.high]},pointValKey:"low",deferTranslatePolar:!0,highToXY:function(b){var a=this.chart,d=this.xAxis.postTranslate(b.rectPlotX,this.yAxis.len-b.plotHigh);b.plotHighX=d.x-a.plotLeft;b.plotHigh=d.y-a.plotTop;b.plotLowX=b.plotX},translate:function(){var b=this,a=b.yAxis,d=!!b.modifyValue;g.area.prototype.translate.apply(b);b.points.forEach(function(c){var k=c.low,p=
c.high,w=c.plotY;null===p||null===k?(c.isNull=!0,c.plotY=null):(c.plotLow=w,c.plotHigh=a.translate(d?b.modifyValue(p,c):p,0,1,0,1),d&&(c.yBottom=c.plotHigh))});this.chart.polar&&this.points.forEach(function(c){b.highToXY(c);c.tooltipPos=[(c.plotHighX+c.plotLowX)/2,(c.plotHigh+c.plotLow)/2]})},getGraphPath:function(b){var a=[],d=[],c,k=g.area.prototype.getGraphPath,p,w,l;l=this.options;var h=this.chart.polar&&!1!==l.connectEnds,r=l.connectNulls,A=l.step;b=b||this.points;for(c=b.length;c--;)p=b[c],
p.isNull||h||r||b[c+1]&&!b[c+1].isNull||d.push({plotX:p.plotX,plotY:p.plotY,doCurve:!1}),w={polarPlotY:p.polarPlotY,rectPlotX:p.rectPlotX,yBottom:p.yBottom,plotX:q(p.plotHighX,p.plotX),plotY:p.plotHigh,isNull:p.isNull},d.push(w),a.push(w),p.isNull||h||r||b[c-1]&&!b[c-1].isNull||d.push({plotX:p.plotX,plotY:p.plotY,doCurve:!1});b=k.call(this,b);A&&(!0===A&&(A="left"),l.step={left:"right",center:"center",right:"left"}[A]);a=k.call(this,a);d=k.call(this,d);l.step=A;l=[].concat(b,a);this.chart.polar||
"M"!==d[0]||(d[0]="L");this.graphPath=l;this.areaPath=b.concat(d);l.isArea=!0;l.xMap=b.xMap;this.areaPath.xMap=b.xMap;return l},drawDataLabels:function(){var b=this.points,a=b.length,d,c=[],k=this.options.dataLabels,p,w,l=this.chart.inverted,h,r;m(k)?1<k.length?(h=k[0],r=k[1]):(h=k[0],r={enabled:!1}):(h=e({},k),h.x=k.xHigh,h.y=k.yHigh,r=e({},k),r.x=k.xLow,r.y=k.yLow);if(h.enabled||this._hasPointLabels){for(d=a;d--;)if(p=b[d])w=h.inside?p.plotHigh<p.plotLow:p.plotHigh>p.plotLow,p.y=p.high,p._plotY=
p.plotY,p.plotY=p.plotHigh,c[d]=p.dataLabel,p.dataLabel=p.dataLabelUpper,p.below=w,l?h.align||(h.align=w?"right":"left"):h.verticalAlign||(h.verticalAlign=w?"top":"bottom");this.options.dataLabels=h;f.drawDataLabels&&f.drawDataLabels.apply(this,arguments);for(d=a;d--;)if(p=b[d])p.dataLabelUpper=p.dataLabel,p.dataLabel=c[d],delete p.dataLabels,p.y=p.low,p.plotY=p._plotY}if(r.enabled||this._hasPointLabels){for(d=a;d--;)if(p=b[d])w=r.inside?p.plotHigh<p.plotLow:p.plotHigh>p.plotLow,p.below=!w,l?r.align||
(r.align=w?"left":"right"):r.verticalAlign||(r.verticalAlign=w?"bottom":"top");this.options.dataLabels=r;f.drawDataLabels&&f.drawDataLabels.apply(this,arguments)}if(h.enabled)for(d=a;d--;)if(p=b[d])p.dataLabels=[p.dataLabelUpper,p.dataLabel].filter(function(c){return!!c});this.options.dataLabels=k},alignDataLabel:function(){g.column.prototype.alignDataLabel.apply(this,arguments)},drawPoints:function(){var b=this.points.length,d,m;f.drawPoints.apply(this,arguments);for(m=0;m<b;)d=this.points[m],d.origProps=
{plotY:d.plotY,plotX:d.plotX,isInside:d.isInside,negative:d.negative,zone:d.zone,y:d.y},d.lowerGraphic=d.graphic,d.graphic=d.upperGraphic,d.plotY=d.plotHigh,n(d.plotHighX)&&(d.plotX=d.plotHighX),d.y=d.high,d.negative=d.high<(this.options.threshold||0),d.zone=this.zones.length&&d.getZone(),this.chart.polar||(d.isInside=d.isTopInside=void 0!==d.plotY&&0<=d.plotY&&d.plotY<=this.yAxis.len&&0<=d.plotX&&d.plotX<=this.xAxis.len),m++;f.drawPoints.apply(this,arguments);for(m=0;m<b;)d=this.points[m],d.upperGraphic=
d.graphic,d.graphic=d.lowerGraphic,a.extend(d,d.origProps),delete d.origProps,m++},setStackedPoints:a.noop},{setState:function(){var b=this.state,a=this.series,f=a.chart.polar;n(this.plotHigh)||(this.plotHigh=a.yAxis.toPixels(this.high,!0));n(this.plotLow)||(this.plotLow=this.plotY=a.yAxis.toPixels(this.low,!0));a.stateMarkerGraphic&&(a.lowerStateMarkerGraphic=a.stateMarkerGraphic,a.stateMarkerGraphic=a.upperStateMarkerGraphic);this.graphic=this.upperGraphic;this.plotY=this.plotHigh;f&&(this.plotX=
this.plotHighX);d.setState.apply(this,arguments);this.state=b;this.plotY=this.plotLow;this.graphic=this.lowerGraphic;f&&(this.plotX=this.plotLowX);a.stateMarkerGraphic&&(a.upperStateMarkerGraphic=a.stateMarkerGraphic,a.stateMarkerGraphic=a.lowerStateMarkerGraphic,a.lowerStateMarkerGraphic=void 0);d.setState.apply(this,arguments)},haloPath:function(){var b=this.series.chart.polar,a=[];this.plotY=this.plotLow;b&&(this.plotX=this.plotLowX);this.isInside&&(a=d.haloPath.apply(this,arguments));this.plotY=
this.plotHigh;b&&(this.plotX=this.plotHighX);this.isTopInside&&(a=a.concat(d.haloPath.apply(this,arguments)));return a},destroyElements:function(){["lowerGraphic","upperGraphic"].forEach(function(b){this[b]&&(this[b]=this[b].destroy())},this);this.graphic=null;return d.destroyElements.apply(this,arguments)}})});y(u,"parts-more/AreaSplineRangeSeries.js",[u["parts/Globals.js"]],function(a){var q=a.seriesType;q("areasplinerange","arearange",null,{getPointSpline:a.seriesTypes.spline.prototype.getPointSpline})});
y(u,"parts-more/ColumnRangeSeries.js",[u["parts/Globals.js"]],function(a){var q=a.defaultPlotOptions,e=a.merge,m=a.noop,n=a.pick,b=a.seriesType,g=a.seriesTypes.column.prototype;b("columnrange","arearange",e(q.column,q.arearange,{pointRange:null,marker:null,states:{hover:{halo:!1}}}),{translate:function(){var b=this,d=b.yAxis,a=b.xAxis,m=a.startAngleRad,q,c=b.chart,k=b.xAxis.isRadial,p=Math.max(c.chartWidth,c.chartHeight)+999,w;g.translate.apply(b);b.points.forEach(function(l){var h=l.shapeArgs,r=
b.options.minPointLength,A,f;l.plotHigh=w=Math.min(Math.max(-p,d.translate(l.high,0,1,0,1)),p);l.plotLow=Math.min(Math.max(-p,l.plotY),p);f=w;A=n(l.rectPlotY,l.plotY)-w;Math.abs(A)<r?(r-=A,A+=r,f-=r/2):0>A&&(A*=-1,f-=A);k?(q=l.barX+m,l.shapeType="path",l.shapeArgs={d:b.polarArc(f+A,f,q,q+l.pointWidth)}):(h.height=A,h.y=f,l.tooltipPos=c.inverted?[d.len+d.pos-c.plotLeft-f-A/2,a.len+a.pos-c.plotTop-h.x-h.width/2,A]:[a.left-c.plotLeft+h.x+h.width/2,d.pos-c.plotTop+f+A/2,A])})},directTouch:!0,trackerGroups:["group",
"dataLabelsGroup"],drawGraph:m,getSymbol:m,crispCol:function(){return g.crispCol.apply(this,arguments)},drawPoints:function(){return g.drawPoints.apply(this,arguments)},drawTracker:function(){return g.drawTracker.apply(this,arguments)},getColumnMetrics:function(){return g.getColumnMetrics.apply(this,arguments)},pointAttribs:function(){return g.pointAttribs.apply(this,arguments)},animate:function(){return g.animate.apply(this,arguments)},polarArc:function(){return g.polarArc.apply(this,arguments)},
translate3dPoints:function(){return g.translate3dPoints.apply(this,arguments)},translate3dShapes:function(){return g.translate3dShapes.apply(this,arguments)}},{setState:g.pointClass.prototype.setState})});y(u,"parts-more/ColumnPyramidSeries.js",[u["parts/Globals.js"]],function(a){var q=a.pick,e=a.seriesType,m=a.seriesTypes.column.prototype;e("columnpyramid","column",{},{translate:function(){var a=this,b=a.chart,g=a.options,f=a.dense=2>a.closestPointRange*a.xAxis.transA,f=a.borderWidth=q(g.borderWidth,
f?0:1),d=a.yAxis,v=g.threshold,e=a.translatedThreshold=d.getThreshold(v),x=q(g.minPointLength,5),c=a.getColumnMetrics(),k=c.width,p=a.barW=Math.max(k,1+2*f),w=a.pointXOffset=c.offset;b.inverted&&(e-=.5);g.pointPadding&&(p=Math.ceil(p));m.translate.apply(a);a.points.forEach(function(c){var h=q(c.yBottom,e),r=999+Math.abs(h),l=Math.min(Math.max(-r,c.plotY),d.len+r),r=c.plotX+w,f=p/2,m=Math.min(l,h),h=Math.max(l,h)-m,n,t,E,C,B,D;c.barX=r;c.pointWidth=k;c.tooltipPos=b.inverted?[d.len+d.pos-b.plotLeft-
l,a.xAxis.len-r-f,h]:[r+f,l+d.pos-b.plotTop,h];l=v+(c.total||c.y);"percent"===g.stacking&&(l=v+(0>c.y)?-100:100);l=d.toPixels(l,!0);n=b.plotHeight-l-(b.plotHeight-e);t=f*(m-l)/n;E=f*(m+h-l)/n;n=r-t+f;t=r+t+f;C=r+E+f;E=r-E+f;B=m-x;D=m+h;0>c.y&&(B=m,D=m+h+x);b.inverted&&(C=b.plotWidth-m,n=l-(b.plotWidth-e),t=f*(l-C)/n,E=f*(l-(C-h))/n,n=r+f+t,t=n-2*t,C=r-E+f,E=r+E+f,B=m,D=m+h-x,0>c.y&&(D=m+h+x));c.shapeType="path";c.shapeArgs={x:n,y:B,width:t-n,height:h,d:["M",n,B,"L",t,B,C,D,E,D,"Z"]}})}})});y(u,"parts-more/GaugeSeries.js",
[u["parts/Globals.js"]],function(a){var q=a.isNumber,e=a.merge,m=a.pick,n=a.pInt,b=a.Series,g=a.seriesType,f=a.TrackerMixin;g("gauge","line",{dataLabels:{borderColor:"#cccccc",borderRadius:3,borderWidth:1,crop:!1,defer:!1,enabled:!0,verticalAlign:"top",y:15,zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1},{angular:!0,directTouch:!0,drawGraph:a.noop,fixedBox:!0,forceDL:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],translate:function(){var b=this.yAxis,a=this.options,
f=b.center;this.generatePoints();this.points.forEach(function(d){var c=e(a.dial,d.dial),k=n(m(c.radius,80))*f[2]/200,p=n(m(c.baseLength,70))*k/100,w=n(m(c.rearLength,10))*k/100,l=c.baseWidth||3,h=c.topWidth||1,r=a.overshoot,A=b.startAngleRad+b.translate(d.y,null,null,null,!0);q(r)?(r=r/180*Math.PI,A=Math.max(b.startAngleRad-r,Math.min(b.endAngleRad+r,A))):!1===a.wrap&&(A=Math.max(b.startAngleRad,Math.min(b.endAngleRad,A)));A=180*A/Math.PI;d.shapeType="path";d.shapeArgs={d:c.path||["M",-w,-l/2,"L",
p,-l/2,k,-h/2,k,h/2,p,l/2,-w,l/2,"z"],translateX:f[0],translateY:f[1],rotation:A};d.plotX=f[0];d.plotY=f[1]})},drawPoints:function(){var b=this,a=b.chart,f=b.yAxis.center,n=b.pivot,c=b.options,k=c.pivot,p=a.renderer;b.points.forEach(function(d){var l=d.graphic,h=d.shapeArgs,r=h.d,k=e(c.dial,d.dial);l?(l.animate(h),h.d=r):d.graphic=p[d.shapeType](h).attr({rotation:h.rotation,zIndex:1}).addClass("highcharts-dial").add(b.group);if(!a.styledMode)d.graphic[l?"animate":"attr"]({stroke:k.borderColor||"none",
"stroke-width":k.borderWidth||0,fill:k.backgroundColor||"#000000"})});n?n.animate({translateX:f[0],translateY:f[1]}):(b.pivot=p.circle(0,0,m(k.radius,5)).attr({zIndex:2}).addClass("highcharts-pivot").translate(f[0],f[1]).add(b.group),a.styledMode||b.pivot.attr({"stroke-width":k.borderWidth||0,stroke:k.borderColor||"#cccccc",fill:k.backgroundColor||"#000000"}))},animate:function(b){var a=this;b||(a.points.forEach(function(b){var d=b.graphic;d&&(d.attr({rotation:180*a.yAxis.startAngleRad/Math.PI}),
d.animate({rotation:b.shapeArgs.rotation},a.options.animation))}),a.animate=null)},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);b.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:function(a,f){b.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();m(f,!0)&&this.chart.redraw()},hasData:function(){return!!this.points.length},drawTracker:f&&f.drawTrackerPoint},{setState:function(b){this.state=
b}})});y(u,"parts-more/BoxPlotSeries.js",[u["parts/Globals.js"]],function(a){var q=a.noop,e=a.pick,m=a.seriesType,n=a.seriesTypes;m("boxplot","column",{threshold:null,tooltip:{pointFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e \x3cb\x3e {series.name}\x3c/b\x3e\x3cbr/\x3eMaximum: {point.high}\x3cbr/\x3eUpper quartile: {point.q3}\x3cbr/\x3eMedian: {point.median}\x3cbr/\x3eLower quartile: {point.q1}\x3cbr/\x3eMinimum: {point.low}\x3cbr/\x3e'},whiskerLength:"50%",fillColor:"#ffffff",
lineWidth:1,medianWidth:2,whiskerWidth:2},{pointArrayMap:["low","q1","median","q3","high"],toYData:function(b){return[b.low,b.q1,b.median,b.q3,b.high]},pointValKey:"high",pointAttribs:function(){return{}},drawDataLabels:q,translate:function(){var b=this.yAxis,a=this.pointArrayMap;n.column.prototype.translate.apply(this);this.points.forEach(function(f){a.forEach(function(a){null!==f[a]&&(f[a+"Plot"]=b.translate(f[a],0,1,0,1))})})},drawPoints:function(){var b=this,a=b.options,f=b.chart,d=f.renderer,
m,n,q,c,k,p,w=0,l,h,r,A,H=!1!==b.doQuartiles,I,G=b.options.whiskerLength;b.points.forEach(function(g){var v=g.graphic,t=v?"animate":"attr",x=g.shapeArgs,K={},u={},L={},F={},z=g.color||b.color;void 0!==g.plotY&&(l=x.width,h=Math.floor(x.x),r=h+l,A=Math.round(l/2),m=Math.floor(H?g.q1Plot:g.lowPlot),n=Math.floor(H?g.q3Plot:g.lowPlot),q=Math.floor(g.highPlot),c=Math.floor(g.lowPlot),v||(g.graphic=v=d.g("point").add(b.group),g.stem=d.path().addClass("highcharts-boxplot-stem").add(v),G&&(g.whiskers=d.path().addClass("highcharts-boxplot-whisker").add(v)),
H&&(g.box=d.path(void 0).addClass("highcharts-boxplot-box").add(v)),g.medianShape=d.path(void 0).addClass("highcharts-boxplot-median").add(v)),f.styledMode||(u.stroke=g.stemColor||a.stemColor||z,u["stroke-width"]=e(g.stemWidth,a.stemWidth,a.lineWidth),u.dashstyle=g.stemDashStyle||a.stemDashStyle,g.stem.attr(u),G&&(L.stroke=g.whiskerColor||a.whiskerColor||z,L["stroke-width"]=e(g.whiskerWidth,a.whiskerWidth,a.lineWidth),g.whiskers.attr(L)),H&&(K.fill=g.fillColor||a.fillColor||z,K.stroke=a.lineColor||
z,K["stroke-width"]=a.lineWidth||0,g.box.attr(K)),F.stroke=g.medianColor||a.medianColor||z,F["stroke-width"]=e(g.medianWidth,a.medianWidth,a.lineWidth),g.medianShape.attr(F)),p=g.stem.strokeWidth()%2/2,w=h+A+p,g.stem[t]({d:["M",w,n,"L",w,q,"M",w,m,"L",w,c]}),H&&(p=g.box.strokeWidth()%2/2,m=Math.floor(m)+p,n=Math.floor(n)+p,h+=p,r+=p,g.box[t]({d:["M",h,n,"L",h,m,"L",r,m,"L",r,n,"L",h,n,"z"]})),G&&(p=g.whiskers.strokeWidth()%2/2,q+=p,c+=p,I=/%$/.test(G)?A*parseFloat(G)/100:G/2,g.whiskers[t]({d:["M",
w-I,q,"L",w+I,q,"M",w-I,c,"L",w+I,c]})),k=Math.round(g.medianPlot),p=g.medianShape.strokeWidth()%2/2,k+=p,g.medianShape[t]({d:["M",h,k,"L",r,k]}))})},setStackedPoints:q})});y(u,"parts-more/ErrorBarSeries.js",[u["parts/Globals.js"]],function(a){var q=a.noop,e=a.seriesType,m=a.seriesTypes;e("errorbar","boxplot",{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.low}\x3c/b\x3e - \x3cb\x3e{point.high}\x3c/b\x3e\x3cbr/\x3e'},
whiskerWidth:null},{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,drawDataLabels:m.arearange?function(){var a=this.pointValKey;m.arearange.prototype.drawDataLabels.call(this);this.data.forEach(function(b){b.y=b[a]})}:q,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||m.column.prototype.getColumnMetrics.call(this)}})});y(u,"parts-more/WaterfallSeries.js",[u["parts/Globals.js"]],function(a){var q=
a.correctFloat,e=a.isNumber,m=a.pick,n=a.objectEach,b=a.arrayMin,g=a.arrayMax,f=a.addEvent,d=a.Axis,v=a.Chart,t=a.Point,x=a.Series,c=a.StackItem,k=a.seriesType,p=a.seriesTypes;f(d,"afterInit",function(){this.isXAxis||(this.waterfallStacks={changed:!1})});f(v,"beforeRedraw",function(){for(var c=this.axes,b=this.series,h=b.length;h--;)b[h].options.stacking&&(c.forEach(function(c){c.isXAxis||(c.waterfallStacks.changed=!0)}),h=0)});f(d,"afterRender",function(){var c=this.options.stackLabels;c&&c.enabled&&
this.waterfallStacks&&this.renderWaterfallStackTotals()});d.prototype.renderWaterfallStackTotals=function(){var b=this.waterfallStacks,a=this.stackTotalGroup,h=new c(this,this.options.stackLabels,!1,0,void 0);this.dummyStackItem=h;n(b,function(b){n(b,function(b){h.total=b.stackTotal;b.label&&(h.label=b.label);c.prototype.render.call(h,a);b.label=h.label;delete h.label})});h.total=null};k("waterfall","column",{dataLabels:{inside:!0},lineWidth:1,lineColor:"#333333",dashStyle:"Dot",borderColor:"#333333",
states:{hover:{lineWidthPlus:0}}},{pointValKey:"y",showLine:!0,generatePoints:function(){var c,b,h,a;p.column.prototype.generatePoints.apply(this);h=0;for(b=this.points.length;h<b;h++)if(c=this.points[h],a=this.processedYData[h],c.isIntermediateSum||c.isSum)c.y=q(a)},translate:function(){var c=this.options,b=this.yAxis,h,a,d,k,f,g,n,q,e,v=m(c.minPointLength,5),t=v/2,x=c.threshold,u=c.stacking,F=b.waterfallStacks[this.stackKey],z;p.column.prototype.translate.apply(this);n=q=x;a=this.points;h=0;for(c=
a.length;h<c;h++){d=a[h];g=this.processedYData[h];k=d.shapeArgs;e=[0,g];z=d.y;if(u){if(F&&(e=F[h],"overlap"===u?(f=e.stackState[e.stateIndex--],f=0<=z?f:f-z,e.hasOwnProperty("absolutePos")&&delete e.absolutePos,e.hasOwnProperty("absoluteNeg")&&delete e.absoluteNeg):(0<=z?(f=e.threshold+e.posTotal,e.posTotal-=z):(f=e.threshold+e.negTotal,e.negTotal-=z,f-=z),!e.posTotal&&e.hasOwnProperty("absolutePos")&&(e.posTotal=e.absolutePos,delete e.absolutePos),!e.negTotal&&e.hasOwnProperty("absoluteNeg")&&(e.negTotal=
e.absoluteNeg,delete e.absoluteNeg)),d.isSum||(e.connectorThreshold=e.threshold+e.stackTotal),b.reversed?(g=0<=z?f-z:f+z,z=f):(g=f,z=f-z),d.below=g<=m(x,0),k.y=b.translate(g,0,1,0,1),k.height=Math.abs(k.y-b.translate(z,0,1,0,1))),z=b.dummyStackItem)z.x=h,z.label=F[h].label,z.setOffset(this.pointXOffset||0,this.barW||0,this.stackedYNeg[h],this.stackedYPos[h])}else f=Math.max(n,n+z)+e[0],k.y=b.translate(f,0,1,0,1),d.isSum?(k.y=b.translate(e[1],0,1,0,1),k.height=Math.min(b.translate(e[0],0,1,0,1),b.len)-
k.y):d.isIntermediateSum?(0<=z?(g=e[1]+q,z=q):(g=q,z=e[1]+q),b.reversed&&(g^=z,z^=g,g^=z),k.y=b.translate(g,0,1,0,1),k.height=Math.abs(k.y-Math.min(b.translate(z,0,1,0,1),b.len)),q+=e[1]):(k.height=0<g?b.translate(n,0,1,0,1)-k.y:b.translate(n,0,1,0,1)-b.translate(n-g,0,1,0,1),n+=g,d.below=n<m(x,0)),0>k.height&&(k.y+=k.height,k.height*=-1);d.plotY=k.y=Math.round(k.y)-this.borderWidth%2/2;k.height=Math.max(Math.round(k.height),.001);d.yBottom=k.y+k.height;k.height<=v&&!d.isNull?(k.height=v,k.y-=t,d.plotY=
k.y,d.minPointLengthOffset=0>d.y?-t:t):(d.isNull&&(k.width=0),d.minPointLengthOffset=0);k=d.plotY+(d.negative?k.height:0);this.chart.inverted?d.tooltipPos[0]=b.len-k:d.tooltipPos[1]=k}},processData:function(c){var b=this.options,h=this.yData,a=b.data,k,d=h.length,f=b.threshold||0,p,m,w,g,n,e;for(e=m=p=w=g=0;e<d;e++)n=h[e],k=a&&a[e]?a[e]:{},"sum"===n||k.isSum?h[e]=q(m):"intermediateSum"===n||k.isIntermediateSum?(h[e]=q(p),p=0):(m+=n,p+=n),w=Math.min(m,w),g=Math.max(m,g);x.prototype.processData.call(this,
c);b.stacking||(this.dataMin=w+f,this.dataMax=g)},toYData:function(c){return c.isSum?0===c.x?null:"sum":c.isIntermediateSum?0===c.x?null:"intermediateSum":c.y},pointAttribs:function(c,b){var h=this.options.upColor;h&&!c.options.color&&(c.color=0<c.y?h:null);c=p.column.prototype.pointAttribs.call(this,c,b);delete c.dashstyle;return c},getGraphPath:function(){return["M",0,0]},getCrispPath:function(){var c=this.data,b=this.yAxis,h=c.length,a=Math.round(this.graph.strokeWidth())%2/2,k=Math.round(this.borderWidth)%
2/2,d=this.xAxis.reversed,f=this.yAxis.reversed,p=this.options.stacking,m=[],g,n,e,q,v,t,x;for(t=1;t<h;t++){v=c[t].shapeArgs;n=c[t-1];q=c[t-1].shapeArgs;g=b.waterfallStacks[this.stackKey];e=0<n.y?-q.height:0;g&&(g=g[t-1],p?(g=g.connectorThreshold,e=Math.round(b.translate(g,0,1,0,1)+(f?e:0))-a):e=q.y+n.minPointLengthOffset+k-a,x=["M",q.x+(d?0:q.width),e,"L",v.x+(d?v.width:0),e]);if(!p&&0>n.y&&!f||0<n.y&&f)x[2]+=q.height,x[5]+=q.height;m=m.concat(x)}return m},drawGraph:function(){x.prototype.drawGraph.call(this);
this.graph.attr({d:this.getCrispPath()})},setStackedPoints:function(){function c(c,b,h,a){if(v)for(h;h<v;h++)g.stackState[h]+=a;else g.stackState[0]=c,v=g.stackState.length;g.stackState.push(g.stackState[v-1]+b)}var b=this.options,h=this.yAxis.waterfallStacks,a=b.threshold,k=a||0,d=k,f=this.stackKey,p=this.xData,m=p.length,g,n,e,q,v,t,x,u,y;this.yAxis.usePercentage=!1;n=e=q=k;if(this.visible||!this.chart.options.chart.ignoreHiddenSeries){h[f]||(h[f]={});for(var f=h[f],J=0;J<m;J++){t=p[J];if(!f[t]||
h.changed)f[t]={negTotal:0,posTotal:0,stackTotal:0,threshold:0,stateIndex:0,stackState:[],label:h.changed&&f[t]?f[t].label:void 0};g=f[t];y=this.yData[J];0<=y?g.posTotal+=y:g.negTotal+=y;u=b.data[J];t=g.absolutePos=g.posTotal;x=g.absoluteNeg=g.negTotal;g.stackTotal=t+x;v=g.stackState.length;u&&u.isIntermediateSum?(c(q,e,0,q),q=e,e=a,k^=d,d^=k,k^=d):u&&u.isSum?(c(a,n,v),k=a):(c(k,y,0,n),u&&(n+=y,e+=y));g.stateIndex++;g.threshold=k;k+=g.stackTotal}h.changed=!1}},getExtremes:function(){var c=this.options.stacking,
a,h,k;c&&(a=this.yAxis,a=a.waterfallStacks,h=this.stackedYNeg=[],k=this.stackedYPos=[],"overlap"===c?n(a[this.stackKey],function(c){h.push(b(c.stackState));k.push(g(c.stackState))}):n(a[this.stackKey],function(c){h.push(c.negTotal+c.threshold);k.push(c.posTotal+c.threshold)}),this.dataMin=b(h),this.dataMax=g(k))}},{getClassName:function(){var c=t.prototype.getClassName.call(this);this.isSum?c+=" highcharts-sum":this.isIntermediateSum&&(c+=" highcharts-intermediate-sum");return c},isValid:function(){return e(this.y,
!0)||this.isSum||this.isIntermediateSum}})});y(u,"parts-more/PolygonSeries.js",[u["parts/Globals.js"]],function(a){var q=a.Series,e=a.seriesType,m=a.seriesTypes;e("polygon","scatter",{marker:{enabled:!1,states:{hover:{enabled:!1}}},stickyTracking:!1,tooltip:{followPointer:!0,pointFormat:""},trackByArea:!0},{type:"polygon",getGraphPath:function(){for(var a=q.prototype.getGraphPath.call(this),b=a.length+1;b--;)(b===a.length||"M"===a[b])&&0<b&&a.splice(b,0,"z");return this.areaPath=a},drawGraph:function(){this.options.fillColor=
this.color;m.area.prototype.drawGraph.call(this)},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,drawTracker:q.prototype.drawTracker,setStackedPoints:a.noop})});y(u,"parts-more/BubbleLegend.js",[u["parts/Globals.js"]],function(a){var q=a.Series,e=a.Legend,m=a.Chart,n=a.addEvent,b=a.wrap,g=a.color,f=a.isNumber,d=a.numberFormat,v=a.objectEach,t=a.merge,x=a.noop,c=a.pick,k=a.stableSort,p=a.setOptions,w=a.arrayMin,l=a.arrayMax;p({legend:{bubbleLegend:{borderColor:void 0,borderWidth:2,className:void 0,
color:void 0,connectorClassName:void 0,connectorColor:void 0,connectorDistance:60,connectorWidth:1,enabled:!1,labels:{className:void 0,allowOverlap:!1,format:"",formatter:void 0,align:"right",style:{fontSize:10,color:void 0},x:0,y:0},maxSize:60,minSize:10,legendIndex:0,ranges:{value:void 0,borderColor:void 0,color:void 0,connectorColor:void 0},sizeBy:"area",sizeByAbsoluteValue:!1,zIndex:1,zThreshold:0}}});a.BubbleLegend=function(c,b){this.init(c,b)};a.BubbleLegend.prototype={init:function(c,b){this.options=
c;this.visible=!0;this.chart=b.chart;this.legend=b},setState:x,addToLegend:function(c){c.splice(this.options.legendIndex,0,this)},drawLegendSymbol:function(b){var h=this.chart,a=this.options,d=c(b.options.itemDistance,20),l,p=a.ranges;l=a.connectorDistance;this.fontMetrics=h.renderer.fontMetrics(a.labels.style.fontSize.toString()+"px");p&&p.length&&f(p[0].value)?(k(p,function(c,b){return b.value-c.value}),this.ranges=p,this.setOptions(),this.render(),h=this.getMaxLabelSize(),p=this.ranges[0].radius,
b=2*p,l=l-p+h.width,l=0<l?l:0,this.maxLabel=h,this.movementX="left"===a.labels.align?l:0,this.legendItemWidth=b+l+d,this.legendItemHeight=b+this.fontMetrics.h/2):b.options.bubbleLegend.autoRanges=!0},setOptions:function(){var b=this.ranges,a=this.options,k=this.chart.series[a.seriesIndex],d=this.legend.baseline,f={"z-index":a.zIndex,"stroke-width":a.borderWidth},l={"z-index":a.zIndex,"stroke-width":a.connectorWidth},p=this.getLabelStyles(),m=k.options.marker.fillOpacity,n=this.chart.styledMode;b.forEach(function(h,
r){n||(f.stroke=c(h.borderColor,a.borderColor,k.color),f.fill=c(h.color,a.color,1!==m?g(k.color).setOpacity(m).get("rgba"):k.color),l.stroke=c(h.connectorColor,a.connectorColor,k.color));b[r].radius=this.getRangeRadius(h.value);b[r]=t(b[r],{center:b[0].radius-b[r].radius+d});n||t(!0,b[r],{bubbleStyle:t(!1,f),connectorStyle:t(!1,l),labelStyle:p})},this)},getLabelStyles:function(){var b=this.options,a={},k="left"===b.labels.align,d=this.legend.options.rtl;v(b.labels.style,function(c,b){"color"!==b&&
"fontSize"!==b&&"z-index"!==b&&(a[b]=c)});return t(!1,a,{"font-size":b.labels.style.fontSize,fill:c(b.labels.style.color,"#000000"),"z-index":b.zIndex,align:d||k?"right":"left"})},getRangeRadius:function(c){var b=this.options;return this.chart.series[this.options.seriesIndex].getRadius.call(this,b.ranges[b.ranges.length-1].value,b.ranges[0].value,b.minSize,b.maxSize,c)},render:function(){var c=this.chart.renderer,b=this.options.zThreshold;this.symbols||(this.symbols={connectors:[],bubbleItems:[],
labels:[]});this.legendSymbol=c.g("bubble-legend");this.legendItem=c.g("bubble-legend-item");this.legendSymbol.translateX=0;this.legendSymbol.translateY=0;this.ranges.forEach(function(c){c.value>=b&&this.renderRange(c)},this);this.legendSymbol.add(this.legendItem);this.legendItem.add(this.legendGroup);this.hideOverlappingLabels()},renderRange:function(c){var b=this.options,a=b.labels,h=this.chart.renderer,k=this.symbols,d=k.labels,f=c.center,l=Math.abs(c.radius),p=b.connectorDistance,m=a.align,g=
a.style.fontSize,p=this.legend.options.rtl||"left"===m?-p:p,a=b.connectorWidth,n=this.ranges[0].radius,e=f-l-b.borderWidth/2+a/2,w,g=g/2-(this.fontMetrics.h-g)/2,q=h.styledMode;"center"===m&&(p=0,b.connectorDistance=0,c.labelStyle.align="center");m=e+b.labels.y;w=n+p+b.labels.x;k.bubbleItems.push(h.circle(n,f+((e%1?1:.5)-(a%2?0:.5)),l).attr(q?{}:c.bubbleStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-symbol "+(b.className||"")).add(this.legendSymbol));
k.connectors.push(h.path(h.crispLine(["M",n,e,"L",n+p,e],b.connectorWidth)).attr(q?{}:c.connectorStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-connectors "+(b.connectorClassName||"")).add(this.legendSymbol));c=h.text(this.formatLabel(c),w,m+g).attr(q?{}:c.labelStyle).addClass("highcharts-bubble-legend-labels "+(b.labels.className||"")).add(this.legendSymbol);d.push(c);c.placed=!0;c.alignAttr={x:w,y:m+g}},getMaxLabelSize:function(){var c,b;this.symbols.labels.forEach(function(a){b=
a.getBBox(!0);c=c?b.width>c.width?b:c:b});return c||{}},formatLabel:function(c){var b=this.options,h=b.labels.formatter;return(b=b.labels.format)?a.format(b,c):h?h.call(c):d(c.value,1)},hideOverlappingLabels:function(){var c=this.chart,b=this.symbols;!this.options.labels.allowOverlap&&b&&(c.hideOverlappingLabels(b.labels),b.labels.forEach(function(c,a){c.newOpacity?c.newOpacity!==c.oldOpacity&&b.connectors[a].show():b.connectors[a].hide()}))},getRanges:function(){var b=this.legend.bubbleLegend,a,
k=b.options.ranges,d,p=Number.MAX_VALUE,m=-Number.MAX_VALUE;b.chart.series.forEach(function(b){b.isBubble&&!b.ignoreSeries&&(d=b.zData.filter(f),d.length&&(p=c(b.options.zMin,Math.min(p,Math.max(w(d),!1===b.options.displayNegative?b.options.zThreshold:-Number.MAX_VALUE))),m=c(b.options.zMax,Math.max(m,l(d)))))});a=p===m?[{value:m}]:[{value:p},{value:(p+m)/2},{value:m,autoRanges:!0}];k.length&&k[0].radius&&a.reverse();a.forEach(function(b,c){k&&k[c]&&(a[c]=t(!1,k[c],b))});return a},predictBubbleSizes:function(){var b=
this.chart,c=this.fontMetrics,a=b.legend.options,k="horizontal"===a.layout,d=k?b.legend.lastLineHeight:0,f=b.plotSizeX,p=b.plotSizeY,l=b.series[this.options.seriesIndex],b=Math.ceil(l.minPxSize),m=Math.ceil(l.maxPxSize),l=l.options.maxSize,g=Math.min(p,f);if(a.floating||!/%$/.test(l))c=m;else if(l=parseFloat(l),c=(g+d-c.h/2)*l/100/(l/100+1),k&&p-c>=f||!k&&f-c>=p)c=m;return[b,Math.ceil(c)]},updateRanges:function(b,c){var a=this.legend.options.bubbleLegend;a.minSize=b;a.maxSize=c;a.ranges=this.getRanges()},
correctSizes:function(){var b=this.legend,c=this.chart.series[this.options.seriesIndex];1<Math.abs(Math.ceil(c.maxPxSize)-this.options.maxSize)&&(this.updateRanges(this.options.minSize,c.maxPxSize),b.render())}};n(a.Legend,"afterGetAllItems",function(c){var b=this.bubbleLegend,k=this.options,h=k.bubbleLegend,d=this.chart.getVisibleBubbleSeriesIndex();b&&b.ranges&&b.ranges.length&&(h.ranges.length&&(h.autoRanges=!!h.ranges[0].autoRanges),this.destroyItem(b));0<=d&&k.enabled&&h.enabled&&(h.seriesIndex=
d,this.bubbleLegend=new a.BubbleLegend(h,this),this.bubbleLegend.addToLegend(c.allItems))});m.prototype.getVisibleBubbleSeriesIndex=function(){for(var b=this.series,c=0;c<b.length;){if(b[c]&&b[c].isBubble&&b[c].visible&&b[c].zData.length)return c;c++}return-1};e.prototype.getLinesHeights=function(){var b=this.allItems,c=[],a,k=b.length,d,l=0;for(d=0;d<k;d++)if(b[d].legendItemHeight&&(b[d].itemHeight=b[d].legendItemHeight),b[d]===b[k-1]||b[d+1]&&b[d]._legendItemPos[1]!==b[d+1]._legendItemPos[1]){c.push({height:0});
a=c[c.length-1];for(l;l<=d;l++)b[l].itemHeight>a.height&&(a.height=b[l].itemHeight);a.step=d}return c};e.prototype.retranslateItems=function(b){var c,a,k,d=this.options.rtl,h=0;this.allItems.forEach(function(l,f){c=l.legendGroup.translateX;a=l._legendItemPos[1];if((k=l.movementX)||d&&l.ranges)k=d?c-l.options.maxSize/2:c+k,l.legendGroup.attr({translateX:k});f>b[h].step&&h++;l.legendGroup.attr({translateY:Math.round(a+b[h].height/2)});l._legendItemPos[1]=a+b[h].height/2})};n(q,"legendItemClick",function(){var b=
this.chart,c=this.visible,a=this.chart.legend;a&&a.bubbleLegend&&(this.visible=!c,this.ignoreSeries=c,b=0<=b.getVisibleBubbleSeriesIndex(),a.bubbleLegend.visible!==b&&(a.update({bubbleLegend:{enabled:b}}),a.bubbleLegend.visible=b),this.visible=c)});b(m.prototype,"drawChartBox",function(b,c,a){var k=this.legend,d=0<=this.getVisibleBubbleSeriesIndex(),h;k&&k.options.enabled&&k.bubbleLegend&&k.options.bubbleLegend.autoRanges&&d?(h=k.bubbleLegend.options,d=k.bubbleLegend.predictBubbleSizes(),k.bubbleLegend.updateRanges(d[0],
d[1]),h.placed||(k.group.placed=!1,k.allItems.forEach(function(b){b.legendGroup.translateY=null})),k.render(),this.getMargins(),this.axes.forEach(function(b){b.render();h.placed||(b.setScale(),b.updateNames(),v(b.ticks,function(b){b.isNew=!0;b.isNewLabel=!0}))}),h.placed=!0,this.getMargins(),b.call(this,c,a),k.bubbleLegend.correctSizes(),k.retranslateItems(k.getLinesHeights())):(b.call(this,c,a),k&&k.options.enabled&&k.bubbleLegend&&(k.render(),k.retranslateItems(k.getLinesHeights())))})});y(u,"parts-more/BubbleSeries.js",
[u["parts/Globals.js"]],function(a){var q=a.arrayMax,e=a.arrayMin,m=a.Axis,n=a.color,b=a.isNumber,g=a.noop,f=a.pick,d=a.pInt,v=a.Point,t=a.Series,x=a.seriesType,c=a.seriesTypes;x("bubble","scatter",{dataLabels:{formatter:function(){return this.point.z},inside:!0,verticalAlign:"middle"},animationLimit:250,marker:{lineColor:null,lineWidth:1,fillOpacity:.5,radius:null,states:{hover:{radiusPlus:0}},symbol:"circle"},minSize:8,maxSize:"20%",softThreshold:!1,states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},
turboThreshold:0,zThreshold:0,zoneAxis:"z"},{pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],specialGroup:"group",bubblePadding:!0,zoneAxis:"z",directTouch:!0,isBubble:!0,pointAttribs:function(b,c){var a=this.options.marker.fillOpacity;b=t.prototype.pointAttribs.call(this,b,c);1!==a&&(b.fill=n(b.fill).setOpacity(a).get("rgba"));return b},getRadii:function(b,c,a){var k,d=this.zData,f=a.minPxSize,p=a.maxPxSize,m=[],g;k=0;for(a=d.length;k<a;k++)g=d[k],m.push(this.getRadius(b,
c,f,p,g));this.radii=m},getRadius:function(c,a,d,l,h){var k=this.options,f="width"!==k.sizeBy,p=k.zThreshold,m=a-c;k.sizeByAbsoluteValue&&null!==h&&(h=Math.abs(h-p),m=Math.max(a-p,Math.abs(c-p)),c=0);b(h)?h<c?d=d/2-1:(c=0<m?(h-c)/m:.5,f&&0<=c&&(c=Math.sqrt(c)),d=Math.ceil(d+c*(l-d))/2):d=null;return d},animate:function(b){!b&&this.points.length<this.options.animationLimit&&(this.points.forEach(function(b){var c=b.graphic,a;c&&c.width&&(a={x:c.x,y:c.y,width:c.width,height:c.height},c.attr({x:b.plotX,
y:b.plotY,width:1,height:1}),c.animate(a,this.options.animation))},this),this.animate=null)},hasData:function(){return!!this.processedXData.length},translate:function(){var k,d=this.data,f,l,h=this.radii;c.scatter.prototype.translate.call(this);for(k=d.length;k--;)f=d[k],l=h?h[k]:0,b(l)&&l>=this.minPxSize/2?(f.marker=a.extend(f.marker,{radius:l,width:2*l,height:2*l}),f.dlBox={x:f.plotX-l,y:f.plotY-l,width:2*l,height:2*l}):f.shapeArgs=f.plotY=f.dlBox=void 0},alignDataLabel:c.column.prototype.alignDataLabel,
buildKDTree:g,applyZones:g},{haloPath:function(b){return v.prototype.haloPath.call(this,0===b?0:(this.marker?this.marker.radius||0:0)+b)},ttBelow:!1});m.prototype.beforePadding=function(){var c=this,p=this.len,m=this.chart,l=0,h=p,g=this.isXAxis,n=g?"xData":"yData",v=this.min,t={},x=Math.min(m.plotWidth,m.plotHeight),u=Number.MAX_VALUE,y=-Number.MAX_VALUE,C=this.max-v,B=p/C,D=[];this.series.forEach(function(b){var k=b.options;!b.bubblePadding||!b.visible&&m.options.chart.ignoreHiddenSeries||(c.allowZoomOutside=
!0,D.push(b),g&&(["minSize","maxSize"].forEach(function(b){var c=k[b],a=/%$/.test(c),c=d(c);t[b]=a?x*c/100:c}),b.minPxSize=t.minSize,b.maxPxSize=Math.max(t.maxSize,t.minSize),b=b.zData.filter(a.isNumber),b.length&&(u=f(k.zMin,Math.min(u,Math.max(e(b),!1===k.displayNegative?k.zThreshold:-Number.MAX_VALUE))),y=f(k.zMax,Math.max(y,q(b))))))});D.forEach(function(a){var d=a[n],k=d.length,f;g&&a.getRadii(u,y,a);if(0<C)for(;k--;)b(d[k])&&c.dataMin<=d[k]&&d[k]<=c.dataMax&&(f=a.radii[k],l=Math.min((d[k]-v)*
B-f,l),h=Math.max((d[k]-v)*B+f,h))});D.length&&0<C&&!this.isLog&&(h-=p,B*=(p+Math.max(0,l)-Math.min(h,p))/p,[["min","userMin",l],["max","userMax",h]].forEach(function(b){void 0===f(c.options[b[0]],c[b[1]])&&(c[b[0]]+=b[2]/B)}))}});y(u,"modules/networkgraph/integrations.js",[u["parts/Globals.js"]],function(a){a.networkgraphIntegrations={verlet:{attractiveForceFunction:function(a,e){return(e-a)/a},repulsiveForceFunction:function(a,e){return(e-a)/a*(e>a?1:0)},barycenter:function(){var a=this.options.gravitationalConstant,
e=this.barycenter.xFactor,m=this.barycenter.yFactor,e=(e-(this.box.left+this.box.width)/2)*a,m=(m-(this.box.top+this.box.height)/2)*a;this.nodes.forEach(function(a){a.fixedPosition||(a.plotX-=e/a.mass/a.degree,a.plotY-=m/a.mass/a.degree)})},repulsive:function(a,e,m){e=e*this.diffTemperature/a.mass/a.degree;a.fixedPosition||(a.plotX+=m.x*e,a.plotY+=m.y*e)},attractive:function(a,e,m){var n=a.getMass(),b=-m.x*e*this.diffTemperature;e=-m.y*e*this.diffTemperature;a.fromNode.fixedPosition||(a.fromNode.plotX-=
b*n.fromNode/a.fromNode.degree,a.fromNode.plotY-=e*n.fromNode/a.fromNode.degree);a.toNode.fixedPosition||(a.toNode.plotX+=b*n.toNode/a.toNode.degree,a.toNode.plotY+=e*n.toNode/a.toNode.degree)},integrate:function(a,e){var m=-a.options.friction,n=a.options.maxSpeed,b=(e.plotX+e.dispX-e.prevX)*m,m=(e.plotY+e.dispY-e.prevY)*m,g=Math.abs,f=g(b)/(b||1),g=g(m)/(m||1),b=f*Math.min(n,Math.abs(b)),m=g*Math.min(n,Math.abs(m));e.prevX=e.plotX+e.dispX;e.prevY=e.plotY+e.dispY;e.plotX+=b;e.plotY+=m;e.temperature=
a.vectorLength({x:b,y:m})},getK:function(a){return Math.pow(a.box.width*a.box.height/a.nodes.length,.5)}},euler:{attractiveForceFunction:function(a,e){return a*a/e},repulsiveForceFunction:function(a,e){return e*e/a},barycenter:function(){var a=this.options.gravitationalConstant,e=this.barycenter.xFactor,m=this.barycenter.yFactor;this.nodes.forEach(function(n){if(!n.fixedPosition){var b=n.getDegree(),b=b*(1+b/2);n.dispX+=(e-n.plotX)*a*b/n.degree;n.dispY+=(m-n.plotY)*a*b/n.degree}})},repulsive:function(a,
e,m,n){a.dispX+=m.x/n*e/a.degree;a.dispY+=m.y/n*e/a.degree},attractive:function(a,e,m,n){var b=a.getMass(),g=m.x/n*e;e*=m.y/n;a.fromNode.fixedPosition||(a.fromNode.dispX-=g*b.fromNode/a.fromNode.degree,a.fromNode.dispY-=e*b.fromNode/a.fromNode.degree);a.toNode.fixedPosition||(a.toNode.dispX+=g*b.toNode/a.toNode.degree,a.toNode.dispY+=e*b.toNode/a.toNode.degree)},integrate:function(a,e){var m;e.dispX+=e.dispX*a.options.friction;e.dispY+=e.dispY*a.options.friction;m=e.temperature=a.vectorLength({x:e.dispX,
y:e.dispY});0!==m&&(e.plotX+=e.dispX/m*Math.min(Math.abs(e.dispX),a.temperature),e.plotY+=e.dispY/m*Math.min(Math.abs(e.dispY),a.temperature))},getK:function(a){return Math.pow(a.box.width*a.box.height/a.nodes.length,.3)}}}});y(u,"modules/networkgraph/QuadTree.js",[u["parts/Globals.js"]],function(a){var q=a.QuadTreeNode=function(a){this.box=a;this.boxSize=Math.min(a.width,a.height);this.nodes=[];this.body=this.isInternal=!1;this.isEmpty=!0};a.extend(q.prototype,{insert:function(a,e){this.isInternal?
this.nodes[this.getBoxPosition(a)].insert(a,e-1):(this.isEmpty=!1,this.body?e?(this.isInternal=!0,this.divideBox(),!0!==this.body&&(this.nodes[this.getBoxPosition(this.body)].insert(this.body,e-1),this.body=!0),this.nodes[this.getBoxPosition(a)].insert(a,e-1)):this.nodes.push(a):(this.isInternal=!1,this.body=a))},updateMassAndCenter:function(){var a=0,e=0,b=0;this.isInternal?(this.nodes.forEach(function(g){g.isEmpty||(a+=g.mass,e+=g.plotX*g.mass,b+=g.plotY*g.mass)}),e/=a,b/=a):this.body&&(a=this.body.mass,
e=this.body.plotX,b=this.body.plotY);this.mass=a;this.plotX=e;this.plotY=b},divideBox:function(){var a=this.box.width/2,e=this.box.height/2;this.nodes[0]=new q({left:this.box.left,top:this.box.top,width:a,height:e});this.nodes[1]=new q({left:this.box.left+a,top:this.box.top,width:a,height:e});this.nodes[2]=new q({left:this.box.left+a,top:this.box.top+e,width:a,height:e});this.nodes[3]=new q({left:this.box.left,top:this.box.top+e,width:a,height:e})},getBoxPosition:function(a){var m=a.plotY<this.box.top+
this.box.height/2;return a.plotX<this.box.left+this.box.width/2?m?0:3:m?1:2}});var e=a.QuadTree=function(a,e,b,g){this.box={left:a,top:e,width:b,height:g};this.maxDepth=25;this.root=new q(this.box,"0");this.root.isInternal=!0;this.root.isRoot=!0;this.root.divideBox()};a.extend(e.prototype,{insertNodes:function(a){a.forEach(function(a){this.root.insert(a,this.maxDepth)},this)},visitNodeRecursive:function(a,e,b,g,f){var d;a||(a=this.root);a===this.root&&e&&(d=e(a));!1!==d&&(a.nodes.forEach(function(a){if(a.isInternal){e&&
(d=e(a));if(!1===d)return;this.visitNodeRecursive(a,e,b,g,f)}else a.body&&e&&e(a.body);b&&b(a)},this),a===this.root&&b&&b(a))},calculateMassAndCenter:function(){this.visitNodeRecursive(null,null,function(a){a.updateMassAndCenter()})},render:function(a,e){this.visitNodeRecursive(this.root,null,null,a,e)},clear:function(a){this.render(a,!0)},renderBox:function(a,e,b){a.graphic||b?b&&(a.graphic&&(a.graphic=a.graphic.destroy()),a.graphic2&&(a.graphic2=a.graphic2.destroy()),a.label&&(a.label=a.label.destroy())):
(a.graphic=e.renderer.rect(a.box.left+e.plotLeft,a.box.top+e.plotTop,a.box.width,a.box.height).attr({stroke:"rgba(100, 100, 100, 0.5)","stroke-width":2}).add(),isNaN(a.plotX)||(a.graphic2=e.renderer.circle(a.plotX,a.plotY,a.mass/10).attr({fill:"red",translateY:e.plotTop,translateX:e.plotLeft}).add()))}})});y(u,"modules/networkgraph/layouts.js",[u["parts/Globals.js"]],function(a){var q=a.pick,e=a.defined,m=a.addEvent,n=a.Chart;a.layouts={"reingold-fruchterman":function(){}};a.extend(a.layouts["reingold-fruchterman"].prototype,
{init:function(b){this.options=b;this.nodes=[];this.links=[];this.series=[];this.box={x:0,y:0,width:0,height:0};this.setInitialRendering(!0);this.integration=a.networkgraphIntegrations[b.integration];this.attractiveForce=q(b.attractiveForce,this.integration.attractiveForceFunction);this.repulsiveForce=q(b.repulsiveForce,this.integration.repulsiveForceFunction);this.approximation=b.approximation},start:function(){var a=this.series,g=this.options;this.currentStep=0;this.forces=a[0]&&a[0].forces||[];
this.initialRendering&&(this.initPositions(),a.forEach(function(a){a.render()}));this.setK();this.resetSimulation(g);g.enableSimulation&&this.step()},step:function(){var b=this,g=this.series,f=this.options;b.currentStep++;"barnes-hut"===b.approximation&&(b.createQuadTree(),b.quadTree.calculateMassAndCenter());b.forces.forEach(function(a){b[a+"Forces"](b.temperature)});b.applyLimits(b.temperature);b.temperature=b.coolDown(b.startTemperature,b.diffTemperature,b.currentStep);b.prevSystemTemperature=
b.systemTemperature;b.systemTemperature=b.getSystemTemperature();f.enableSimulation&&(g.forEach(function(a){a.chart&&a.render()}),b.maxIterations--&&isFinite(b.temperature)&&!b.isStable()?(b.simulation&&a.win.cancelAnimationFrame(b.simulation),b.simulation=a.win.requestAnimationFrame(function(){b.step()})):b.simulation=!1)},stop:function(){this.simulation&&a.win.cancelAnimationFrame(this.simulation)},setArea:function(a,g,f,d){this.box={left:a,top:g,width:f,height:d}},setK:function(){this.k=this.options.linkLength||
this.integration.getK(this)},addNodes:function(a){a.forEach(function(a){-1===this.nodes.indexOf(a)&&this.nodes.push(a)},this)},removeNode:function(a){a=this.nodes.indexOf(a);-1!==a&&this.nodes.splice(a,1)},removeLink:function(a){a=this.links.indexOf(a);-1!==a&&this.links.splice(a,1)},addLinks:function(a){a.forEach(function(a){-1===this.links.indexOf(a)&&this.links.push(a)},this)},addSeries:function(a){-1===this.series.indexOf(a)&&this.series.push(a)},clear:function(){this.nodes.length=0;this.links.length=
0;this.series.length=0;this.resetSimulation()},resetSimulation:function(){this.forcedStop=!1;this.systemTemperature=0;this.setMaxIterations();this.setTemperature();this.setDiffTemperature()},setMaxIterations:function(a){this.maxIterations=q(a,this.options.maxIterations)},setTemperature:function(){this.temperature=this.startTemperature=Math.sqrt(this.nodes.length)},setDiffTemperature:function(){this.diffTemperature=this.startTemperature/(this.options.maxIterations+1)},setInitialRendering:function(a){this.initialRendering=
a},createQuadTree:function(){this.quadTree=new a.QuadTree(this.box.left,this.box.top,this.box.width,this.box.height);this.quadTree.insertNodes(this.nodes)},initPositions:function(){var b=this.options.initialPositions;a.isFunction(b)?(b.call(this),this.nodes.forEach(function(a){e(a.prevX)||(a.prevX=a.plotX);e(a.prevY)||(a.prevY=a.plotY);a.dispX=0;a.dispY=0})):"circle"===b?this.setCircularPositions():this.setRandomPositions()},setCircularPositions:function(){function a(c){c.linksFrom.forEach(function(c){n[c.toNode.id]||
(n[c.toNode.id]=!0,m.push(c.toNode),a(c.toNode))})}var g=this.box,f=this.nodes,d=2*Math.PI/(f.length+1),e=f.filter(function(a){return 0===a.linksTo.length}),m=[],n={},c=this.options.initialPositionRadius;e.forEach(function(c){m.push(c);a(c)});m.length?f.forEach(function(a){-1===m.indexOf(a)&&m.push(a)}):m=f;m.forEach(function(a,b){a.plotX=a.prevX=q(a.plotX,g.width/2+c*Math.cos(b*d));a.plotY=a.prevY=q(a.plotY,g.height/2+c*Math.sin(b*d));a.dispX=0;a.dispY=0})},setRandomPositions:function(){function a(a){a=
a*a/Math.PI;return a-=Math.floor(a)}var g=this.box,f=this.nodes,d=f.length+1;f.forEach(function(b,f){b.plotX=b.prevX=q(b.plotX,g.width*a(f));b.plotY=b.prevY=q(b.plotY,g.height*a(d+f));b.dispX=0;b.dispY=0})},force:function(a){this.integration[a].apply(this,Array.prototype.slice.call(arguments,1))},barycenterForces:function(){this.getBarycenter();this.force("barycenter")},getBarycenter:function(){var a=0,g=0,f=0;this.nodes.forEach(function(b){g+=b.plotX*b.mass;f+=b.plotY*b.mass;a+=b.mass});return this.barycenter=
{x:g,y:f,xFactor:g/a,yFactor:f/a}},barnesHutApproximation:function(a,g){var b=this.getDistXY(a,g),d=this.vectorLength(b),e,m;a!==g&&0!==d&&(g.isInternal?g.boxSize/d<this.options.theta&&0!==d?(m=this.repulsiveForce(d,this.k),this.force("repulsive",a,m*g.mass,b,d),e=!1):e=!0:(m=this.repulsiveForce(d,this.k),this.force("repulsive",a,m*g.mass,b,d)));return e},repulsiveForces:function(){var a=this;"barnes-hut"===a.approximation?a.nodes.forEach(function(b){a.quadTree.visitNodeRecursive(null,function(f){return a.barnesHutApproximation(b,
f)})}):a.nodes.forEach(function(b){a.nodes.forEach(function(f){var d,e,g;b===f||b.fixedPosition||(g=a.getDistXY(b,f),e=a.vectorLength(g),0!==e&&(d=a.repulsiveForce(e,a.k),a.force("repulsive",b,d*f.mass,g,e)))})})},attractiveForces:function(){var a=this,e,f,d;a.links.forEach(function(b){b.fromNode&&b.toNode&&(e=a.getDistXY(b.fromNode,b.toNode),f=a.vectorLength(e),0!==f&&(d=a.attractiveForce(f,a.k),a.force("attractive",b,d,e,f)))})},applyLimits:function(){var a=this;a.nodes.forEach(function(b){b.fixedPosition||
(a.integration.integrate(a,b),a.applyLimitBox(b,a.box),b.dispX=0,b.dispY=0)})},applyLimitBox:function(a,e){var b=a.marker&&a.marker.radius||0;a.plotX=Math.max(Math.min(a.plotX,e.width-b),e.left+b);a.plotY=Math.max(Math.min(a.plotY,e.height-b),e.top+b)},coolDown:function(a,e,f){return a-e*f},isStable:function(){return.00001>Math.abs(this.systemTemperature-this.prevSystemTemperature)||0>=this.temperature},getSystemTemperature:function(){return this.nodes.reduce(function(a,e){return a+e.temperature},
0)},vectorLength:function(a){return Math.sqrt(a.x*a.x+a.y*a.y)},getDistR:function(a,e){a=this.getDistXY(a,e);return this.vectorLength(a)},getDistXY:function(a,e){var b=a.plotX-e.plotX;a=a.plotY-e.plotY;return{x:b,y:a,absX:Math.abs(b),absY:Math.abs(a)}}});m(n,"predraw",function(){this.graphLayoutsLookup&&this.graphLayoutsLookup.forEach(function(a){a.stop()})});m(n,"render",function(){function b(a){a.maxIterations--&&isFinite(a.temperature)&&!a.isStable()&&!a.options.enableSimulation&&(a.beforeStep&&
a.beforeStep(),a.step(),e=!1,f=!0)}var e,f=!1;if(this.graphLayoutsLookup){a.setAnimation(!1,this);for(this.graphLayoutsLookup.forEach(function(a){a.start()});!e;)e=!0,this.graphLayoutsLookup.forEach(b);f&&this.series.forEach(function(a){a&&a.layout&&a.render()})}})});y(u,"modules/networkgraph/draggable-nodes.js",[u["parts/Globals.js"]],function(a){var q=a.Chart,e=a.addEvent;a.dragNodesMixin={onMouseDown:function(a,e){e=this.chart.pointer.normalize(e);a.fixedPosition={chartX:e.chartX,chartY:e.chartY,
plotX:a.plotX,plotY:a.plotY};a.inDragMode=!0},onMouseMove:function(a,e){if(a.fixedPosition&&a.inDragMode){var b=this.chart,g=b.pointer.normalize(e);e=a.fixedPosition.chartX-g.chartX;g=a.fixedPosition.chartY-g.chartY;if(5<Math.abs(e)||5<Math.abs(g))e=a.fixedPosition.plotX-e,g=a.fixedPosition.plotY-g,b.isInsidePlot(e,g)&&(a.plotX=e,a.plotY=g,this.redrawHalo(a),this.layout.simulation?this.layout.resetSimulation():(this.layout.setInitialRendering(!1),this.layout.enableSimulation?this.layout.start():this.layout.setMaxIterations(1),
this.chart.redraw(),this.layout.setInitialRendering(!0)))}},onMouseUp:function(a){a.fixedPosition&&(this.layout.enableSimulation?this.layout.start():this.chart.redraw(),a.inDragMode=!1,this.options.fixedDraggable||delete a.fixedPosition)},redrawHalo:function(a){a&&this.halo&&this.halo.attr({d:a.haloPath(this.options.states.hover.halo.size)})}};e(q,"load",function(){var a=this,n,b,g;a.container&&(n=e(a.container,"mousedown",function(f){var d=a.hoverPoint;d&&d.series&&d.series.hasDraggableNodes&&d.series.options.draggable&&
(d.series.onMouseDown(d,f),b=e(a.container,"mousemove",function(a){return d&&d.series&&d.series.onMouseMove(d,a)}),g=e(a.container.ownerDocument,"mouseup",function(a){b();g();return d&&d.series&&d.series.onMouseUp(d,a)}))}));e(a,"destroy",function(){n()})})});y(u,"parts-more/PackedBubbleSeries.js",[u["parts/Globals.js"]],function(a){var q=a.seriesType,e=a.Series,m=a.Point,n=a.defined,b=a.pick,g=a.addEvent,f=a.Chart,d=a.Color,v=a.layouts["reingold-fruchterman"],t=a.seriesTypes.bubble.prototype.pointClass,
x=a.dragNodesMixin;a.networkgraphIntegrations.packedbubble={repulsiveForceFunction:function(a,b,d,f){return Math.min(a,(d.marker.radius+f.marker.radius)/2)},barycenter:function(){var a=this,b=a.options.gravitationalConstant,d=a.box,f=a.nodes,l,h;f.forEach(function(c){a.options.splitSeries&&!c.isParentNode?(l=c.series.parentNode.plotX,h=c.series.parentNode.plotY):(l=d.width/2,h=d.height/2);c.fixedPosition||(c.plotX-=(c.plotX-l)*b/(c.mass*Math.sqrt(f.length)),c.plotY-=(c.plotY-h)*b/(c.mass*Math.sqrt(f.length)))})},
repulsive:function(a,b,d,f){var c=b*this.diffTemperature/a.mass/a.degree;b=d.x*c;d=d.y*c;a.fixedPosition||(a.plotX+=b,a.plotY+=d);f.fixedPosition||(f.plotX-=b,f.plotY-=d)},integrate:a.networkgraphIntegrations.verlet.integrate,getK:a.noop};a.layouts.packedbubble=a.extendClass(v,{beforeStep:function(){this.options.marker&&this.series.forEach(function(a){a&&a.calculateParentRadius()})},setCircularPositions:function(){var a=this,d=a.box,f=a.nodes,e=2*Math.PI/(f.length+1),l,h,g=a.options.initialPositionRadius;
f.forEach(function(c,k){a.options.splitSeries&&!c.isParentNode?(l=c.series.parentNode.plotX,h=c.series.parentNode.plotY):(l=d.width/2,h=d.height/2);c.plotX=c.prevX=b(c.plotX,l+g*Math.cos(c.index||k*e));c.plotY=c.prevY=b(c.plotY,h+g*Math.sin(c.index||k*e));c.dispX=0;c.dispY=0})},repulsiveForces:function(){var a=this,b,d,f,l=a.options.bubblePadding;a.nodes.forEach(function(c){c.degree=c.mass;c.neighbours=0;a.nodes.forEach(function(k){b=0;c===k||c.fixedPosition||!a.options.seriesInteraction&&c.series!==
k.series||(f=a.getDistXY(c,k),d=a.vectorLength(f)-(c.marker.radius+k.marker.radius+l),0>d&&(c.degree+=.01,c.neighbours++,b=a.repulsiveForce(-d/Math.sqrt(c.neighbours),a.k,c,k)),a.force("repulsive",c,b*k.mass,f,k,d))})})},applyLimitBox:function(a){var c,b;this.options.splitSeries&&!a.isParentNode&&this.options.parentNodeLimit&&(c=this.getDistXY(a,a.series.parentNode),b=a.series.parentNodeRadius-a.marker.radius-this.vectorLength(c),0>b&&b>-2*a.marker.radius&&(a.plotX-=.01*c.x,a.plotY-=.01*c.y));v.prototype.applyLimitBox.apply(this,
arguments)},isStable:function(){return.00001>Math.abs(this.systemTemperature-this.prevSystemTemperature)||0>=this.temperature||0<this.systemTemperature&&.02>this.systemTemperature/this.nodes.length&&this.enableSimulation}});q("packedbubble","bubble",{minSize:"10%",maxSize:"50%",sizeBy:"area",zoneAxis:"y",tooltip:{pointFormat:"Value: {point.value}"},draggable:!0,useSimulation:!0,dataLabels:{formatter:function(){return this.point.value},parentNodeFormatter:function(){return this.name},parentNodeTextPath:{enabled:!0},
padding:0},layoutAlgorithm:{initialPositions:"circle",initialPositionRadius:20,bubblePadding:5,parentNodeLimit:!1,seriesInteraction:!0,dragBetweenSeries:!1,parentNodeOptions:{maxIterations:400,gravitationalConstant:.03,maxSpeed:50,initialPositionRadius:100,seriesInteraction:!0,marker:{fillColor:null,fillOpacity:1,lineWidth:1,lineColor:null,symbol:"circle"}},enableSimulation:!0,type:"packedbubble",integration:"packedbubble",maxIterations:1E3,splitSeries:!1,maxSpeed:5,gravitationalConstant:.01,friction:-.981}},
{hasDraggableNodes:!0,forces:["barycenter","repulsive"],pointArrayMap:["value"],pointValKey:"value",isCartesian:!1,axisTypes:[],noSharedTooltip:!0,accumulateAllPoints:function(a){var c=a.chart,b=[],d,f;for(d=0;d<c.series.length;d++)if(a=c.series[d],a.visible||!c.options.chart.ignoreHiddenSeries)for(f=0;f<a.yData.length;f++)b.push([null,null,a.yData[f],a.index,f,{id:f,marker:{radius:0}}]);return b},init:function(){e.prototype.init.apply(this,arguments);g(this,"updatedData",function(){this.chart.series.forEach(function(a){a.type===
this.type&&(a.isDirty=!0)},this)});return this},render:function(){var c=[];e.prototype.render.apply(this,arguments);this.options.dataLabels.allowOverlap||(this.data.forEach(function(b){a.isArray(b.dataLabels)&&b.dataLabels.forEach(function(a){c.push(a)})}),this.chart.hideOverlappingLabels(c))},setVisible:function(){var a=this;e.prototype.setVisible.apply(a,arguments);a.parentNodeLayout&&a.graph?a.visible?(a.graph.show(),a.parentNode.dataLabel&&a.parentNode.dataLabel.show()):(a.graph.hide(),a.parentNodeLayout.removeNode(a.parentNode),
a.parentNode.dataLabel&&a.parentNode.dataLabel.hide()):a.layout&&(a.visible?a.layout.addNodes(a.points):a.points.forEach(function(b){a.layout.removeNode(b)}))},drawDataLabels:function(){var a=this.options.dataLabels.textPath,b=this.points;e.prototype.drawDataLabels.apply(this,arguments);this.parentNode&&(this.parentNode.formatPrefix="parentNode",this.points=[this.parentNode],this.options.dataLabels.textPath=this.options.dataLabels.parentNodeTextPath,e.prototype.drawDataLabels.apply(this,arguments),
this.points=b,this.options.dataLabels.textPath=a)},seriesBox:function(){var b=this.chart,d=Math.max,f=Math.min,e,l=[b.plotLeft,b.plotLeft+b.plotWidth,b.plotTop,b.plotTop+b.plotHeight];this.data.forEach(function(a){n(a.plotX)&&n(a.plotY)&&a.marker.radius&&(e=a.marker.radius,l[0]=f(l[0],a.plotX-e),l[1]=d(l[1],a.plotX+e),l[2]=f(l[2],a.plotY-e),l[3]=d(l[3],a.plotY+e))});return a.isNumber(l.width/l.height)?l:null},calculateParentRadius:function(){var a;a=this.seriesBox();this.parentNodeRadius=Math.min(Math.max(Math.sqrt(2*
this.parentNodeMass/Math.PI)+20,20),a?Math.max(Math.sqrt(Math.pow(a.width,2)+Math.pow(a.height,2))/2+20,20):Math.sqrt(2*this.parentNodeMass/Math.PI)+20);this.parentNode&&(this.parentNode.marker.radius=this.parentNodeRadius)},drawGraph:function(){if(this.layout&&this.layout.options.splitSeries){var b=this.chart,k,f=this.layout.options.parentNodeOptions.marker,f={fill:f.fillColor||d(this.color).brighten(.4).get(),opacity:f.fillOpacity,stroke:f.lineColor||this.color,"stroke-width":f.lineWidth};k=this.visible?
"inherit":"hidden";this.parentNodesGroup||(this.parentNodesGroup=this.plotGroup("parentNodesGroup","parentNode",k,.1,b.seriesGroup),this.group.attr({zIndex:2}));this.calculateParentRadius();k=a.merge({x:this.parentNode.plotX-this.parentNodeRadius,y:this.parentNode.plotY-this.parentNodeRadius,width:2*this.parentNodeRadius,height:2*this.parentNodeRadius},f);this.graph?this.graph.attr(k):this.graph=this.parentNode.graphic=b.renderer.symbol(f.symbol).attr(k).add(this.parentNodesGroup)}},createParentNodes:function(){var a=
this,b=a.chart,d=a.parentNodeLayout,f,e=a.parentNode;a.parentNodeMass=0;a.points.forEach(function(b){a.parentNodeMass+=Math.PI*Math.pow(b.marker.radius,2)});a.calculateParentRadius();d.nodes.forEach(function(b){b.seriesIndex===a.index&&(f=!0)});d.setArea(0,0,b.plotWidth,b.plotHeight);f||(e||(e=(new t).init(this,{mass:a.parentNodeRadius/2,marker:{radius:a.parentNodeRadius},dataLabels:{inside:!1},dataLabelOnNull:!0,degree:a.parentNodeRadius,isParentNode:!0,seriesIndex:a.index})),a.parentNode&&(e.plotX=
a.parentNode.plotX,e.plotY=a.parentNode.plotY),a.parentNode=e,d.addSeries(a),d.addNodes([e]))},addSeriesLayout:function(){var b=this.options.layoutAlgorithm,d=this.chart.graphLayoutsStorage,f=this.chart.graphLayoutsLookup,e=a.merge(b,b.parentNodeOptions,{enableSimulation:this.layout.options.enableSimulation}),l;l=d[b.type+"-series"];l||(d[b.type+"-series"]=l=new a.layouts[b.type],l.init(e),f.splice(l.index,0,l));this.parentNodeLayout=l;this.createParentNodes()},addLayout:function(){var b=this.options.layoutAlgorithm,
d=this.chart.graphLayoutsStorage,f=this.chart.graphLayoutsLookup,e=this.chart.options.chart,l;d||(this.chart.graphLayoutsStorage=d={},this.chart.graphLayoutsLookup=f=[]);l=d[b.type];l||(b.enableSimulation=n(e.forExport)?!e.forExport:b.enableSimulation,d[b.type]=l=new a.layouts[b.type],l.init(b),f.splice(l.index,0,l));this.layout=l;this.points.forEach(function(a){a.mass=2;a.degree=1;a.collisionNmb=1});l.setArea(0,0,this.chart.plotWidth,this.chart.plotHeight);l.addSeries(this);l.addNodes(this.points)},
deferLayout:function(){var a=this.options.layoutAlgorithm;this.visible&&(this.addLayout(),a.splitSeries&&this.addSeriesLayout())},translate:function(){var b=this.chart,d=this.data,f=this.index,e,l,h,g,m=this.options.useSimulation;this.processedXData=this.xData;this.generatePoints();n(b.allDataPoints)||(b.allDataPoints=this.accumulateAllPoints(this),this.getPointRadius());m?h=b.allDataPoints:(h=this.placeBubbles(b.allDataPoints),this.options.draggable=!1);for(g=0;g<h.length;g++)h[g][3]===f&&(e=d[h[g][4]],
l=h[g][2],m||(e.plotX=h[g][0]-b.plotLeft+b.diffX,e.plotY=h[g][1]-b.plotTop+b.diffY),e.marker=a.extend(e.marker,{radius:l,width:2*l,height:2*l}));m&&this.deferLayout()},checkOverlap:function(a,b){var c=a[0]-b[0],d=a[1]-b[1];return-.001>Math.sqrt(c*c+d*d)-Math.abs(a[2]+b[2])},positionBubble:function(a,b,d){var c=Math.sqrt,f=Math.asin,e=Math.acos,k=Math.pow,g=Math.abs,c=c(k(a[0]-b[0],2)+k(a[1]-b[1],2)),e=e((k(c,2)+k(d[2]+b[2],2)-k(d[2]+a[2],2))/(2*(d[2]+b[2])*c)),f=f(g(a[0]-b[0])/c);a=(0>a[1]-b[1]?0:
Math.PI)+e+f*(0>(a[0]-b[0])*(a[1]-b[1])?1:-1);return[b[0]+(b[2]+d[2])*Math.sin(a),b[1]-(b[2]+d[2])*Math.cos(a),d[2],d[3],d[4]]},placeBubbles:function(a){var b=this.checkOverlap,c=this.positionBubble,d=[],f=1,e=0,g=0,m;m=[];var n;a=a.sort(function(a,b){return b[2]-a[2]});if(a.length){d.push([[0,0,a[0][2],a[0][3],a[0][4]]]);if(1<a.length)for(d.push([[0,0-a[1][2]-a[0][2],a[1][2],a[1][3],a[1][4]]]),n=2;n<a.length;n++)a[n][2]=a[n][2]||1,m=c(d[f][e],d[f-1][g],a[n]),b(m,d[f][0])?(d.push([]),g=0,d[f+1].push(c(d[f][e],
d[f][0],a[n])),f++,e=0):1<f&&d[f-1][g+1]&&b(m,d[f-1][g+1])?(g++,d[f].push(c(d[f][e],d[f-1][g],a[n])),e++):(e++,d[f].push(m));this.chart.stages=d;this.chart.rawPositions=[].concat.apply([],d);this.resizeRadius();m=this.chart.rawPositions}return m},resizeRadius:function(){var a=this.chart,b=a.rawPositions,d=Math.min,f=Math.max,e=a.plotLeft,h=a.plotTop,g=a.plotHeight,m=a.plotWidth,n,t,v,q,x,u;n=v=Number.POSITIVE_INFINITY;t=q=Number.NEGATIVE_INFINITY;for(u=0;u<b.length;u++)x=b[u][2],n=d(n,b[u][0]-x),
t=f(t,b[u][0]+x),v=d(v,b[u][1]-x),q=f(q,b[u][1]+x);u=[t-n,q-v];d=d.apply([],[(m-e)/u[0],(g-h)/u[1]]);if(1e-10<Math.abs(d-1)){for(u=0;u<b.length;u++)b[u][2]*=d;this.placeBubbles(b)}else a.diffY=g/2+h-v-(q-v)/2,a.diffX=m/2+e-n-(t-n)/2},calculateZExtremes:function(){var c=this.options.zMin,d=this.options.zMax,f=Infinity,e=-Infinity;if(c&&d)return[c,d];this.chart.series.forEach(function(b){b.yData.forEach(function(b){a.defined(b)&&(b>e&&(e=b),b<f&&(f=b))})});c=b(c,f);d=b(d,e);return[c,d]},getPointRadius:function(){var a=
this,b=a.chart,d=a.options,f=d.useSimulation,e=Math.min(b.plotWidth,b.plotHeight),h={},g=[],m=b.allDataPoints,n,t,v,q,x;["minSize","maxSize"].forEach(function(a){var b=parseInt(d[a],10),c=/%$/.test(d[a]);h[a]=c?e*b/100:b*Math.sqrt(m.length)});b.minRadius=n=h.minSize/Math.sqrt(m.length);b.maxRadius=t=h.maxSize/Math.sqrt(m.length);x=f?a.calculateZExtremes():[n,t];(m||[]).forEach(function(b,c){v=f?Math.max(Math.min(b[2],x[1]),x[0]):b[2];q=a.getRadius(x[0],x[1],n,t,v);0===q&&(q=null);m[c][2]=q;g.push(q)});
a.radii=g},redrawHalo:x.redrawHalo,onMouseDown:x.onMouseDown,onMouseMove:x.onMouseMove,onMouseUp:function(b){if(b.fixedPosition&&!b.removed){var c,d,f=this.layout,e=this.parentNodeLayout;e&&f.options.dragBetweenSeries&&e.nodes.forEach(function(e){b&&b.marker&&e!==b.series.parentNode&&(c=f.getDistXY(b,e),d=f.vectorLength(c)-e.marker.radius-b.marker.radius,0>d&&(e.series.addPoint(a.merge(b.options,{plotX:b.plotX,plotY:b.plotY}),!1),f.removeNode(b),b.remove()))});x.onMouseUp.apply(this,arguments)}},
destroy:function(){this.parentNode&&(this.parentNodeLayout.removeNode(this.parentNode),this.parentNode.dataLabel&&(this.parentNode.dataLabel=this.parentNode.dataLabel.destroy()));a.Series.prototype.destroy.apply(this,arguments)},alignDataLabel:a.Series.prototype.alignDataLabel},{destroy:function(){this.series.layout&&this.series.layout.removeNode(this);return m.prototype.destroy.apply(this,arguments)}});g(f,"beforeRedraw",function(){this.allDataPoints&&delete this.allDataPoints})});y(u,"parts-more/Polar.js",
[u["parts/Globals.js"]],function(a){var q=a.pick,e=a.Series,m=a.seriesTypes,n=a.wrap,b=e.prototype,g=a.Pointer.prototype;b.searchPointByAngle=function(a){var b=this.chart,f=this.xAxis.pane.center;return this.searchKDTree({clientX:180+-180/Math.PI*Math.atan2(a.chartX-f[0]-b.plotLeft,a.chartY-f[1]-b.plotTop)})};b.getConnectors=function(a,b,e,g){var d,c,f,m,n,l,h,t;c=g?1:0;d=0<=b&&b<=a.length-1?b:0>b?a.length-1+b:0;b=0>d-1?a.length-(1+c):d-1;c=d+1>a.length-1?c:d+1;f=a[b];c=a[c];m=f.plotX;f=f.plotY;n=
c.plotX;l=c.plotY;c=a[d].plotX;d=a[d].plotY;m=(1.5*c+m)/2.5;f=(1.5*d+f)/2.5;n=(1.5*c+n)/2.5;h=(1.5*d+l)/2.5;l=Math.sqrt(Math.pow(m-c,2)+Math.pow(f-d,2));t=Math.sqrt(Math.pow(n-c,2)+Math.pow(h-d,2));m=Math.atan2(f-d,m-c);h=Math.PI/2+(m+Math.atan2(h-d,n-c))/2;Math.abs(m-h)>Math.PI/2&&(h-=Math.PI);m=c+Math.cos(h)*l;f=d+Math.sin(h)*l;n=c+Math.cos(Math.PI+h)*t;h=d+Math.sin(Math.PI+h)*t;c={rightContX:n,rightContY:h,leftContX:m,leftContY:f,plotX:c,plotY:d};e&&(c.prevPointCont=this.getConnectors(a,b,!1,g));
return c};b.toXY=function(a){var b,f=this.chart,e=a.plotX;b=a.plotY;a.rectPlotX=e;a.rectPlotY=b;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-b);a.plotX=a.polarPlotX=b.x-f.plotLeft;a.plotY=a.polarPlotY=b.y-f.plotTop;this.kdByAngle?(f=(e/Math.PI*180+this.xAxis.pane.options.startAngle)%360,0>f&&(f+=360),a.clientX=f):a.clientX=a.plotX};m.spline&&(n(m.spline.prototype,"getPointSpline",function(a,b,e,g){this.chart.polar?g?(a=this.getConnectors(b,g,!0,this.connectEnds),a=["C",a.prevPointCont.rightContX,
a.prevPointCont.rightContY,a.leftContX,a.leftContY,a.plotX,a.plotY]):a=["M",e.plotX,e.plotY]:a=a.call(this,b,e,g);return a}),m.areasplinerange&&(m.areasplinerange.prototype.getPointSpline=m.spline.prototype.getPointSpline));a.addEvent(e,"afterTranslate",function(){var b=this.chart,d,e;if(b.polar){(this.kdByAngle=b.tooltip&&b.tooltip.shared)?this.searchPoint=this.searchPointByAngle:this.options.findNearestPointBy="xy";if(!this.preventPostTranslate)for(d=this.points,e=d.length;e--;)this.toXY(d[e]),
!b.hasParallelCoordinates&&!this.yAxis.reversed&&d[e].y<this.yAxis.min&&(d[e].isNull=!0);this.hasClipCircleSetter||(this.hasClipCircleSetter=!!a.addEvent(this,"afterRender",function(){var d;b.polar&&(d=this.yAxis.center,this.group.clip(b.renderer.clipCircle(d[0],d[1],d[2]/2)),this.setClip=a.noop)}))}},{order:2});n(b,"getGraphPath",function(a,b){var d=this,e,f,c;if(this.chart.polar){b=b||this.points;for(e=0;e<b.length;e++)if(!b[e].isNull){f=e;break}!1!==this.options.connectEnds&&void 0!==f&&(this.connectEnds=
!0,b.splice(b.length,0,b[f]),c=!0);b.forEach(function(a){void 0===a.polarPlotY&&d.toXY(a)})}e=a.apply(this,[].slice.call(arguments,1));c&&b.pop();return e});e=function(a,b){var d=this.chart,e=this.options.animation,f=this.group,c=this.markerGroup,k=this.xAxis.center,g=d.plotLeft,m=d.plotTop;d.polar?d.renderer.isSVG&&(!0===e&&(e={}),b?(a={translateX:k[0]+g,translateY:k[1]+m,scaleX:.001,scaleY:.001},f.attr(a),c&&c.attr(a)):(a={translateX:g,translateY:m,scaleX:1,scaleY:1},f.animate(a,e),c&&c.animate(a,
e),this.animate=null)):a.call(this,b)};n(b,"animate",e);m.column&&(m=m.column.prototype,m.polarArc=function(a,b,e,g){var d=this.xAxis.center,c=this.yAxis.len;return this.chart.renderer.symbols.arc(d[0],d[1],c-b,null,{start:e,end:g,innerR:c-q(a,c)})},n(m,"animate",e),n(m,"translate",function(a){var b=this.xAxis,e=b.startAngleRad,f,g,c;this.preventPostTranslate=!0;a.call(this);if(b.isRadial)for(f=this.points,c=f.length;c--;)g=f[c],a=g.barX+e,g.shapeType="path",g.shapeArgs={d:this.polarArc(g.yBottom,
g.plotY,a,a+g.pointWidth)},this.toXY(g),g.tooltipPos=[g.plotX,g.plotY],g.ttBelow=g.plotY>b.center[1]}),n(m,"alignDataLabel",function(a,d,e,g,m,c){this.chart.polar?(a=d.rectPlotX/Math.PI*180,null===g.align&&(g.align=20<a&&160>a?"left":200<a&&340>a?"right":"center"),null===g.verticalAlign&&(g.verticalAlign=45>a||315<a?"bottom":135<a&&225>a?"top":"middle"),b.alignDataLabel.call(this,d,e,g,m,c)):a.call(this,d,e,g,m,c)}));n(g,"getCoordinates",function(a,b){var d=this.chart,e={xAxis:[],yAxis:[]};d.polar?
d.axes.forEach(function(a){var c=a.isXAxis,f=a.center,g=b.chartX-f[0]-d.plotLeft,f=b.chartY-f[1]-d.plotTop;e[c?"xAxis":"yAxis"].push({axis:a,value:a.translate(c?Math.PI-Math.atan2(g,f):Math.sqrt(Math.pow(g,2)+Math.pow(f,2)),!0)})}):e=a.call(this,b);return e});a.SVGRenderer.prototype.clipCircle=function(b,d,e){var f=a.uniqueKey(),g=this.createElement("clipPath").attr({id:f}).add(this.defs);b=this.circle(b,d,e).add(g);b.id=f;b.clipPath=g;return b};a.addEvent(a.Chart,"getAxes",function(){this.pane||
(this.pane=[]);a.splat(this.options.pane).forEach(function(b){new a.Pane(b,this)},this)});a.addEvent(a.Chart,"afterDrawChartBox",function(){this.pane.forEach(function(a){a.render()})});n(a.Chart.prototype,"get",function(b,d){return a.find(this.pane,function(a){return a.options.id===d})||b.call(this,d)})});y(u,"masters/highcharts-more.src.js",[],function(){})});
//# sourceMappingURL=highcharts-more.js.map
(function(c){"object"===typeof module&&module.exports?(c["default"]=c,module.exports=c):"function"===typeof define&&define.amd?define("highcharts/highcharts-more",["highcharts"],function(A){c(A);c.Highcharts=A;return c}):c("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(c){function A(c,b,h,a){c.hasOwnProperty(b)||(c[b]=a.apply(null,h))}c=c?c._modules:{};A(c,"Extensions/Pane.js",[c["Core/Chart/Chart.js"],c["Core/Globals.js"],c["Core/Pointer.js"],c["Core/Utilities.js"],c["Mixins/CenteredSeries.js"]],
function(c,b,h,a,f){function q(e,f,m){return Math.sqrt(Math.pow(e-m[0],2)+Math.pow(f-m[1],2))<=m[2]/2}var u=a.addEvent,z=a.extend,E=a.merge,B=a.pick,e=a.splat;c.prototype.collectionsWithUpdate.push("pane");a=function(){function b(e,m){this.options=this.chart=this.center=this.background=void 0;this.coll="pane";this.defaultOptions={center:["50%","50%"],size:"85%",innerSize:"0%",startAngle:0};this.defaultBackgroundOptions={shape:"circle",borderWidth:1,borderColor:"#cccccc",backgroundColor:{linearGradient:{x1:0,
y1:0,x2:0,y2:1},stops:[[0,"#ffffff"],[1,"#e6e6e6"]]},from:-Number.MAX_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"};this.init(e,m)}b.prototype.init=function(e,m){this.chart=m;this.background=[];m.pane.push(this);this.setOptions(e)};b.prototype.setOptions=function(e){this.options=E(this.defaultOptions,this.chart.angular?{background:{}}:void 0,e)};b.prototype.render=function(){var b=this.options,m=this.options.background,f=this.chart.renderer;this.group||(this.group=f.g("pane-group").attr({zIndex:b.zIndex||
0}).add());this.updateCenter();if(m)for(m=e(m),b=Math.max(m.length,this.background.length||0),f=0;f<b;f++)m[f]&&this.axis?this.renderBackground(E(this.defaultBackgroundOptions,m[f]),f):this.background[f]&&(this.background[f]=this.background[f].destroy(),this.background.splice(f,1))};b.prototype.renderBackground=function(e,f){var b="animate",m={"class":"highcharts-pane "+(e.className||"")};this.chart.styledMode||z(m,{fill:e.backgroundColor,stroke:e.borderColor,"stroke-width":e.borderWidth});this.background[f]||
(this.background[f]=this.chart.renderer.path().add(this.group),b="attr");this.background[f][b]({d:this.axis.getPlotBandPath(e.from,e.to,e)}).attr(m)};b.prototype.updateCenter=function(e){this.center=(e||this.axis||{}).center=f.getCenter.call(this)};b.prototype.update=function(e,f){E(!0,this.options,e);E(!0,this.chart.options.pane,e);this.setOptions(this.options);this.render();this.chart.axes.forEach(function(e){e.pane===this&&(e.pane=null,e.update({},f))},this)};return b}();c.prototype.getHoverPane=
function(e){var f=this,b;e&&f.pane.forEach(function(m){var a=e.chartX-f.plotLeft,p=e.chartY-f.plotTop;q(f.inverted?p:a,f.inverted?a:p,m.center)&&(b=m)});return b};u(c,"afterIsInsidePlot",function(e){this.polar&&(e.isInsidePlot=this.pane.some(function(f){return q(e.x,e.y,f.center)}))});u(h,"beforeGetHoverData",function(e){var f=this.chart;f.polar&&(f.hoverPane=f.getHoverPane(e),e.filter=function(b){return b.visible&&!(!e.shared&&b.directTouch)&&B(b.options.enableMouseTracking,!0)&&(!f.hoverPane||b.xAxis.pane===
f.hoverPane)})});u(h,"afterGetHoverData",function(e){var f=this.chart;e.hoverPoint&&e.hoverPoint.plotX&&e.hoverPoint.plotY&&f.hoverPane&&!q(e.hoverPoint.plotX,e.hoverPoint.plotY,f.hoverPane.center)&&(e.hoverPoint=void 0)});b.Pane=a;return b.Pane});A(c,"Core/Axis/HiddenAxis.js",[],function(){return function(){function c(){}c.init=function(b){b.getOffset=function(){};b.redraw=function(){this.isDirty=!1};b.render=function(){this.isDirty=!1};b.createLabelCollector=function(){return function(){}};b.setScale=
function(){};b.setCategories=function(){};b.setTitle=function(){};b.isHidden=!0};return c}()});A(c,"Core/Axis/RadialAxis.js",[c["Core/Axis/Axis.js"],c["Core/Axis/Tick.js"],c["Core/Axis/HiddenAxis.js"],c["Core/Utilities.js"]],function(c,b,h,a){var f=a.addEvent,q=a.correctFloat,u=a.defined,z=a.extend,E=a.fireEvent,B=a.merge,e=a.pick,x=a.relativeLength,w=a.wrap;a=function(){function b(){}b.init=function(f){var b=c.prototype;f.setOptions=function(e){e=this.options=B(f.constructor.defaultOptions,this.defaultPolarOptions,
e);e.plotBands||(e.plotBands=[]);E(this,"afterSetOptions")};f.getOffset=function(){b.getOffset.call(this);this.chart.axisOffset[this.side]=0};f.getLinePath=function(f,l,k){f=this.pane.center;var d=this.chart,g=e(l,f[2]/2-this.offset);"undefined"===typeof k&&(k=this.horiz?0:this.center&&-this.center[3]/2);k&&(g+=k);this.isCircular||"undefined"!==typeof l?(l=this.chart.renderer.symbols.arc(this.left+f[0],this.top+f[1],g,g,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0}),l.xBounds=[this.left+
f[0]],l.yBounds=[this.top+f[1]-g]):(l=this.postTranslate(this.angleRad,g),l=[["M",this.center[0]+d.plotLeft,this.center[1]+d.plotTop],["L",l.x,l.y]]);return l};f.setAxisTranslation=function(){b.setAxisTranslation.call(this);this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):(this.center[2]-this.center[3])/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset:0)};f.beforeSetTickPositions=function(){this.autoConnect=
this.isCircular&&"undefined"===typeof e(this.userMax,this.options.max)&&q(this.endAngleRad-this.startAngleRad)===q(2*Math.PI);!this.isCircular&&this.chart.inverted&&this.max++;this.autoConnect&&(this.max+=this.categories&&1||this.pointRange||this.closestPointRange||0)};f.setAxisSize=function(){b.setAxisSize.call(this);if(this.isRadial){this.pane.updateCenter(this);var f=this.center=z([],this.pane.center);if(this.isCircular)this.sector=this.endAngleRad-this.startAngleRad;else{var l=this.postTranslate(this.angleRad,
f[3]/2);f[0]=l.x-this.chart.plotLeft;f[1]=l.y-this.chart.plotTop}this.len=this.width=this.height=(f[2]-f[3])*e(this.sector,1)/2}};f.getPosition=function(f,l){f=this.translate(f);return this.postTranslate(this.isCircular?f:this.angleRad,e(this.isCircular?l:0>f?0:f,this.center[2]/2)-this.offset)};f.postTranslate=function(e,l){var k=this.chart,d=this.center;e=this.startAngleRad+e;return{x:k.plotLeft+d[0]+Math.cos(e)*l,y:k.plotTop+d[1]+Math.sin(e)*l}};f.getPlotBandPath=function(f,l,k){var d=function(d){if("string"===
typeof d){var g=parseInt(d,10);n.test(d)&&(g=g*D/100);return g}return d},g=this.center,r=this.startAngleRad,D=g[2]/2,t=Math.min(this.offset,0),n=/%$/;var y=this.isCircular;var b=e(d(k.outerRadius),D),p=d(k.innerRadius);d=e(d(k.thickness),10);if("polygon"===this.options.gridLineInterpolation)t=this.getPlotLinePath({value:f}).concat(this.getPlotLinePath({value:l,reverse:!0}));else{f=Math.max(f,this.min);l=Math.min(l,this.max);f=this.translate(f);l=this.translate(l);y||(b=f||0,p=l||0);if("circle"!==
k.shape&&y)k=r+(f||0),r+=l||0;else{k=-Math.PI/2;r=1.5*Math.PI;var a=!0}b-=t;t=this.chart.renderer.symbols.arc(this.left+g[0],this.top+g[1],b,b,{start:Math.min(k,r),end:Math.max(k,r),innerR:e(p,b-(d-t)),open:a});y&&(y=(r+k)/2,a=this.left+g[0]+g[2]/2*Math.cos(y),t.xBounds=y>-Math.PI/2&&y<Math.PI/2?[a,this.chart.plotWidth]:[0,a],t.yBounds=[this.top+g[1]+g[2]/2*Math.sin(y)],t.yBounds[0]+=y>-Math.PI&&0>y||y>Math.PI?-10:10)}return t};f.getCrosshairPosition=function(e,l,k){var d=e.value,g=this.pane.center;
if(this.isCircular){if(u(d))e.point&&(r=e.point.shapeArgs||{},r.start&&(d=this.chart.inverted?this.translate(e.point.rectPlotY,!0):e.point.x));else{var r=e.chartX||0;var D=e.chartY||0;d=this.translate(Math.atan2(D-k,r-l)-this.startAngleRad,!0)}e=this.getPosition(d);r=e.x;D=e.y}else u(d)||(r=e.chartX,D=e.chartY),u(r)&&u(D)&&(k=g[1]+this.chart.plotTop,d=this.translate(Math.min(Math.sqrt(Math.pow(r-l,2)+Math.pow(D-k,2)),g[2]/2)-g[3]/2,!0));return[d,r||0,D||0]};f.getPlotLinePath=function(e){var l=this,
k=l.pane.center,d=l.chart,g=d.inverted,r=e.value,D=e.reverse,t=l.getPosition(r),n=l.pane.options.background?l.pane.options.background[0]||l.pane.options.background:{},f=n.innerRadius||"0%",b=n.outerRadius||"100%";n=k[0]+d.plotLeft;var a=k[1]+d.plotTop,p=t.x,m=t.y,h=l.height;t=k[3]/2;var v;e.isCrosshair&&(m=this.getCrosshairPosition(e,n,a),r=m[0],p=m[1],m=m[2]);if(l.isCircular)r=Math.sqrt(Math.pow(p-n,2)+Math.pow(m-a,2)),D="string"===typeof f?x(f,1):f/r,d="string"===typeof b?x(b,1):b/r,k&&t&&(r=t/
r,D<r&&(D=r),d<r&&(d=r)),k=[["M",n+D*(p-n),a-D*(a-m)],["L",p-(1-d)*(p-n),m+(1-d)*(a-m)]];else if((r=l.translate(r))&&(0>r||r>h)&&(r=0),"circle"===l.options.gridLineInterpolation)k=l.getLinePath(0,r,t);else if(k=[],d[g?"yAxis":"xAxis"].forEach(function(d){d.pane===l.pane&&(v=d)}),v)for(n=v.tickPositions,v.autoConnect&&(n=n.concat([n[0]])),D&&(n=n.slice().reverse()),r&&(r+=t),p=0;p<n.length;p++)a=v.getPosition(n[p],r),k.push(p?["L",a.x,a.y]:["M",a.x,a.y]);return k};f.getTitlePosition=function(){var e=
this.center,l=this.chart,k=this.options.title;return{x:l.plotLeft+e[0]+(k.x||0),y:l.plotTop+e[1]-{high:.5,middle:.25,low:0}[k.align]*e[2]+(k.y||0)}};f.createLabelCollector=function(){var e=this;return function(){if(e.isRadial&&e.tickPositions&&!0!==e.options.labels.allowOverlap)return e.tickPositions.map(function(l){return e.ticks[l]&&e.ticks[l].label}).filter(function(e){return!!e})}}};b.compose=function(a,m){f(a,"init",function(e){var l=this.chart,k=l.inverted,d=l.angular,g=l.polar,r=this.isXAxis,
D=this.coll,t=d&&r,n,f=l.options;e=e.userOptions.pane||0;e=this.pane=l.pane&&l.pane[e];if("colorAxis"===D)this.isRadial=!1;else{if(d){if(t?h.init(this):b.init(this),n=!r)this.defaultPolarOptions=b.defaultRadialGaugeOptions}else g&&(b.init(this),this.defaultPolarOptions=(n=this.horiz)?b.defaultCircularOptions:B("xAxis"===D?a.defaultOptions:a.defaultYAxisOptions,b.defaultRadialOptions),k&&"yAxis"===D&&(this.defaultPolarOptions.stackLabels=a.defaultYAxisOptions.stackLabels));d||g?(this.isRadial=!0,f.chart.zoomType=
null,this.labelCollector||(this.labelCollector=this.createLabelCollector()),this.labelCollector&&l.labelCollectors.push(this.labelCollector)):this.isRadial=!1;e&&n&&(e.axis=this);this.isCircular=n}});f(a,"afterInit",function(){var f=this.chart,l=this.options,k=this.pane,d=k&&k.options;f.angular&&this.isXAxis||!k||!f.angular&&!f.polar||(this.angleRad=(l.angle||0)*Math.PI/180,this.startAngleRad=(d.startAngle-90)*Math.PI/180,this.endAngleRad=(e(d.endAngle,d.startAngle+360)-90)*Math.PI/180,this.offset=
l.offset||0)});f(a,"autoLabelAlign",function(e){this.isRadial&&(e.align=void 0,e.preventDefault())});f(a,"destroy",function(){if(this.chart&&this.chart.labelCollectors){var e=this.labelCollector?this.chart.labelCollectors.indexOf(this.labelCollector):-1;0<=e&&this.chart.labelCollectors.splice(e,1)}});f(a,"initialAxisTranslation",function(){this.isRadial&&this.beforeSetTickPositions()});f(m,"afterGetPosition",function(e){this.axis.getPosition&&z(e.pos,this.axis.getPosition(this.pos))});f(m,"afterGetLabelPosition",
function(f){var l=this.axis,k=this.label;if(k){var d=k.getBBox(),g=l.options.labels,r=g.y,D=20,t=g.align,n=(l.translate(this.pos)+l.startAngleRad+Math.PI/2)/Math.PI*180%360,y=Math.round(n),b="end",a=0>y?y+360:y,m=a,h=0,v=0,p=null===g.y?.3*-d.height:0;if(l.isRadial){var c=l.getPosition(this.pos,l.center[2]/2+x(e(g.distance,-25),l.center[2]/2,-l.center[2]/2));"auto"===g.rotation?k.attr({rotation:n}):null===r&&(r=l.chart.renderer.fontMetrics(k.styles&&k.styles.fontSize).b-d.height/2);null===t&&(l.isCircular?
(d.width>l.len*l.tickInterval/(l.max-l.min)&&(D=0),t=n>D&&n<180-D?"left":n>180+D&&n<360-D?"right":"center"):t="center",k.attr({align:t}));if("auto"===t&&2===l.tickPositions.length&&l.isCircular){90<a&&180>a?a=180-a:270<a&&360>=a&&(a=540-a);180<m&&360>=m&&(m=360-m);if(l.pane.options.startAngle===y||l.pane.options.startAngle===y+360||l.pane.options.startAngle===y-360)b="start";t=-90<=y&&90>=y||-360<=y&&-270>=y||270<=y&&360>=y?"start"===b?"right":"left":"start"===b?"left":"right";70<m&&110>m&&(t="center");
15>a||180<=a&&195>a?h=.3*d.height:15<=a&&35>=a?h="start"===b?0:.75*d.height:195<=a&&215>=a?h="start"===b?.75*d.height:0:35<a&&90>=a?h="start"===b?.25*-d.height:d.height:215<a&&270>=a&&(h="start"===b?d.height:.25*-d.height);15>m?v="start"===b?.15*-d.height:.15*d.height:165<m&&180>=m&&(v="start"===b?.15*d.height:.15*-d.height);k.attr({align:t});k.translate(v,h+p)}f.pos.x=c.x+g.x;f.pos.y=c.y+r}}});w(m.prototype,"getMarkPath",function(e,l,k,d,g,r,D){var t=this.axis;t.isRadial?(e=t.getPosition(this.pos,
t.center[2]/2+d),l=["M",l,k,"L",e.x,e.y]):l=e.call(this,l,k,d,g,r,D);return l})};b.defaultCircularOptions={gridLineWidth:1,labels:{align:null,distance:15,x:0,y:null,style:{textOverflow:"none"}},maxPadding:0,minPadding:0,showLastLabel:!1,tickLength:0};b.defaultRadialGaugeOptions={labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2};
b.defaultRadialOptions={gridLineInterpolation:"circle",gridLineWidth:1,labels:{align:"right",x:-3,y:-2},showLastLabel:!1,title:{x:4,text:null,rotation:90}};return b}();a.compose(c,b);return a});A(c,"Series/AreaRangeSeries.js",[c["Core/Series/Series.js"],c["Core/Globals.js"],c["Core/Series/Point.js"],c["Core/Utilities.js"]],function(c,b,h,a){var f=a.defined,q=a.extend,u=a.isArray,z=a.isNumber,E=a.pick,B=c.seriesTypes.area.prototype,e=c.seriesTypes.column.prototype,x=h.prototype,w=b.Series.prototype;
c.seriesType("arearange","area",{lineWidth:1,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">\u25cf</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},trackByArea:!0,dataLabels:{align:void 0,verticalAlign:void 0,xLow:0,xHigh:0,yLow:0,yHigh:0}},{pointArrayMap:["low","high"],pointValKey:"low",deferTranslatePolar:!0,toYData:function(e){return[e.low,e.high]},highToXY:function(e){var f=this.chart,b=this.xAxis.postTranslate(e.rectPlotX,this.yAxis.len-e.plotHigh);
e.plotHighX=b.x-f.plotLeft;e.plotHigh=b.y-f.plotTop;e.plotLowX=e.plotX},translate:function(){var e=this,f=e.yAxis,b=!!e.modifyValue;B.translate.apply(e);e.points.forEach(function(a){var l=a.high,k=a.plotY;a.isNull?a.plotY=null:(a.plotLow=k,a.plotHigh=f.translate(b?e.modifyValue(l,a):l,0,1,0,1),b&&(a.yBottom=a.plotHigh))});this.chart.polar&&this.points.forEach(function(f){e.highToXY(f);f.tooltipPos=[(f.plotHighX+f.plotLowX)/2,(f.plotHigh+f.plotLow)/2]})},getGraphPath:function(e){var f=[],b=[],a,l=
B.getGraphPath;var k=this.options;var d=this.chart.polar,g=d&&!1!==k.connectEnds,r=k.connectNulls,D=k.step;e=e||this.points;for(a=e.length;a--;){var t=e[a];var n=d?{plotX:t.rectPlotX,plotY:t.yBottom,doCurve:!1}:{plotX:t.plotX,plotY:t.plotY,doCurve:!1};t.isNull||g||r||e[a+1]&&!e[a+1].isNull||b.push(n);var y={polarPlotY:t.polarPlotY,rectPlotX:t.rectPlotX,yBottom:t.yBottom,plotX:E(t.plotHighX,t.plotX),plotY:t.plotHigh,isNull:t.isNull};b.push(y);f.push(y);t.isNull||g||r||e[a-1]&&!e[a-1].isNull||b.push(n)}e=
l.call(this,e);D&&(!0===D&&(D="left"),k.step={left:"right",center:"center",right:"left"}[D]);f=l.call(this,f);b=l.call(this,b);k.step=D;k=[].concat(e,f);!this.chart.polar&&b[0]&&"M"===b[0][0]&&(b[0]=["L",b[0][1],b[0][2]]);this.graphPath=k;this.areaPath=e.concat(b);k.isArea=!0;k.xMap=e.xMap;this.areaPath.xMap=e.xMap;return k},drawDataLabels:function(){var e=this.points,f=e.length,b,a=[],l=this.options.dataLabels,k,d=this.chart.inverted;if(u(l)){var g=l[0]||{enabled:!1};var r=l[1]||{enabled:!1}}else g=
q({},l),g.x=l.xHigh,g.y=l.yHigh,r=q({},l),r.x=l.xLow,r.y=l.yLow;if(g.enabled||this._hasPointLabels){for(b=f;b--;)if(k=e[b]){var D=g.inside?k.plotHigh<k.plotLow:k.plotHigh>k.plotLow;k.y=k.high;k._plotY=k.plotY;k.plotY=k.plotHigh;a[b]=k.dataLabel;k.dataLabel=k.dataLabelUpper;k.below=D;d?g.align||(g.align=D?"right":"left"):g.verticalAlign||(g.verticalAlign=D?"top":"bottom")}this.options.dataLabels=g;w.drawDataLabels&&w.drawDataLabels.apply(this,arguments);for(b=f;b--;)if(k=e[b])k.dataLabelUpper=k.dataLabel,
k.dataLabel=a[b],delete k.dataLabels,k.y=k.low,k.plotY=k._plotY}if(r.enabled||this._hasPointLabels){for(b=f;b--;)if(k=e[b])D=r.inside?k.plotHigh<k.plotLow:k.plotHigh>k.plotLow,k.below=!D,d?r.align||(r.align=D?"left":"right"):r.verticalAlign||(r.verticalAlign=D?"bottom":"top");this.options.dataLabels=r;w.drawDataLabels&&w.drawDataLabels.apply(this,arguments)}if(g.enabled)for(b=f;b--;)if(k=e[b])k.dataLabels=[k.dataLabelUpper,k.dataLabel].filter(function(d){return!!d});this.options.dataLabels=l},alignDataLabel:function(){e.alignDataLabel.apply(this,
arguments)},drawPoints:function(){var e=this.points.length,b;w.drawPoints.apply(this,arguments);for(b=0;b<e;){var a=this.points[b];a.origProps={plotY:a.plotY,plotX:a.plotX,isInside:a.isInside,negative:a.negative,zone:a.zone,y:a.y};a.lowerGraphic=a.graphic;a.graphic=a.upperGraphic;a.plotY=a.plotHigh;f(a.plotHighX)&&(a.plotX=a.plotHighX);a.y=a.high;a.negative=a.high<(this.options.threshold||0);a.zone=this.zones.length&&a.getZone();this.chart.polar||(a.isInside=a.isTopInside="undefined"!==typeof a.plotY&&
0<=a.plotY&&a.plotY<=this.yAxis.len&&0<=a.plotX&&a.plotX<=this.xAxis.len);b++}w.drawPoints.apply(this,arguments);for(b=0;b<e;)a=this.points[b],a.upperGraphic=a.graphic,a.graphic=a.lowerGraphic,q(a,a.origProps),delete a.origProps,b++},setStackedPoints:b.noop},{setState:function(){var e=this.state,a=this.series,b=a.chart.polar;f(this.plotHigh)||(this.plotHigh=a.yAxis.toPixels(this.high,!0));f(this.plotLow)||(this.plotLow=this.plotY=a.yAxis.toPixels(this.low,!0));a.stateMarkerGraphic&&(a.lowerStateMarkerGraphic=
a.stateMarkerGraphic,a.stateMarkerGraphic=a.upperStateMarkerGraphic);this.graphic=this.upperGraphic;this.plotY=this.plotHigh;b&&(this.plotX=this.plotHighX);x.setState.apply(this,arguments);this.state=e;this.plotY=this.plotLow;this.graphic=this.lowerGraphic;b&&(this.plotX=this.plotLowX);a.stateMarkerGraphic&&(a.upperStateMarkerGraphic=a.stateMarkerGraphic,a.stateMarkerGraphic=a.lowerStateMarkerGraphic,a.lowerStateMarkerGraphic=void 0);x.setState.apply(this,arguments)},haloPath:function(){var e=this.series.chart.polar,
a=[];this.plotY=this.plotLow;e&&(this.plotX=this.plotLowX);this.isInside&&(a=x.haloPath.apply(this,arguments));this.plotY=this.plotHigh;e&&(this.plotX=this.plotHighX);this.isTopInside&&(a=a.concat(x.haloPath.apply(this,arguments)));return a},destroyElements:function(){["lowerGraphic","upperGraphic"].forEach(function(e){this[e]&&(this[e]=this[e].destroy())},this);this.graphic=null;return x.destroyElements.apply(this,arguments)},isValid:function(){return z(this.low)&&z(this.high)}});""});A(c,"Series/AreaSplineRangeSeries.js",
[c["Core/Series/Series.js"]],function(c){c.seriesType("areasplinerange","arearange",null,{getPointSpline:c.seriesTypes.spline.prototype.getPointSpline});""});A(c,"Series/ColumnRangeSeries.js",[c["Core/Series/Series.js"],c["Core/Globals.js"],c["Core/Options.js"],c["Core/Utilities.js"]],function(c,b,h,a){b=b.noop;h=h.defaultOptions;var f=a.clamp,q=a.merge,u=a.pick,z=c.seriesTypes.column.prototype;c.seriesType("columnrange","arearange",q(h.plotOptions.column,h.plotOptions.arearange,{pointRange:null,
marker:null,states:{hover:{halo:!1}}}),{translate:function(){var a=this,b=a.yAxis,e=a.xAxis,h=e.startAngleRad,c,m=a.chart,q=a.xAxis.isRadial,v=Math.max(m.chartWidth,m.chartHeight)+999,p;z.translate.apply(a);a.points.forEach(function(l){var k=l.shapeArgs,d=a.options.minPointLength;l.plotHigh=p=f(b.translate(l.high,0,1,0,1),-v,v);l.plotLow=f(l.plotY,-v,v);var g=p;var r=u(l.rectPlotY,l.plotY)-p;Math.abs(r)<d?(d-=r,r+=d,g-=d/2):0>r&&(r*=-1,g-=r);q?(c=l.barX+h,l.shapeType="arc",l.shapeArgs=a.polarArc(g+
r,g,c,c+l.pointWidth)):(k.height=r,k.y=g,l.tooltipPos=m.inverted?[b.len+b.pos-m.plotLeft-g-r/2,e.len+e.pos-m.plotTop-k.x-k.width/2,r]:[e.left-m.plotLeft+k.x+k.width/2,b.pos-m.plotTop+g+r/2,r])})},directTouch:!0,trackerGroups:["group","dataLabelsGroup"],drawGraph:b,getSymbol:b,crispCol:function(){return z.crispCol.apply(this,arguments)},drawPoints:function(){return z.drawPoints.apply(this,arguments)},drawTracker:function(){return z.drawTracker.apply(this,arguments)},getColumnMetrics:function(){return z.getColumnMetrics.apply(this,
arguments)},pointAttribs:function(){return z.pointAttribs.apply(this,arguments)},animate:function(){return z.animate.apply(this,arguments)},polarArc:function(){return z.polarArc.apply(this,arguments)},translate3dPoints:function(){return z.translate3dPoints.apply(this,arguments)},translate3dShapes:function(){return z.translate3dShapes.apply(this,arguments)}},{setState:z.pointClass.prototype.setState});""});A(c,"Series/ColumnPyramidSeries.js",[c["Core/Series/Series.js"],c["Series/ColumnSeries.js"],
c["Core/Utilities.js"]],function(c,b,h){var a=b.prototype,f=h.clamp,q=h.pick;c.seriesType("columnpyramid","column",{},{translate:function(){var b=this,h=b.chart,c=b.options,B=b.dense=2>b.closestPointRange*b.xAxis.transA;B=b.borderWidth=q(c.borderWidth,B?0:1);var e=b.yAxis,x=c.threshold,w=b.translatedThreshold=e.getThreshold(x),m=q(c.minPointLength,5),C=b.getColumnMetrics(),v=C.width,p=b.barW=Math.max(v,1+2*B),l=b.pointXOffset=C.offset;h.inverted&&(w-=.5);c.pointPadding&&(p=Math.ceil(p));a.translate.apply(b);
b.points.forEach(function(k){var d=q(k.yBottom,w),g=999+Math.abs(d),r=f(k.plotY,-g,e.len+g);g=k.plotX+l;var a=p/2,t=Math.min(r,d);d=Math.max(r,d)-t;var n;k.barX=g;k.pointWidth=v;k.tooltipPos=h.inverted?[e.len+e.pos-h.plotLeft-r,b.xAxis.len-g-a,d]:[g+a,r+e.pos-h.plotTop,d];r=x+(k.total||k.y);"percent"===c.stacking&&(r=x+(0>k.y)?-100:100);r=e.toPixels(r,!0);var y=(n=h.plotHeight-r-(h.plotHeight-w))?a*(t-r)/n:0;var G=n?a*(t+d-r)/n:0;n=g-y+a;y=g+y+a;var H=g+G+a;G=g-G+a;var u=t-m;var F=t+d;0>k.y&&(u=t,
F=t+d+m);h.inverted&&(H=h.plotWidth-t,n=r-(h.plotWidth-w),y=a*(r-H)/n,G=a*(r-(H-d))/n,n=g+a+y,y=n-2*y,H=g-G+a,G=g+G+a,u=t,F=t+d-m,0>k.y&&(F=t+d+m));k.shapeType="path";k.shapeArgs={x:n,y:u,width:y-n,height:d,d:[["M",n,u],["L",y,u],["L",H,F],["L",G,F],["Z"]]}})}});""});A(c,"Series/GaugeSeries.js",[c["Core/Series/Series.js"],c["Core/Globals.js"],c["Core/Utilities.js"]],function(c,b,h){var a=h.clamp,f=h.isNumber,q=h.merge,u=h.pick,z=h.pInt,E=b.Series;h=b.TrackerMixin;c.seriesType("gauge","line",{dataLabels:{borderColor:"#cccccc",
borderRadius:3,borderWidth:1,crop:!1,defer:!1,enabled:!0,verticalAlign:"top",y:15,zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1},{angular:!0,directTouch:!0,drawGraph:b.noop,fixedBox:!0,forceDL:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],translate:function(){var b=this.yAxis,e=this.options,h=b.center;this.generatePoints();this.points.forEach(function(c){var m=q(e.dial,c.dial),x=z(u(m.radius,"80%"))*h[2]/200,w=z(u(m.baseLength,"70%"))*x/100,p=z(u(m.rearLength,
"10%"))*x/100,l=m.baseWidth||3,k=m.topWidth||1,d=e.overshoot,g=b.startAngleRad+b.translate(c.y,null,null,null,!0);if(f(d)||!1===e.wrap)d=f(d)?d/180*Math.PI:0,g=a(g,b.startAngleRad-d,b.endAngleRad+d);g=180*g/Math.PI;c.shapeType="path";c.shapeArgs={d:m.path||[["M",-p,-l/2],["L",w,-l/2],["L",x,-k/2],["L",x,k/2],["L",w,l/2],["L",-p,l/2],["Z"]],translateX:h[0],translateY:h[1],rotation:g};c.plotX=h[0];c.plotY=h[1]})},drawPoints:function(){var a=this,e=a.chart,b=a.yAxis.center,f=a.pivot,h=a.options,c=h.pivot,
z=e.renderer;a.points.forEach(function(b){var f=b.graphic,k=b.shapeArgs,d=k.d,g=q(h.dial,b.dial);f?(f.animate(k),k.d=d):b.graphic=z[b.shapeType](k).attr({rotation:k.rotation,zIndex:1}).addClass("highcharts-dial").add(a.group);if(!e.styledMode)b.graphic[f?"animate":"attr"]({stroke:g.borderColor||"none","stroke-width":g.borderWidth||0,fill:g.backgroundColor||"#000000"})});f?f.animate({translateX:b[0],translateY:b[1]}):(a.pivot=z.circle(0,0,u(c.radius,5)).attr({zIndex:2}).addClass("highcharts-pivot").translate(b[0],
b[1]).add(a.group),e.styledMode||a.pivot.attr({"stroke-width":c.borderWidth||0,stroke:c.borderColor||"#cccccc",fill:c.backgroundColor||"#000000"}))},animate:function(a){var e=this;a||e.points.forEach(function(a){var b=a.graphic;b&&(b.attr({rotation:180*e.yAxis.startAngleRad/Math.PI}),b.animate({rotation:a.shapeArgs.rotation},e.options.animation))})},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);E.prototype.render.call(this);
this.group.clip(this.chart.clipRect)},setData:function(a,e){E.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();u(e,!0)&&this.chart.redraw()},hasData:function(){return!!this.points.length},drawTracker:h&&h.drawTrackerPoint},{setState:function(a){this.state=a}});""});A(c,"Series/BoxPlotSeries.js",[c["Core/Series/Series.js"],c["Series/ColumnSeries.js"],c["Core/Globals.js"],c["Core/Utilities.js"]],function(c,b,h,a){var f=b.prototype;b=h.noop;var q=a.pick;c.seriesType("boxplot",
"column",{threshold:null,tooltip:{pointFormat:'<span style="color:{point.color}">\u25cf</span> <b> {series.name}</b><br/>Maximum: {point.high}<br/>Upper quartile: {point.q3}<br/>Median: {point.median}<br/>Lower quartile: {point.q1}<br/>Minimum: {point.low}<br/>'},whiskerLength:"50%",fillColor:"#ffffff",lineWidth:1,medianWidth:2,whiskerWidth:2},{pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttribs:function(){return{}},
drawDataLabels:b,translate:function(){var a=this.yAxis,b=this.pointArrayMap;f.translate.apply(this);this.points.forEach(function(f){b.forEach(function(b){null!==f[b]&&(f[b+"Plot"]=a.translate(f[b],0,1,0,1))});f.plotHigh=f.highPlot})},drawPoints:function(){var a=this,b=a.options,f=a.chart,h=f.renderer,e,c,w,m,C,v,p=0,l,k,d,g,r=!1!==a.doQuartiles,D,t=a.options.whiskerLength;a.points.forEach(function(n){var y=n.graphic,G=y?"animate":"attr",x=n.shapeArgs,I={},F={},J={},K={},u=n.color||a.color;"undefined"!==
typeof n.plotY&&(l=Math.round(x.width),k=Math.floor(x.x),d=k+l,g=Math.round(l/2),e=Math.floor(r?n.q1Plot:n.lowPlot),c=Math.floor(r?n.q3Plot:n.lowPlot),w=Math.floor(n.highPlot),m=Math.floor(n.lowPlot),y||(n.graphic=y=h.g("point").add(a.group),n.stem=h.path().addClass("highcharts-boxplot-stem").add(y),t&&(n.whiskers=h.path().addClass("highcharts-boxplot-whisker").add(y)),r&&(n.box=h.path(void 0).addClass("highcharts-boxplot-box").add(y)),n.medianShape=h.path(void 0).addClass("highcharts-boxplot-median").add(y)),
f.styledMode||(F.stroke=n.stemColor||b.stemColor||u,F["stroke-width"]=q(n.stemWidth,b.stemWidth,b.lineWidth),F.dashstyle=n.stemDashStyle||b.stemDashStyle||b.dashStyle,n.stem.attr(F),t&&(J.stroke=n.whiskerColor||b.whiskerColor||u,J["stroke-width"]=q(n.whiskerWidth,b.whiskerWidth,b.lineWidth),J.dashstyle=n.whiskerDashStyle||b.whiskerDashStyle||b.dashStyle,n.whiskers.attr(J)),r&&(I.fill=n.fillColor||b.fillColor||u,I.stroke=b.lineColor||u,I["stroke-width"]=b.lineWidth||0,I.dashstyle=n.boxDashStyle||b.boxDashStyle||
b.dashStyle,n.box.attr(I)),K.stroke=n.medianColor||b.medianColor||u,K["stroke-width"]=q(n.medianWidth,b.medianWidth,b.lineWidth),K.dashstyle=n.medianDashStyle||b.medianDashStyle||b.dashStyle,n.medianShape.attr(K)),v=n.stem.strokeWidth()%2/2,p=k+g+v,y=[["M",p,c],["L",p,w],["M",p,e],["L",p,m]],n.stem[G]({d:y}),r&&(v=n.box.strokeWidth()%2/2,e=Math.floor(e)+v,c=Math.floor(c)+v,k+=v,d+=v,y=[["M",k,c],["L",k,e],["L",d,e],["L",d,c],["L",k,c],["Z"]],n.box[G]({d:y})),t&&(v=n.whiskers.strokeWidth()%2/2,w+=
v,m+=v,D=/%$/.test(t)?g*parseFloat(t)/100:t/2,y=[["M",p-D,w],["L",p+D,w],["M",p-D,m],["L",p+D,m]],n.whiskers[G]({d:y})),C=Math.round(n.medianPlot),v=n.medianShape.strokeWidth()%2/2,C+=v,y=[["M",k,C],["L",d,C]],n.medianShape[G]({d:y}))})},setStackedPoints:b});""});A(c,"Series/ErrorBarSeries.js",[c["Core/Series/Series.js"],c["Core/Globals.js"]],function(c,b){b=b.noop;var h=c.seriesTypes;c.seriesType("errorbar","boxplot",{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'<span style="color:{point.color}">\u25cf</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
whiskerWidth:null},{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,drawDataLabels:h.arearange?function(){var a=this.pointValKey;h.arearange.prototype.drawDataLabels.call(this);this.data.forEach(function(b){b.y=b[a]})}:b,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});""});A(c,"Series/WaterfallSeries.js",[c["Core/Axis/Axis.js"],c["Core/Series/Series.js"],
c["Core/Chart/Chart.js"],c["Core/Globals.js"],c["Core/Series/Point.js"],c["Extensions/Stacking.js"],c["Core/Utilities.js"]],function(c,b,h,a,f,q,u){var z=b.seriesTypes,E=u.addEvent,B=u.arrayMax,e=u.arrayMin,x=u.correctFloat,w=u.isNumber,m=u.objectEach,C=u.pick,v=a.Series,p;(function(e){function a(){var d=this.waterfall.stacks;d&&(d.changed=!1,delete d.alreadyChanged)}function d(){var d=this.options.stackLabels;d&&d.enabled&&this.waterfall.stacks&&this.waterfall.renderStackTotals()}function g(){for(var d=
this.axes,g=this.series,e=g.length;e--;)g[e].options.stacking&&(d.forEach(function(d){d.isXAxis||(d.waterfall.stacks.changed=!0)}),e=0)}function b(){this.waterfall||(this.waterfall=new f(this))}var f=function(){function d(d){this.axis=d;this.stacks={changed:!1}}d.prototype.renderStackTotals=function(){var d=this.axis,g=d.waterfall.stacks,e=d.stacking&&d.stacking.stackTotalGroup,a=new q(d,d.options.stackLabels,!1,0,void 0);this.dummyStackItem=a;m(g,function(d){m(d,function(d){a.total=d.stackTotal;
d.label&&(a.label=d.label);q.prototype.render.call(a,e);d.label=a.label;delete a.label})});a.total=null};return d}();e.Composition=f;e.compose=function(e,k){E(e,"init",b);E(e,"afterBuildStacks",a);E(e,"afterRender",d);E(k,"beforeRedraw",g)}})(p||(p={}));b.seriesType("waterfall","column",{dataLabels:{inside:!0},lineWidth:1,lineColor:"#333333",dashStyle:"Dot",borderColor:"#333333",states:{hover:{lineWidthPlus:0}}},{pointValKey:"y",showLine:!0,generatePoints:function(){var e;z.column.prototype.generatePoints.apply(this);
var a=0;for(e=this.points.length;a<e;a++){var d=this.points[a];var g=this.processedYData[a];if(d.isIntermediateSum||d.isSum)d.y=x(g)}},translate:function(){var e=this.options,a=this.yAxis,d,g=C(e.minPointLength,5),b=g/2,f=e.threshold,t=e.stacking,n=a.waterfall.stacks[this.stackKey];z.column.prototype.translate.apply(this);var y=d=f;var h=this.points;var c=0;for(e=h.length;c<e;c++){var m=h[c];var x=this.processedYData[c];var q=m.shapeArgs;var p=[0,x];var w=m.y;if(t){if(n){p=n[c];if("overlap"===t){var u=
p.stackState[p.stateIndex--];u=0<=w?u:u-w;Object.hasOwnProperty.call(p,"absolutePos")&&delete p.absolutePos;Object.hasOwnProperty.call(p,"absoluteNeg")&&delete p.absoluteNeg}else 0<=w?(u=p.threshold+p.posTotal,p.posTotal-=w):(u=p.threshold+p.negTotal,p.negTotal-=w,u-=w),!p.posTotal&&Object.hasOwnProperty.call(p,"absolutePos")&&(p.posTotal=p.absolutePos,delete p.absolutePos),!p.negTotal&&Object.hasOwnProperty.call(p,"absoluteNeg")&&(p.negTotal=p.absoluteNeg,delete p.absoluteNeg);m.isSum||(p.connectorThreshold=
p.threshold+p.stackTotal);a.reversed?(x=0<=w?u-w:u+w,w=u):(x=u,w=u-w);m.below=x<=C(f,0);q.y=a.translate(x,0,1,0,1);q.height=Math.abs(q.y-a.translate(w,0,1,0,1))}if(w=a.waterfall.dummyStackItem)w.x=c,w.label=n[c].label,w.setOffset(this.pointXOffset||0,this.barW||0,this.stackedYNeg[c],this.stackedYPos[c])}else u=Math.max(y,y+w)+p[0],q.y=a.translate(u,0,1,0,1),m.isSum?(q.y=a.translate(p[1],0,1,0,1),q.height=Math.min(a.translate(p[0],0,1,0,1),a.len)-q.y):m.isIntermediateSum?(0<=w?(x=p[1]+d,w=d):(x=d,
w=p[1]+d),a.reversed&&(x^=w,w^=x,x^=w),q.y=a.translate(x,0,1,0,1),q.height=Math.abs(q.y-Math.min(a.translate(w,0,1,0,1),a.len)),d+=p[1]):(q.height=0<x?a.translate(y,0,1,0,1)-q.y:a.translate(y,0,1,0,1)-a.translate(y-x,0,1,0,1),y+=x,m.below=y<C(f,0)),0>q.height&&(q.y+=q.height,q.height*=-1);m.plotY=q.y=Math.round(q.y)-this.borderWidth%2/2;q.height=Math.max(Math.round(q.height),.001);m.yBottom=q.y+q.height;q.height<=g&&!m.isNull?(q.height=g,q.y-=b,m.plotY=q.y,m.minPointLengthOffset=0>m.y?-b:b):(m.isNull&&
(q.width=0),m.minPointLengthOffset=0);q=m.plotY+(m.negative?q.height:0);this.chart.inverted?m.tooltipPos[0]=a.len-q:m.tooltipPos[1]=q}},processData:function(a){var e=this.options,d=this.yData,g=e.data,b=d.length,f=e.threshold||0,t,n,l,h,c;for(c=n=t=l=h=0;c<b;c++){var m=d[c];var q=g&&g[c]?g[c]:{};"sum"===m||q.isSum?d[c]=x(n):"intermediateSum"===m||q.isIntermediateSum?(d[c]=x(t),t=0):(n+=m,t+=m);l=Math.min(n,l);h=Math.max(n,h)}v.prototype.processData.call(this,a);e.stacking||(this.dataMin=l+f,this.dataMax=
h)},toYData:function(a){return a.isSum?"sum":a.isIntermediateSum?"intermediateSum":a.y},updateParallelArrays:function(a,e){v.prototype.updateParallelArrays.call(this,a,e);if("sum"===this.yData[0]||"intermediateSum"===this.yData[0])this.yData[0]=null},pointAttribs:function(a,e){var d=this.options.upColor;d&&!a.options.color&&(a.color=0<a.y?d:null);a=z.column.prototype.pointAttribs.call(this,a,e);delete a.dashstyle;return a},getGraphPath:function(){return[["M",0,0]]},getCrispPath:function(){var a=this.data,
e=this.yAxis,d=a.length,g=Math.round(this.graph.strokeWidth())%2/2,b=Math.round(this.borderWidth)%2/2,f=this.xAxis.reversed,t=this.yAxis.reversed,n=this.options.stacking,y=[],c;for(c=1;c<d;c++){var h=a[c].shapeArgs;var m=a[c-1];var q=a[c-1].shapeArgs;var p=e.waterfall.stacks[this.stackKey];var x=0<m.y?-q.height:0;p&&q&&h&&(p=p[c-1],n?(p=p.connectorThreshold,x=Math.round(e.translate(p,0,1,0,1)+(t?x:0))-g):x=q.y+m.minPointLengthOffset+b-g,y.push(["M",(q.x||0)+(f?0:q.width||0),x],["L",(h.x||0)+(f?h.width||
0:0),x]));!n&&y.length&&q&&(0>m.y&&!t||0<m.y&&t)&&(y[y.length-2][2]+=q.height,y[y.length-1][2]+=q.height)}return y},drawGraph:function(){v.prototype.drawGraph.call(this);this.graph.attr({d:this.getCrispPath()})},setStackedPoints:function(){function a(d,g,a,e){if(E)for(a;a<E;a++)v.stackState[a]+=e;else v.stackState[0]=d,E=v.stackState.length;v.stackState.push(v.stackState[E-1]+g)}var e=this.options,d=this.yAxis.waterfall.stacks,g=e.threshold,b=g||0,f=b,t=this.stackKey,n=this.xData,c=n.length,h,q,m;
this.yAxis.stacking.usePercentage=!1;var p=q=m=b;if(this.visible||!this.chart.options.chart.ignoreHiddenSeries){var x=d.changed;(h=d.alreadyChanged)&&0>h.indexOf(t)&&(x=!0);d[t]||(d[t]={});h=d[t];for(var w=0;w<c;w++){var u=n[w];if(!h[u]||x)h[u]={negTotal:0,posTotal:0,stackTotal:0,threshold:0,stateIndex:0,stackState:[],label:x&&h[u]?h[u].label:void 0};var v=h[u];var z=this.yData[w];0<=z?v.posTotal+=z:v.negTotal+=z;var B=e.data[w];u=v.absolutePos=v.posTotal;var C=v.absoluteNeg=v.negTotal;v.stackTotal=
u+C;var E=v.stackState.length;B&&B.isIntermediateSum?(a(m,q,0,m),m=q,q=g,b^=f,f^=b,b^=f):B&&B.isSum?(a(g,p,E),b=g):(a(b,z,0,p),B&&(p+=z,q+=z));v.stateIndex++;v.threshold=b;b+=v.stackTotal}d.changed=!1;d.alreadyChanged||(d.alreadyChanged=[]);d.alreadyChanged.push(t)}},getExtremes:function(){var a=this.options.stacking;if(a){var b=this.yAxis;b=b.waterfall.stacks;var d=this.stackedYNeg=[];var g=this.stackedYPos=[];"overlap"===a?m(b[this.stackKey],function(a){d.push(e(a.stackState));g.push(B(a.stackState))}):
m(b[this.stackKey],function(a){d.push(a.negTotal+a.threshold);g.push(a.posTotal+a.threshold)});return{dataMin:e(d),dataMax:B(g)}}return{dataMin:this.dataMin,dataMax:this.dataMax}}},{getClassName:function(){var a=f.prototype.getClassName.call(this);this.isSum?a+=" highcharts-sum":this.isIntermediateSum&&(a+=" highcharts-intermediate-sum");return a},isValid:function(){return w(this.y)||this.isSum||!!this.isIntermediateSum}});"";p.compose(c,h);return p});A(c,"Series/PolygonSeries.js",[c["Core/Series/Series.js"],
c["Core/Globals.js"],c["Mixins/LegendSymbol.js"]],function(c,b,h){var a=c.seriesTypes,f=b.Series;c.seriesType("polygon","scatter",{marker:{enabled:!1,states:{hover:{enabled:!1}}},stickyTracking:!1,tooltip:{followPointer:!0,pointFormat:""},trackByArea:!0},{type:"polygon",getGraphPath:function(){for(var a=f.prototype.getGraphPath.call(this),b=a.length+1;b--;)(b===a.length||"M"===a[b][0])&&0<b&&a.splice(b,0,["Z"]);return this.areaPath=a},drawGraph:function(){this.options.fillColor=this.color;a.area.prototype.drawGraph.call(this)},
drawLegendSymbol:h.drawRectangle,drawTracker:f.prototype.drawTracker,setStackedPoints:b.noop});""});A(c,"Series/Bubble/BubbleLegend.js",[c["Core/Chart/Chart.js"],c["Core/Color/Color.js"],c["Core/Globals.js"],c["Core/Legend.js"],c["Core/Utilities.js"]],function(c,b,h,a,f){var q=b.parse;b=f.addEvent;var u=f.arrayMax,z=f.arrayMin,E=f.isNumber,B=f.merge,e=f.objectEach,x=f.pick,w=f.setOptions,m=f.stableSort,C=f.wrap;"";var v=h.Series,p=h.noop;w({legend:{bubbleLegend:{borderColor:void 0,borderWidth:2,className:void 0,
color:void 0,connectorClassName:void 0,connectorColor:void 0,connectorDistance:60,connectorWidth:1,enabled:!1,labels:{className:void 0,allowOverlap:!1,format:"",formatter:void 0,align:"right",style:{fontSize:10,color:void 0},x:0,y:0},maxSize:60,minSize:10,legendIndex:0,ranges:{value:void 0,borderColor:void 0,color:void 0,connectorColor:void 0},sizeBy:"area",sizeByAbsoluteValue:!1,zIndex:1,zThreshold:0}}});w=function(){function a(a,d){this.options=this.symbols=this.visible=this.ranges=this.movementX=
this.maxLabel=this.legendSymbol=this.legendItemWidth=this.legendItemHeight=this.legendItem=this.legendGroup=this.legend=this.fontMetrics=this.chart=void 0;this.setState=p;this.init(a,d)}a.prototype.init=function(a,d){this.options=a;this.visible=!0;this.chart=d.chart;this.legend=d};a.prototype.addToLegend=function(a){a.splice(this.options.legendIndex,0,this)};a.prototype.drawLegendSymbol=function(a){var d=this.chart,g=this.options,e=x(a.options.itemDistance,20),b=g.ranges;var f=g.connectorDistance;
this.fontMetrics=d.renderer.fontMetrics(g.labels.style.fontSize.toString()+"px");b&&b.length&&E(b[0].value)?(m(b,function(d,a){return a.value-d.value}),this.ranges=b,this.setOptions(),this.render(),d=this.getMaxLabelSize(),b=this.ranges[0].radius,a=2*b,f=f-b+d.width,f=0<f?f:0,this.maxLabel=d,this.movementX="left"===g.labels.align?f:0,this.legendItemWidth=a+f+e,this.legendItemHeight=a+this.fontMetrics.h/2):a.options.bubbleLegend.autoRanges=!0};a.prototype.setOptions=function(){var a=this.ranges,d=
this.options,g=this.chart.series[d.seriesIndex],e=this.legend.baseline,b={"z-index":d.zIndex,"stroke-width":d.borderWidth},f={"z-index":d.zIndex,"stroke-width":d.connectorWidth},n=this.getLabelStyles(),c=g.options.marker.fillOpacity,h=this.chart.styledMode;a.forEach(function(t,r){h||(b.stroke=x(t.borderColor,d.borderColor,g.color),b.fill=x(t.color,d.color,1!==c?q(g.color).setOpacity(c).get("rgba"):g.color),f.stroke=x(t.connectorColor,d.connectorColor,g.color));a[r].radius=this.getRangeRadius(t.value);
a[r]=B(a[r],{center:a[0].radius-a[r].radius+e});h||B(!0,a[r],{bubbleStyle:B(!1,b),connectorStyle:B(!1,f),labelStyle:n})},this)};a.prototype.getLabelStyles=function(){var a=this.options,d={},g="left"===a.labels.align,b=this.legend.options.rtl;e(a.labels.style,function(a,g){"color"!==g&&"fontSize"!==g&&"z-index"!==g&&(d[g]=a)});return B(!1,d,{"font-size":a.labels.style.fontSize,fill:x(a.labels.style.color,"#000000"),"z-index":a.zIndex,align:b||g?"right":"left"})};a.prototype.getRangeRadius=function(a){var d=
this.options;return this.chart.series[this.options.seriesIndex].getRadius.call(this,d.ranges[d.ranges.length-1].value,d.ranges[0].value,d.minSize,d.maxSize,a)};a.prototype.render=function(){var a=this.chart.renderer,d=this.options.zThreshold;this.symbols||(this.symbols={connectors:[],bubbleItems:[],labels:[]});this.legendSymbol=a.g("bubble-legend");this.legendItem=a.g("bubble-legend-item");this.legendSymbol.translateX=0;this.legendSymbol.translateY=0;this.ranges.forEach(function(a){a.value>=d&&this.renderRange(a)},
this);this.legendSymbol.add(this.legendItem);this.legendItem.add(this.legendGroup);this.hideOverlappingLabels()};a.prototype.renderRange=function(a){var d=this.options,g=d.labels,e=this.chart.renderer,b=this.symbols,f=b.labels,n=a.center,k=Math.abs(a.radius),c=d.connectorDistance||0,h=g.align,l=g.style.fontSize;c=this.legend.options.rtl||"left"===h?-c:c;g=d.connectorWidth;var m=this.ranges[0].radius||0,q=n-k-d.borderWidth/2+g/2;l=l/2-(this.fontMetrics.h-l)/2;var p=e.styledMode;"center"===h&&(c=0,
d.connectorDistance=0,a.labelStyle.align="center");h=q+d.labels.y;var x=m+c+d.labels.x;b.bubbleItems.push(e.circle(m,n+((q%1?1:.5)-(g%2?0:.5)),k).attr(p?{}:a.bubbleStyle).addClass((p?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-symbol "+(d.className||"")).add(this.legendSymbol));b.connectors.push(e.path(e.crispLine([["M",m,q],["L",m+c,q]],d.connectorWidth)).attr(p?{}:a.connectorStyle).addClass((p?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-connectors "+
(d.connectorClassName||"")).add(this.legendSymbol));a=e.text(this.formatLabel(a),x,h+l).attr(p?{}:a.labelStyle).addClass("highcharts-bubble-legend-labels "+(d.labels.className||"")).add(this.legendSymbol);f.push(a);a.placed=!0;a.alignAttr={x:x,y:h+l}};a.prototype.getMaxLabelSize=function(){var a,d;this.symbols.labels.forEach(function(g){d=g.getBBox(!0);a=a?d.width>a.width?d:a:d});return a||{}};a.prototype.formatLabel=function(a){var d=this.options,g=d.labels.formatter;d=d.labels.format;var e=this.chart.numberFormatter;
return d?f.format(d,a):g?g.call(a):e(a.value,1)};a.prototype.hideOverlappingLabels=function(){var a=this.chart,d=this.symbols;!this.options.labels.allowOverlap&&d&&(a.hideOverlappingLabels(d.labels),d.labels.forEach(function(a,e){a.newOpacity?a.newOpacity!==a.oldOpacity&&d.connectors[e].show():d.connectors[e].hide()}))};a.prototype.getRanges=function(){var a=this.legend.bubbleLegend,d=a.options.ranges,g,e=Number.MAX_VALUE,b=-Number.MAX_VALUE;a.chart.series.forEach(function(d){d.isBubble&&!d.ignoreSeries&&
(g=d.zData.filter(E),g.length&&(e=x(d.options.zMin,Math.min(e,Math.max(z(g),!1===d.options.displayNegative?d.options.zThreshold:-Number.MAX_VALUE))),b=x(d.options.zMax,Math.max(b,u(g)))))});var f=e===b?[{value:b}]:[{value:e},{value:(e+b)/2},{value:b,autoRanges:!0}];d.length&&d[0].radius&&f.reverse();f.forEach(function(a,g){d&&d[g]&&(f[g]=B(!1,d[g],a))});return f};a.prototype.predictBubbleSizes=function(){var a=this.chart,d=this.fontMetrics,g=a.legend.options,e="horizontal"===g.layout,b=e?a.legend.lastLineHeight:
0,f=a.plotSizeX,n=a.plotSizeY,c=a.series[this.options.seriesIndex];a=Math.ceil(c.minPxSize);var h=Math.ceil(c.maxPxSize);c=c.options.maxSize;var l=Math.min(n,f);if(g.floating||!/%$/.test(c))d=h;else if(c=parseFloat(c),d=(l+b-d.h/2)*c/100/(c/100+1),e&&n-d>=f||!e&&f-d>=n)d=h;return[a,Math.ceil(d)]};a.prototype.updateRanges=function(a,d){var g=this.legend.options.bubbleLegend;g.minSize=a;g.maxSize=d;g.ranges=this.getRanges()};a.prototype.correctSizes=function(){var a=this.legend,d=this.chart.series[this.options.seriesIndex];
1<Math.abs(Math.ceil(d.maxPxSize)-this.options.maxSize)&&(this.updateRanges(this.options.minSize,d.maxPxSize),a.render())};return a}();b(a,"afterGetAllItems",function(a){var e=this.bubbleLegend,d=this.options,g=d.bubbleLegend,b=this.chart.getVisibleBubbleSeriesIndex();e&&e.ranges&&e.ranges.length&&(g.ranges.length&&(g.autoRanges=!!g.ranges[0].autoRanges),this.destroyItem(e));0<=b&&d.enabled&&g.enabled&&(g.seriesIndex=b,this.bubbleLegend=new h.BubbleLegend(g,this),this.bubbleLegend.addToLegend(a.allItems))});
c.prototype.getVisibleBubbleSeriesIndex=function(){for(var a=this.series,e=0;e<a.length;){if(a[e]&&a[e].isBubble&&a[e].visible&&a[e].zData.length)return e;e++}return-1};a.prototype.getLinesHeights=function(){var a=this.allItems,e=[],d=a.length,g,b=0;for(g=0;g<d;g++)if(a[g].legendItemHeight&&(a[g].itemHeight=a[g].legendItemHeight),a[g]===a[d-1]||a[g+1]&&a[g]._legendItemPos[1]!==a[g+1]._legendItemPos[1]){e.push({height:0});var f=e[e.length-1];for(b;b<=g;b++)a[b].itemHeight>f.height&&(f.height=a[b].itemHeight);
f.step=g}return e};a.prototype.retranslateItems=function(a){var e,d,g,b=this.options.rtl,f=0;this.allItems.forEach(function(t,n){e=t.legendGroup.translateX;d=t._legendItemPos[1];if((g=t.movementX)||b&&t.ranges)g=b?e-t.options.maxSize/2:e+g,t.legendGroup.attr({translateX:g});n>a[f].step&&f++;t.legendGroup.attr({translateY:Math.round(d+a[f].height/2)});t._legendItemPos[1]=d+a[f].height/2})};b(v,"legendItemClick",function(){var a=this.chart,e=this.visible,d=this.chart.legend;d&&d.bubbleLegend&&(this.visible=
!e,this.ignoreSeries=e,a=0<=a.getVisibleBubbleSeriesIndex(),d.bubbleLegend.visible!==a&&(d.update({bubbleLegend:{enabled:a}}),d.bubbleLegend.visible=a),this.visible=e)});C(c.prototype,"drawChartBox",function(a,b,d){var g=this.legend,f=0<=this.getVisibleBubbleSeriesIndex();if(g&&g.options.enabled&&g.bubbleLegend&&g.options.bubbleLegend.autoRanges&&f){var c=g.bubbleLegend.options;f=g.bubbleLegend.predictBubbleSizes();g.bubbleLegend.updateRanges(f[0],f[1]);c.placed||(g.group.placed=!1,g.allItems.forEach(function(d){d.legendGroup.translateY=
null}));g.render();this.getMargins();this.axes.forEach(function(d){d.visible&&d.render();c.placed||(d.setScale(),d.updateNames(),e(d.ticks,function(d){d.isNew=!0;d.isNewLabel=!0}))});c.placed=!0;this.getMargins();a.call(this,b,d);g.bubbleLegend.correctSizes();g.retranslateItems(g.getLinesHeights())}else a.call(this,b,d),g&&g.options.enabled&&g.bubbleLegend&&(g.render(),g.retranslateItems(g.getLinesHeights()))});h.BubbleLegend=w;return h.BubbleLegend});A(c,"Series/Bubble/BubbleSeries.js",[c["Core/Axis/Axis.js"],
c["Core/Series/Series.js"],c["Core/Color/Color.js"],c["Core/Globals.js"],c["Core/Series/Point.js"],c["Core/Utilities.js"]],function(c,b,h,a,f,q){var u=h.parse;h=a.noop;var z=q.arrayMax,E=q.arrayMin,B=q.clamp,e=q.extend,x=q.isNumber,w=q.pick,m=q.pInt,C=a.Series,v=b.seriesTypes;"";b.seriesType("bubble","scatter",{dataLabels:{formatter:function(){return this.point.z},inside:!0,verticalAlign:"middle"},animationLimit:250,marker:{lineColor:null,lineWidth:1,fillOpacity:.5,radius:null,states:{hover:{radiusPlus:0}},
symbol:"circle"},minSize:8,maxSize:"20%",softThreshold:!1,states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0,zoneAxis:"z"},{pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],specialGroup:"group",bubblePadding:!0,zoneAxis:"z",directTouch:!0,isBubble:!0,pointAttribs:function(a,e){var b=this.options.marker.fillOpacity;a=C.prototype.pointAttribs.call(this,a,e);1!==b&&(a.fill=u(a.fill).setOpacity(b).get("rgba"));
return a},getRadii:function(a,e,b){var d=this.zData,g=this.yData,f=b.minPxSize,c=b.maxPxSize,t=[];var n=0;for(b=d.length;n<b;n++){var h=d[n];t.push(this.getRadius(a,e,f,c,h,g[n]))}this.radii=t},getRadius:function(a,e,b,d,g,f){var r=this.options,t="width"!==r.sizeBy,n=r.zThreshold,c=e-a,h=.5;if(null===f||null===g)return null;if(x(g)){r.sizeByAbsoluteValue&&(g=Math.abs(g-n),c=Math.max(e-n,Math.abs(a-n)),a=0);if(g<a)return b/2-1;0<c&&(h=(g-a)/c)}t&&0<=h&&(h=Math.sqrt(h));return Math.ceil(b+h*(d-b))/
2},animate:function(a){!a&&this.points.length<this.options.animationLimit&&this.points.forEach(function(a){var e=a.graphic;e&&e.width&&(this.hasRendered||e.attr({x:a.plotX,y:a.plotY,width:1,height:1}),e.animate(this.markerAttribs(a),this.options.animation))},this)},hasData:function(){return!!this.processedXData.length},translate:function(){var a,b=this.data,f=this.radii;v.scatter.prototype.translate.call(this);for(a=b.length;a--;){var d=b[a];var g=f?f[a]:0;x(g)&&g>=this.minPxSize/2?(d.marker=e(d.marker,
{radius:g,width:2*g,height:2*g}),d.dlBox={x:d.plotX-g,y:d.plotY-g,width:2*g,height:2*g}):d.shapeArgs=d.plotY=d.dlBox=void 0}},alignDataLabel:v.column.prototype.alignDataLabel,buildKDTree:h,applyZones:h},{haloPath:function(a){return f.prototype.haloPath.call(this,0===a?0:(this.marker?this.marker.radius||0:0)+a)},ttBelow:!1});c.prototype.beforePadding=function(){var a=this,e=this.len,b=this.chart,d=0,g=e,f=this.isXAxis,c=f?"xData":"yData",t=this.min,n={},h=Math.min(b.plotWidth,b.plotHeight),q=Number.MAX_VALUE,
u=-Number.MAX_VALUE,v=this.max-t,F=e/v,C=[];this.series.forEach(function(d){var e=d.options;!d.bubblePadding||!d.visible&&b.options.chart.ignoreHiddenSeries||(a.allowZoomOutside=!0,C.push(d),f&&(["minSize","maxSize"].forEach(function(d){var a=e[d],g=/%$/.test(a);a=m(a);n[d]=g?h*a/100:a}),d.minPxSize=n.minSize,d.maxPxSize=Math.max(n.maxSize,n.minSize),d=d.zData.filter(x),d.length&&(q=w(e.zMin,B(E(d),!1===e.displayNegative?e.zThreshold:-Number.MAX_VALUE,q)),u=w(e.zMax,Math.max(u,z(d))))))});C.forEach(function(e){var b=
e[c],n=b.length;f&&e.getRadii(q,u,e);if(0<v)for(;n--;)if(x(b[n])&&a.dataMin<=b[n]&&b[n]<=a.max){var r=e.radii?e.radii[n]:0;d=Math.min((b[n]-t)*F-r,d);g=Math.max((b[n]-t)*F+r,g)}});C.length&&0<v&&!this.logarithmic&&(g-=e,F*=(e+Math.max(0,d)-Math.min(g,e))/e,[["min","userMin",d],["max","userMax",g]].forEach(function(d){"undefined"===typeof w(a.options[d[0]],a[d[1]])&&(a[d[0]]+=d[2]/F)}))};""});A(c,"Series/Networkgraph/DraggableNodes.js",[c["Core/Chart/Chart.js"],c["Core/Globals.js"],c["Core/Utilities.js"]],
function(c,b,h){var a=h.addEvent;b.dragNodesMixin={onMouseDown:function(a,b){b=this.chart.pointer.normalize(b);a.fixedPosition={chartX:b.chartX,chartY:b.chartY,plotX:a.plotX,plotY:a.plotY};a.inDragMode=!0},onMouseMove:function(a,b){if(a.fixedPosition&&a.inDragMode){var f=this.chart;b=f.pointer.normalize(b);var c=a.fixedPosition.chartX-b.chartX,h=a.fixedPosition.chartY-b.chartY;b=f.graphLayoutsLookup;if(5<Math.abs(c)||5<Math.abs(h))c=a.fixedPosition.plotX-c,h=a.fixedPosition.plotY-h,f.isInsidePlot(c,
h)&&(a.plotX=c,a.plotY=h,a.hasDragged=!0,this.redrawHalo(a),b.forEach(function(a){a.restartSimulation()}))}},onMouseUp:function(a,b){a.fixedPosition&&(a.hasDragged&&(this.layout.enableSimulation?this.layout.start():this.chart.redraw()),a.inDragMode=a.hasDragged=!1,this.options.fixedDraggable||delete a.fixedPosition)},redrawHalo:function(a){a&&this.halo&&this.halo.attr({d:a.haloPath(this.options.states.hover.halo.size)})}};a(c,"load",function(){var b=this,c,h,z;b.container&&(c=a(b.container,"mousedown",
function(f){var c=b.hoverPoint;c&&c.series&&c.series.hasDraggableNodes&&c.series.options.draggable&&(c.series.onMouseDown(c,f),h=a(b.container,"mousemove",function(a){return c&&c.series&&c.series.onMouseMove(c,a)}),z=a(b.container.ownerDocument,"mouseup",function(a){h();z();return c&&c.series&&c.series.onMouseUp(c,a)}))}));a(b,"destroy",function(){c()})})});A(c,"Series/Networkgraph/Integrations.js",[c["Core/Globals.js"]],function(c){c.networkgraphIntegrations={verlet:{attractiveForceFunction:function(b,
c){return(c-b)/b},repulsiveForceFunction:function(b,c){return(c-b)/b*(c>b?1:0)},barycenter:function(){var b=this.options.gravitationalConstant,c=this.barycenter.xFactor,a=this.barycenter.yFactor;c=(c-(this.box.left+this.box.width)/2)*b;a=(a-(this.box.top+this.box.height)/2)*b;this.nodes.forEach(function(b){b.fixedPosition||(b.plotX-=c/b.mass/b.degree,b.plotY-=a/b.mass/b.degree)})},repulsive:function(b,c,a){c=c*this.diffTemperature/b.mass/b.degree;b.fixedPosition||(b.plotX+=a.x*c,b.plotY+=a.y*c)},
attractive:function(b,c,a){var f=b.getMass(),h=-a.x*c*this.diffTemperature;c=-a.y*c*this.diffTemperature;b.fromNode.fixedPosition||(b.fromNode.plotX-=h*f.fromNode/b.fromNode.degree,b.fromNode.plotY-=c*f.fromNode/b.fromNode.degree);b.toNode.fixedPosition||(b.toNode.plotX+=h*f.toNode/b.toNode.degree,b.toNode.plotY+=c*f.toNode/b.toNode.degree)},integrate:function(b,c){var a=-b.options.friction,f=b.options.maxSpeed,h=(c.plotX+c.dispX-c.prevX)*a;a*=c.plotY+c.dispY-c.prevY;var u=Math.abs,z=u(h)/(h||1);
u=u(a)/(a||1);h=z*Math.min(f,Math.abs(h));a=u*Math.min(f,Math.abs(a));c.prevX=c.plotX+c.dispX;c.prevY=c.plotY+c.dispY;c.plotX+=h;c.plotY+=a;c.temperature=b.vectorLength({x:h,y:a})},getK:function(b){return Math.pow(b.box.width*b.box.height/b.nodes.length,.5)}},euler:{attractiveForceFunction:function(b,c){return b*b/c},repulsiveForceFunction:function(b,c){return c*c/b},barycenter:function(){var b=this.options.gravitationalConstant,c=this.barycenter.xFactor,a=this.barycenter.yFactor;this.nodes.forEach(function(f){if(!f.fixedPosition){var h=
f.getDegree();h*=1+h/2;f.dispX+=(c-f.plotX)*b*h/f.degree;f.dispY+=(a-f.plotY)*b*h/f.degree}})},repulsive:function(b,c,a,f){b.dispX+=a.x/f*c/b.degree;b.dispY+=a.y/f*c/b.degree},attractive:function(b,c,a,f){var h=b.getMass(),u=a.x/f*c;c*=a.y/f;b.fromNode.fixedPosition||(b.fromNode.dispX-=u*h.fromNode/b.fromNode.degree,b.fromNode.dispY-=c*h.fromNode/b.fromNode.degree);b.toNode.fixedPosition||(b.toNode.dispX+=u*h.toNode/b.toNode.degree,b.toNode.dispY+=c*h.toNode/b.toNode.degree)},integrate:function(b,
c){c.dispX+=c.dispX*b.options.friction;c.dispY+=c.dispY*b.options.friction;var a=c.temperature=b.vectorLength({x:c.dispX,y:c.dispY});0!==a&&(c.plotX+=c.dispX/a*Math.min(Math.abs(c.dispX),b.temperature),c.plotY+=c.dispY/a*Math.min(Math.abs(c.dispY),b.temperature))},getK:function(b){return Math.pow(b.box.width*b.box.height/b.nodes.length,.3)}}}});A(c,"Series/Networkgraph/QuadTree.js",[c["Core/Globals.js"],c["Core/Utilities.js"]],function(c,b){b=b.extend;var h=c.QuadTreeNode=function(a){this.box=a;this.boxSize=
Math.min(a.width,a.height);this.nodes=[];this.body=this.isInternal=!1;this.isEmpty=!0};b(h.prototype,{insert:function(a,b){this.isInternal?this.nodes[this.getBoxPosition(a)].insert(a,b-1):(this.isEmpty=!1,this.body?b?(this.isInternal=!0,this.divideBox(),!0!==this.body&&(this.nodes[this.getBoxPosition(this.body)].insert(this.body,b-1),this.body=!0),this.nodes[this.getBoxPosition(a)].insert(a,b-1)):(b=new h({top:a.plotX,left:a.plotY,width:.1,height:.1}),b.body=a,b.isInternal=!1,this.nodes.push(b)):
(this.isInternal=!1,this.body=a))},updateMassAndCenter:function(){var a=0,b=0,c=0;this.isInternal?(this.nodes.forEach(function(f){f.isEmpty||(a+=f.mass,b+=f.plotX*f.mass,c+=f.plotY*f.mass)}),b/=a,c/=a):this.body&&(a=this.body.mass,b=this.body.plotX,c=this.body.plotY);this.mass=a;this.plotX=b;this.plotY=c},divideBox:function(){var a=this.box.width/2,b=this.box.height/2;this.nodes[0]=new h({left:this.box.left,top:this.box.top,width:a,height:b});this.nodes[1]=new h({left:this.box.left+a,top:this.box.top,
width:a,height:b});this.nodes[2]=new h({left:this.box.left+a,top:this.box.top+b,width:a,height:b});this.nodes[3]=new h({left:this.box.left,top:this.box.top+b,width:a,height:b})},getBoxPosition:function(a){var b=a.plotY<this.box.top+this.box.height/2;return a.plotX<this.box.left+this.box.width/2?b?0:3:b?1:2}});c=c.QuadTree=function(a,b,c,u){this.box={left:a,top:b,width:c,height:u};this.maxDepth=25;this.root=new h(this.box,"0");this.root.isInternal=!0;this.root.isRoot=!0;this.root.divideBox()};b(c.prototype,
{insertNodes:function(a){a.forEach(function(a){this.root.insert(a,this.maxDepth)},this)},visitNodeRecursive:function(a,b,c){var f;a||(a=this.root);a===this.root&&b&&(f=b(a));!1!==f&&(a.nodes.forEach(function(a){if(a.isInternal){b&&(f=b(a));if(!1===f)return;this.visitNodeRecursive(a,b,c)}else a.body&&b&&b(a.body);c&&c(a)},this),a===this.root&&c&&c(a))},calculateMassAndCenter:function(){this.visitNodeRecursive(null,null,function(a){a.updateMassAndCenter()})}})});A(c,"Series/Networkgraph/Layouts.js",
[c["Core/Chart/Chart.js"],c["Core/Animation/AnimationUtilities.js"],c["Core/Globals.js"],c["Core/Utilities.js"]],function(c,b,h,a){var f=b.setAnimation;b=a.addEvent;var q=a.clamp,u=a.defined,z=a.extend,E=a.isFunction,B=a.pick;h.layouts={"reingold-fruchterman":function(){}};z(h.layouts["reingold-fruchterman"].prototype,{init:function(a){this.options=a;this.nodes=[];this.links=[];this.series=[];this.box={x:0,y:0,width:0,height:0};this.setInitialRendering(!0);this.integration=h.networkgraphIntegrations[a.integration];
this.enableSimulation=a.enableSimulation;this.attractiveForce=B(a.attractiveForce,this.integration.attractiveForceFunction);this.repulsiveForce=B(a.repulsiveForce,this.integration.repulsiveForceFunction);this.approximation=a.approximation},updateSimulation:function(a){this.enableSimulation=B(a,this.options.enableSimulation)},start:function(){var a=this.series,b=this.options;this.currentStep=0;this.forces=a[0]&&a[0].forces||[];this.chart=a[0]&&a[0].chart;this.initialRendering&&(this.initPositions(),
a.forEach(function(a){a.finishedAnimating=!0;a.render()}));this.setK();this.resetSimulation(b);this.enableSimulation&&this.step()},step:function(){var a=this,b=this.series;a.currentStep++;"barnes-hut"===a.approximation&&(a.createQuadTree(),a.quadTree.calculateMassAndCenter());a.forces.forEach(function(b){a[b+"Forces"](a.temperature)});a.applyLimits(a.temperature);a.temperature=a.coolDown(a.startTemperature,a.diffTemperature,a.currentStep);a.prevSystemTemperature=a.systemTemperature;a.systemTemperature=
a.getSystemTemperature();a.enableSimulation&&(b.forEach(function(a){a.chart&&a.render()}),a.maxIterations--&&isFinite(a.temperature)&&!a.isStable()?(a.simulation&&h.win.cancelAnimationFrame(a.simulation),a.simulation=h.win.requestAnimationFrame(function(){a.step()})):a.simulation=!1)},stop:function(){this.simulation&&h.win.cancelAnimationFrame(this.simulation)},setArea:function(a,b,c,f){this.box={left:a,top:b,width:c,height:f}},setK:function(){this.k=this.options.linkLength||this.integration.getK(this)},
addElementsToCollection:function(a,b){a.forEach(function(a){-1===b.indexOf(a)&&b.push(a)})},removeElementFromCollection:function(a,b){a=b.indexOf(a);-1!==a&&b.splice(a,1)},clear:function(){this.nodes.length=0;this.links.length=0;this.series.length=0;this.resetSimulation()},resetSimulation:function(){this.forcedStop=!1;this.systemTemperature=0;this.setMaxIterations();this.setTemperature();this.setDiffTemperature()},restartSimulation:function(){this.simulation?this.resetSimulation():(this.setInitialRendering(!1),
this.enableSimulation?this.start():this.setMaxIterations(1),this.chart&&this.chart.redraw(),this.setInitialRendering(!0))},setMaxIterations:function(a){this.maxIterations=B(a,this.options.maxIterations)},setTemperature:function(){this.temperature=this.startTemperature=Math.sqrt(this.nodes.length)},setDiffTemperature:function(){this.diffTemperature=this.startTemperature/(this.options.maxIterations+1)},setInitialRendering:function(a){this.initialRendering=a},createQuadTree:function(){this.quadTree=
new h.QuadTree(this.box.left,this.box.top,this.box.width,this.box.height);this.quadTree.insertNodes(this.nodes)},initPositions:function(){var a=this.options.initialPositions;E(a)?(a.call(this),this.nodes.forEach(function(a){u(a.prevX)||(a.prevX=a.plotX);u(a.prevY)||(a.prevY=a.plotY);a.dispX=0;a.dispY=0})):"circle"===a?this.setCircularPositions():this.setRandomPositions()},setCircularPositions:function(){function a(b){b.linksFrom.forEach(function(d){p[d.toNode.id]||(p[d.toNode.id]=!0,q.push(d.toNode),
a(d.toNode))})}var b=this.box,c=this.nodes,f=2*Math.PI/(c.length+1),h=c.filter(function(a){return 0===a.linksTo.length}),q=[],p={},l=this.options.initialPositionRadius;h.forEach(function(b){q.push(b);a(b)});q.length?c.forEach(function(a){-1===q.indexOf(a)&&q.push(a)}):q=c;q.forEach(function(a,d){a.plotX=a.prevX=B(a.plotX,b.width/2+l*Math.cos(d*f));a.plotY=a.prevY=B(a.plotY,b.height/2+l*Math.sin(d*f));a.dispX=0;a.dispY=0})},setRandomPositions:function(){function a(a){a=a*a/Math.PI;return a-=Math.floor(a)}
var b=this.box,c=this.nodes,f=c.length+1;c.forEach(function(e,c){e.plotX=e.prevX=B(e.plotX,b.width*a(c));e.plotY=e.prevY=B(e.plotY,b.height*a(f+c));e.dispX=0;e.dispY=0})},force:function(a){this.integration[a].apply(this,Array.prototype.slice.call(arguments,1))},barycenterForces:function(){this.getBarycenter();this.force("barycenter")},getBarycenter:function(){var a=0,b=0,c=0;this.nodes.forEach(function(e){b+=e.plotX*e.mass;c+=e.plotY*e.mass;a+=e.mass});return this.barycenter={x:b,y:c,xFactor:b/a,
yFactor:c/a}},barnesHutApproximation:function(a,b){var e=this.getDistXY(a,b),c=this.vectorLength(e);if(a!==b&&0!==c)if(b.isInternal)if(b.boxSize/c<this.options.theta&&0!==c){var f=this.repulsiveForce(c,this.k);this.force("repulsive",a,f*b.mass,e,c);var h=!1}else h=!0;else f=this.repulsiveForce(c,this.k),this.force("repulsive",a,f*b.mass,e,c);return h},repulsiveForces:function(){var a=this;"barnes-hut"===a.approximation?a.nodes.forEach(function(b){a.quadTree.visitNodeRecursive(null,function(e){return a.barnesHutApproximation(b,
e)})}):a.nodes.forEach(function(b){a.nodes.forEach(function(e){if(b!==e&&!b.fixedPosition){var c=a.getDistXY(b,e);var f=a.vectorLength(c);if(0!==f){var h=a.repulsiveForce(f,a.k);a.force("repulsive",b,h*e.mass,c,f)}}})})},attractiveForces:function(){var a=this,b,c,f;a.links.forEach(function(e){e.fromNode&&e.toNode&&(b=a.getDistXY(e.fromNode,e.toNode),c=a.vectorLength(b),0!==c&&(f=a.attractiveForce(c,a.k),a.force("attractive",e,f,b,c)))})},applyLimits:function(){var a=this;a.nodes.forEach(function(b){b.fixedPosition||
(a.integration.integrate(a,b),a.applyLimitBox(b,a.box),b.dispX=0,b.dispY=0)})},applyLimitBox:function(a,b){var c=a.radius;a.plotX=q(a.plotX,b.left+c,b.width-c);a.plotY=q(a.plotY,b.top+c,b.height-c)},coolDown:function(a,b,c){return a-b*c},isStable:function(){return.00001>Math.abs(this.systemTemperature-this.prevSystemTemperature)||0>=this.temperature},getSystemTemperature:function(){return this.nodes.reduce(function(a,b){return a+b.temperature},0)},vectorLength:function(a){return Math.sqrt(a.x*a.x+
a.y*a.y)},getDistR:function(a,b){a=this.getDistXY(a,b);return this.vectorLength(a)},getDistXY:function(a,b){var c=a.plotX-b.plotX;a=a.plotY-b.plotY;return{x:c,y:a,absX:Math.abs(c),absY:Math.abs(a)}}});b(c,"predraw",function(){this.graphLayoutsLookup&&this.graphLayoutsLookup.forEach(function(a){a.stop()})});b(c,"render",function(){function a(a){a.maxIterations--&&isFinite(a.temperature)&&!a.isStable()&&!a.enableSimulation&&(a.beforeStep&&a.beforeStep(),a.step(),c=!1,b=!0)}var b=!1;if(this.graphLayoutsLookup){f(!1,
this);for(this.graphLayoutsLookup.forEach(function(a){a.start()});!c;){var c=!0;this.graphLayoutsLookup.forEach(a)}b&&this.series.forEach(function(a){a&&a.layout&&a.render()})}});b(c,"beforePrint",function(){this.graphLayoutsLookup&&(this.graphLayoutsLookup.forEach(function(a){a.updateSimulation(!1)}),this.redraw())});b(c,"afterPrint",function(){this.graphLayoutsLookup&&this.graphLayoutsLookup.forEach(function(a){a.updateSimulation()});this.redraw()})});A(c,"Series/PackedBubbleSeries.js",[c["Core/Series/Series.js"],
c["Core/Chart/Chart.js"],c["Core/Color/Color.js"],c["Core/Globals.js"],c["Core/Series/Point.js"],c["Core/Utilities.js"]],function(c,b,h,a,f,q){var u=h.parse,z=q.addEvent,E=q.clamp,B=q.defined,e=q.extend;h=q.extendClass;var x=q.fireEvent,w=q.isArray,m=q.isNumber,C=q.merge,v=q.pick,p=a.Series,l=a.layouts["reingold-fruchterman"],k=a.dragNodesMixin;"";b.prototype.getSelectedParentNodes=function(){var a=[];this.series.forEach(function(d){d.parentNode&&d.parentNode.selected&&a.push(d.parentNode)});return a};
a.networkgraphIntegrations.packedbubble={repulsiveForceFunction:function(a,b,c,e){return Math.min(a,(c.marker.radius+e.marker.radius)/2)},barycenter:function(){var a=this,b=a.options.gravitationalConstant,c=a.box,e=a.nodes,f,n;e.forEach(function(d){a.options.splitSeries&&!d.isParentNode?(f=d.series.parentNode.plotX,n=d.series.parentNode.plotY):(f=c.width/2,n=c.height/2);d.fixedPosition||(d.plotX-=(d.plotX-f)*b/(d.mass*Math.sqrt(e.length)),d.plotY-=(d.plotY-n)*b/(d.mass*Math.sqrt(e.length)))})},repulsive:function(a,
b,c,e){var d=b*this.diffTemperature/a.mass/a.degree;b=c.x*d;c=c.y*d;a.fixedPosition||(a.plotX+=b,a.plotY+=c);e.fixedPosition||(e.plotX-=b,e.plotY-=c)},integrate:a.networkgraphIntegrations.verlet.integrate,getK:a.noop};a.layouts.packedbubble=h(l,{beforeStep:function(){this.options.marker&&this.series.forEach(function(a){a&&a.calculateParentRadius()})},setCircularPositions:function(){var a=this,b=a.box,c=a.nodes,e=2*Math.PI/(c.length+1),f,n,h=a.options.initialPositionRadius;c.forEach(function(d,g){a.options.splitSeries&&
!d.isParentNode?(f=d.series.parentNode.plotX,n=d.series.parentNode.plotY):(f=b.width/2,n=b.height/2);d.plotX=d.prevX=v(d.plotX,f+h*Math.cos(d.index||g*e));d.plotY=d.prevY=v(d.plotY,n+h*Math.sin(d.index||g*e));d.dispX=0;d.dispY=0})},repulsiveForces:function(){var a=this,b,c,e,f=a.options.bubblePadding;a.nodes.forEach(function(d){d.degree=d.mass;d.neighbours=0;a.nodes.forEach(function(g){b=0;d===g||d.fixedPosition||!a.options.seriesInteraction&&d.series!==g.series||(e=a.getDistXY(d,g),c=a.vectorLength(e)-
(d.marker.radius+g.marker.radius+f),0>c&&(d.degree+=.01,d.neighbours++,b=a.repulsiveForce(-c/Math.sqrt(d.neighbours),a.k,d,g)),a.force("repulsive",d,b*g.mass,e,g,c))})})},applyLimitBox:function(a){if(this.options.splitSeries&&!a.isParentNode&&this.options.parentNodeLimit){var d=this.getDistXY(a,a.series.parentNode);var b=a.series.parentNodeRadius-a.marker.radius-this.vectorLength(d);0>b&&b>-2*a.marker.radius&&(a.plotX-=.01*d.x,a.plotY-=.01*d.y)}l.prototype.applyLimitBox.apply(this,arguments)}});c.seriesType("packedbubble",
"bubble",{minSize:"10%",maxSize:"50%",sizeBy:"area",zoneAxis:"y",crisp:!1,tooltip:{pointFormat:"Value: {point.value}"},draggable:!0,useSimulation:!0,parentNode:{allowPointSelect:!1},dataLabels:{formatter:function(){return this.point.value},parentNodeFormatter:function(){return this.name},parentNodeTextPath:{enabled:!0},padding:0,style:{transition:"opacity 2000ms"}},layoutAlgorithm:{initialPositions:"circle",initialPositionRadius:20,bubblePadding:5,parentNodeLimit:!1,seriesInteraction:!0,dragBetweenSeries:!1,
parentNodeOptions:{maxIterations:400,gravitationalConstant:.03,maxSpeed:50,initialPositionRadius:100,seriesInteraction:!0,marker:{fillColor:null,fillOpacity:1,lineWidth:1,lineColor:null,symbol:"circle"}},enableSimulation:!0,type:"packedbubble",integration:"packedbubble",maxIterations:1E3,splitSeries:!1,maxSpeed:5,gravitationalConstant:.01,friction:-.981}},{hasDraggableNodes:!0,forces:["barycenter","repulsive"],pointArrayMap:["value"],trackerGroups:["group","dataLabelsGroup","parentNodesGroup"],pointValKey:"value",
isCartesian:!1,requireSorting:!1,directTouch:!0,axisTypes:[],noSharedTooltip:!0,searchPoint:a.noop,accumulateAllPoints:function(a){var d=a.chart,b=[],c,e;for(c=0;c<d.series.length;c++)if(a=d.series[c],a.is("packedbubble")&&a.visible||!d.options.chart.ignoreHiddenSeries)for(e=0;e<a.yData.length;e++)b.push([null,null,a.yData[e],a.index,e,{id:e,marker:{radius:0}}]);return b},init:function(){p.prototype.init.apply(this,arguments);z(this,"updatedData",function(){this.chart.series.forEach(function(a){a.type===
this.type&&(a.isDirty=!0)},this)});return this},render:function(){var a=[];p.prototype.render.apply(this,arguments);this.options.dataLabels.allowOverlap||(this.data.forEach(function(d){w(d.dataLabels)&&d.dataLabels.forEach(function(d){a.push(d)})}),this.options.useSimulation&&this.chart.hideOverlappingLabels(a))},setVisible:function(){var a=this;p.prototype.setVisible.apply(a,arguments);a.parentNodeLayout&&a.graph?a.visible?(a.graph.show(),a.parentNode.dataLabel&&a.parentNode.dataLabel.show()):(a.graph.hide(),
a.parentNodeLayout.removeElementFromCollection(a.parentNode,a.parentNodeLayout.nodes),a.parentNode.dataLabel&&a.parentNode.dataLabel.hide()):a.layout&&(a.visible?a.layout.addElementsToCollection(a.points,a.layout.nodes):a.points.forEach(function(d){a.layout.removeElementFromCollection(d,a.layout.nodes)}))},drawDataLabels:function(){var a=this.options.dataLabels.textPath,b=this.points;p.prototype.drawDataLabels.apply(this,arguments);this.parentNode&&(this.parentNode.formatPrefix="parentNode",this.points=
[this.parentNode],this.options.dataLabels.textPath=this.options.dataLabels.parentNodeTextPath,p.prototype.drawDataLabels.apply(this,arguments),this.points=b,this.options.dataLabels.textPath=a)},seriesBox:function(){var a=this.chart,b=Math.max,c=Math.min,e,f=[a.plotLeft,a.plotLeft+a.plotWidth,a.plotTop,a.plotTop+a.plotHeight];this.data.forEach(function(a){B(a.plotX)&&B(a.plotY)&&a.marker.radius&&(e=a.marker.radius,f[0]=c(f[0],a.plotX-e),f[1]=b(f[1],a.plotX+e),f[2]=c(f[2],a.plotY-e),f[3]=b(f[3],a.plotY+
e))});return m(f.width/f.height)?f:null},calculateParentRadius:function(){var a=this.seriesBox();this.parentNodeRadius=E(Math.sqrt(2*this.parentNodeMass/Math.PI)+20,20,a?Math.max(Math.sqrt(Math.pow(a.width,2)+Math.pow(a.height,2))/2+20,20):Math.sqrt(2*this.parentNodeMass/Math.PI)+20);this.parentNode&&(this.parentNode.marker.radius=this.parentNode.radius=this.parentNodeRadius)},drawGraph:function(){if(this.layout&&this.layout.options.splitSeries){var a=this.chart,b=this.layout.options.parentNodeOptions.marker;
b={fill:b.fillColor||u(this.color).brighten(.4).get(),opacity:b.fillOpacity,stroke:b.lineColor||this.color,"stroke-width":b.lineWidth};var c=this.visible?"inherit":"hidden";this.parentNodesGroup||(this.parentNodesGroup=this.plotGroup("parentNodesGroup","parentNode",c,.1,a.seriesGroup),this.group.attr({zIndex:2}));this.calculateParentRadius();c=C({x:this.parentNode.plotX-this.parentNodeRadius,y:this.parentNode.plotY-this.parentNodeRadius,width:2*this.parentNodeRadius,height:2*this.parentNodeRadius},
b);this.parentNode.graphic||(this.graph=this.parentNode.graphic=a.renderer.symbol(b.symbol).add(this.parentNodesGroup));this.parentNode.graphic.attr(c)}},createParentNodes:function(){var a=this,b=a.chart,c=a.parentNodeLayout,e,f=a.parentNode,n=a.pointClass;a.parentNodeMass=0;a.points.forEach(function(d){a.parentNodeMass+=Math.PI*Math.pow(d.marker.radius,2)});a.calculateParentRadius();c.nodes.forEach(function(d){d.seriesIndex===a.index&&(e=!0)});c.setArea(0,0,b.plotWidth,b.plotHeight);e||(f||(f=(new n).init(this,
{mass:a.parentNodeRadius/2,marker:{radius:a.parentNodeRadius},dataLabels:{inside:!1},dataLabelOnNull:!0,degree:a.parentNodeRadius,isParentNode:!0,seriesIndex:a.index})),a.parentNode&&(f.plotX=a.parentNode.plotX,f.plotY=a.parentNode.plotY),a.parentNode=f,c.addElementsToCollection([a],c.series),c.addElementsToCollection([f],c.nodes))},drawTracker:function(){var d=this.parentNode;a.TrackerMixin.drawTrackerPoint.call(this);if(d){var b=w(d.dataLabels)?d.dataLabels:d.dataLabel?[d.dataLabel]:[];d.graphic&&
(d.graphic.element.point=d);b.forEach(function(a){a.div?a.div.point=d:a.element.point=d})}},addSeriesLayout:function(){var d=this.options.layoutAlgorithm,b=this.chart.graphLayoutsStorage,c=this.chart.graphLayoutsLookup,e=C(d,d.parentNodeOptions,{enableSimulation:this.layout.options.enableSimulation});var f=b[d.type+"-series"];f||(b[d.type+"-series"]=f=new a.layouts[d.type],f.init(e),c.splice(f.index,0,f));this.parentNodeLayout=f;this.createParentNodes()},addLayout:function(){var d=this.options.layoutAlgorithm,
b=this.chart.graphLayoutsStorage,c=this.chart.graphLayoutsLookup,e=this.chart.options.chart;b||(this.chart.graphLayoutsStorage=b={},this.chart.graphLayoutsLookup=c=[]);var f=b[d.type];f||(d.enableSimulation=B(e.forExport)?!e.forExport:d.enableSimulation,b[d.type]=f=new a.layouts[d.type],f.init(d),c.splice(f.index,0,f));this.layout=f;this.points.forEach(function(a){a.mass=2;a.degree=1;a.collisionNmb=1});f.setArea(0,0,this.chart.plotWidth,this.chart.plotHeight);f.addElementsToCollection([this],f.series);
f.addElementsToCollection(this.points,f.nodes)},deferLayout:function(){var a=this.options.layoutAlgorithm;this.visible&&(this.addLayout(),a.splitSeries&&this.addSeriesLayout())},translate:function(){var a=this.chart,b=this.data,c=this.index,f,t=this.options.useSimulation;this.processedXData=this.xData;this.generatePoints();B(a.allDataPoints)||(a.allDataPoints=this.accumulateAllPoints(this),this.getPointRadius());if(t)var n=a.allDataPoints;else n=this.placeBubbles(a.allDataPoints),this.options.draggable=
!1;for(f=0;f<n.length;f++)if(n[f][3]===c){var h=b[n[f][4]];var l=n[f][2];t||(h.plotX=n[f][0]-a.plotLeft+a.diffX,h.plotY=n[f][1]-a.plotTop+a.diffY);h.marker=e(h.marker,{radius:l,width:2*l,height:2*l});h.radius=l}t&&this.deferLayout();x(this,"afterTranslate")},checkOverlap:function(a,b){var d=a[0]-b[0],c=a[1]-b[1];return-.001>Math.sqrt(d*d+c*c)-Math.abs(a[2]+b[2])},positionBubble:function(a,b,c){var d=Math.sqrt,e=Math.asin,g=Math.acos,f=Math.pow,h=Math.abs;d=d(f(a[0]-b[0],2)+f(a[1]-b[1],2));g=g((f(d,
2)+f(c[2]+b[2],2)-f(c[2]+a[2],2))/(2*(c[2]+b[2])*d));e=e(h(a[0]-b[0])/d);a=(0>a[1]-b[1]?0:Math.PI)+g+e*(0>(a[0]-b[0])*(a[1]-b[1])?1:-1);return[b[0]+(b[2]+c[2])*Math.sin(a),b[1]-(b[2]+c[2])*Math.cos(a),c[2],c[3],c[4]]},placeBubbles:function(a){var b=this.checkOverlap,d=this.positionBubble,c=[],e=1,f=0,h=0;var l=[];var k;a=a.sort(function(a,b){return b[2]-a[2]});if(a.length){c.push([[0,0,a[0][2],a[0][3],a[0][4]]]);if(1<a.length)for(c.push([[0,0-a[1][2]-a[0][2],a[1][2],a[1][3],a[1][4]]]),k=2;k<a.length;k++)a[k][2]=
a[k][2]||1,l=d(c[e][f],c[e-1][h],a[k]),b(l,c[e][0])?(c.push([]),h=0,c[e+1].push(d(c[e][f],c[e][0],a[k])),e++,f=0):1<e&&c[e-1][h+1]&&b(l,c[e-1][h+1])?(h++,c[e].push(d(c[e][f],c[e-1][h],a[k])),f++):(f++,c[e].push(l));this.chart.stages=c;this.chart.rawPositions=[].concat.apply([],c);this.resizeRadius();l=this.chart.rawPositions}return l},resizeRadius:function(){var a=this.chart,b=a.rawPositions,c=Math.min,e=Math.max,f=a.plotLeft,n=a.plotTop,h=a.plotHeight,l=a.plotWidth,k,q,m;var p=k=Number.POSITIVE_INFINITY;
var u=q=Number.NEGATIVE_INFINITY;for(m=0;m<b.length;m++){var v=b[m][2];p=c(p,b[m][0]-v);u=e(u,b[m][0]+v);k=c(k,b[m][1]-v);q=e(q,b[m][1]+v)}m=[u-p,q-k];c=c.apply([],[(l-f)/m[0],(h-n)/m[1]]);if(1e-10<Math.abs(c-1)){for(m=0;m<b.length;m++)b[m][2]*=c;this.placeBubbles(b)}else a.diffY=h/2+n-k-(q-k)/2,a.diffX=l/2+f-p-(u-p)/2},calculateZExtremes:function(){var a=this.options.zMin,b=this.options.zMax,c=Infinity,e=-Infinity;if(a&&b)return[a,b];this.chart.series.forEach(function(a){a.yData.forEach(function(a){B(a)&&
(a>e&&(e=a),a<c&&(c=a))})});a=v(a,c);b=v(b,e);return[a,b]},getPointRadius:function(){var a=this,b=a.chart,c=a.options,e=c.useSimulation,f=Math.min(b.plotWidth,b.plotHeight),n={},h=[],k=b.allDataPoints,l,m,q,p;["minSize","maxSize"].forEach(function(a){var b=parseInt(c[a],10),d=/%$/.test(c[a]);n[a]=d?f*b/100:b*Math.sqrt(k.length)});b.minRadius=l=n.minSize/Math.sqrt(k.length);b.maxRadius=m=n.maxSize/Math.sqrt(k.length);var u=e?a.calculateZExtremes():[l,m];(k||[]).forEach(function(b,d){q=e?E(b[2],u[0],
u[1]):b[2];p=a.getRadius(u[0],u[1],l,m,q);0===p&&(p=null);k[d][2]=p;h.push(p)});a.radii=h},redrawHalo:k.redrawHalo,onMouseDown:k.onMouseDown,onMouseMove:k.onMouseMove,onMouseUp:function(a){if(a.fixedPosition&&!a.removed){var b,d,c=this.layout,e=this.parentNodeLayout;e&&c.options.dragBetweenSeries&&e.nodes.forEach(function(e){a&&a.marker&&e!==a.series.parentNode&&(b=c.getDistXY(a,e),d=c.vectorLength(b)-e.marker.radius-a.marker.radius,0>d&&(e.series.addPoint(C(a.options,{plotX:a.plotX,plotY:a.plotY}),
!1),c.removeElementFromCollection(a,c.nodes),a.remove()))});k.onMouseUp.apply(this,arguments)}},destroy:function(){this.chart.graphLayoutsLookup&&this.chart.graphLayoutsLookup.forEach(function(a){a.removeElementFromCollection(this,a.series)},this);this.parentNode&&(this.parentNodeLayout.removeElementFromCollection(this.parentNode,this.parentNodeLayout.nodes),this.parentNode.dataLabel&&(this.parentNode.dataLabel=this.parentNode.dataLabel.destroy()));a.Series.prototype.destroy.apply(this,arguments)},
alignDataLabel:a.Series.prototype.alignDataLabel},{destroy:function(){this.series.layout&&this.series.layout.removeElementFromCollection(this,this.series.layout.nodes);return f.prototype.destroy.apply(this,arguments)},firePointEvent:function(a,b,c){var d=this.series.options;if(this.isParentNode&&d.parentNode){var e=d.allowPointSelect;d.allowPointSelect=d.parentNode.allowPointSelect;f.prototype.firePointEvent.apply(this,arguments);d.allowPointSelect=e}else f.prototype.firePointEvent.apply(this,arguments)},
select:function(a,c){var d=this.series.chart;this.isParentNode?(d.getSelectedPoints=d.getSelectedParentNodes,f.prototype.select.apply(this,arguments),d.getSelectedPoints=b.prototype.getSelectedPoints):f.prototype.select.apply(this,arguments)}});z(b,"beforeRedraw",function(){this.allDataPoints&&delete this.allDataPoints});""});A(c,"Extensions/Polar.js",[c["Core/Animation/AnimationUtilities.js"],c["Core/Chart/Chart.js"],c["Core/Globals.js"],c["Extensions/Pane.js"],c["Core/Pointer.js"],c["Core/Renderer/SVG/SVGRenderer.js"],
c["Core/Utilities.js"]],function(c,b,h,a,f,q,u){var z=c.animObject,E=u.addEvent,B=u.defined,e=u.find,x=u.isNumber,w=u.pick,m=u.splat,C=u.uniqueKey;c=u.wrap;var v=h.Series,p=h.seriesTypes,l=v.prototype;f=f.prototype;l.searchPointByAngle=function(a){var b=this.chart,d=this.xAxis.pane.center;return this.searchKDTree({clientX:180+-180/Math.PI*Math.atan2(a.chartX-d[0]-b.plotLeft,a.chartY-d[1]-b.plotTop)})};l.getConnectors=function(a,b,c,e){var d=e?1:0;var f=0<=b&&b<=a.length-1?b:0>b?a.length-1+b:0;b=0>
f-1?a.length-(1+d):f-1;d=f+1>a.length-1?d:f+1;var g=a[b];d=a[d];var h=g.plotX;g=g.plotY;var k=d.plotX;var l=d.plotY;d=a[f].plotX;f=a[f].plotY;h=(1.5*d+h)/2.5;g=(1.5*f+g)/2.5;k=(1.5*d+k)/2.5;var r=(1.5*f+l)/2.5;l=Math.sqrt(Math.pow(h-d,2)+Math.pow(g-f,2));var m=Math.sqrt(Math.pow(k-d,2)+Math.pow(r-f,2));h=Math.atan2(g-f,h-d);r=Math.PI/2+(h+Math.atan2(r-f,k-d))/2;Math.abs(h-r)>Math.PI/2&&(r-=Math.PI);h=d+Math.cos(r)*l;g=f+Math.sin(r)*l;k=d+Math.cos(Math.PI+r)*m;r=f+Math.sin(Math.PI+r)*m;d={rightContX:k,
rightContY:r,leftContX:h,leftContY:g,plotX:d,plotY:f};c&&(d.prevPointCont=this.getConnectors(a,b,!1,e));return d};l.toXY=function(a){var b=this.chart,d=this.xAxis;var c=this.yAxis;var e=a.plotX,f=a.plotY,h=a.series,k=b.inverted,l=a.y,m=k?e:c.len-f;k&&h&&!h.isRadialBar&&(a.plotY=f="number"===typeof l?c.translate(l)||0:0);a.rectPlotX=e;a.rectPlotY=f;c.center&&(m+=c.center[3]/2);c=k?c.postTranslate(f,m):d.postTranslate(e,m);a.plotX=a.polarPlotX=c.x-b.plotLeft;a.plotY=a.polarPlotY=c.y-b.plotTop;this.kdByAngle?
(b=(e/Math.PI*180+d.pane.options.startAngle)%360,0>b&&(b+=360),a.clientX=b):a.clientX=a.plotX};p.spline&&(c(p.spline.prototype,"getPointSpline",function(a,b,c,e){this.chart.polar?e?(a=this.getConnectors(b,e,!0,this.connectEnds),a=["C",a.prevPointCont.rightContX,a.prevPointCont.rightContY,a.leftContX,a.leftContY,a.plotX,a.plotY]):a=["M",c.plotX,c.plotY]:a=a.call(this,b,c,e);return a}),p.areasplinerange&&(p.areasplinerange.prototype.getPointSpline=p.spline.prototype.getPointSpline));E(v,"afterTranslate",
function(){var a=this.chart;if(a.polar&&this.xAxis){(this.kdByAngle=a.tooltip&&a.tooltip.shared)?this.searchPoint=this.searchPointByAngle:this.options.findNearestPointBy="xy";if(!this.preventPostTranslate)for(var b=this.points,c=b.length;c--;)this.toXY(b[c]),!a.hasParallelCoordinates&&!this.yAxis.reversed&&b[c].y<this.yAxis.min&&(b[c].isNull=!0);this.hasClipCircleSetter||(this.hasClipCircleSetter=!!this.eventsToUnbind.push(E(this,"afterRender",function(){if(a.polar){var b=this.yAxis.pane.center;this.clipCircle?
this.clipCircle.animate({x:b[0],y:b[1],r:b[2]/2,innerR:b[3]/2}):this.clipCircle=a.renderer.clipCircle(b[0],b[1],b[2]/2,b[3]/2);this.group.clip(this.clipCircle);this.setClip=h.noop}})))}},{order:2});c(l,"getGraphPath",function(a,b){var c=this,d;if(this.chart.polar){b=b||this.points;for(d=0;d<b.length;d++)if(!b[d].isNull){var e=d;break}if(!1!==this.options.connectEnds&&"undefined"!==typeof e){this.connectEnds=!0;b.splice(b.length,0,b[e]);var f=!0}b.forEach(function(a){"undefined"===typeof a.polarPlotY&&
c.toXY(a)})}d=a.apply(this,[].slice.call(arguments,1));f&&b.pop();return d});var k=function(a,b){var c=this,d=this.chart,e=this.options.animation,f=this.group,g=this.markerGroup,k=this.xAxis.center,l=d.plotLeft,m=d.plotTop,p,q,u,v;if(d.polar)if(c.isRadialBar)b||(c.startAngleRad=w(c.translatedThreshold,c.xAxis.startAngleRad),h.seriesTypes.pie.prototype.animate.call(c,b));else{if(d.renderer.isSVG)if(e=z(e),c.is("column")){if(!b){var x=k[3]/2;c.points.forEach(function(a){p=a.graphic;u=(q=a.shapeArgs)&&
q.r;v=q&&q.innerR;p&&q&&(p.attr({r:x,innerR:x}),p.animate({r:u,innerR:v},c.options.animation))})}}else b?(a={translateX:k[0]+l,translateY:k[1]+m,scaleX:.001,scaleY:.001},f.attr(a),g&&g.attr(a)):(a={translateX:l,translateY:m,scaleX:1,scaleY:1},f.animate(a,e),g&&g.animate(a,e))}else a.call(this,b)};c(l,"animate",k);p.column&&(v=p.arearange.prototype,p=p.column.prototype,p.polarArc=function(a,b,c,e){var d=this.xAxis.center,f=this.yAxis.len,g=d[3]/2;b=f-b+g;a=f-w(a,f)+g;this.yAxis.reversed&&(0>b&&(b=
g),0>a&&(a=g));return{x:d[0],y:d[1],r:b,innerR:a,start:c,end:e}},c(p,"animate",k),c(p,"translate",function(a){var b=this.options,c=b.stacking,d=this.chart,e=this.xAxis,f=this.yAxis,h=f.reversed,k=f.center,l=e.startAngleRad,m=e.endAngleRad-l;this.preventPostTranslate=!0;a.call(this);if(e.isRadial){a=this.points;e=a.length;var p=f.translate(f.min);var q=f.translate(f.max);b=b.threshold||0;if(d.inverted&&x(b)){var v=f.translate(b);B(v)&&(0>v?v=0:v>m&&(v=m),this.translatedThreshold=v+l)}for(;e--;){b=
a[e];var w=b.barX;var z=b.x;var E=b.y;b.shapeType="arc";if(d.inverted){b.plotY=f.translate(E);if(c&&f.stacking){if(E=f.stacking.stacks[(0>E?"-":"")+this.stackKey],this.visible&&E&&E[z]&&!b.isNull){var C=E[z].points[this.getStackIndicator(void 0,z,this.index).key];var A=f.translate(C[0]);C=f.translate(C[1]);B(A)&&(A=u.clamp(A,0,m))}}else A=v,C=b.plotY;A>C&&(C=[A,A=C][0]);if(!h)if(A<p)A=p;else if(C>q)C=q;else{if(C<p||A>q)A=C=0}else if(C>p)C=p;else if(A<q)A=q;else if(A>p||C<q)A=C=m;f.min>f.max&&(A=C=
h?m:0);A+=l;C+=l;k&&(b.barX=w+=k[3]/2);z=Math.max(w,0);E=Math.max(w+b.pointWidth,0);b.shapeArgs={x:k&&k[0],y:k&&k[1],r:E,innerR:z,start:A,end:C};b.opacity=A===C?0:void 0;b.plotY=(B(this.translatedThreshold)&&(A<this.translatedThreshold?A:C))-l}else A=w+l,b.shapeArgs=this.polarArc(b.yBottom,b.plotY,A,A+b.pointWidth);this.toXY(b);d.inverted?(w=f.postTranslate(b.rectPlotY,w+b.pointWidth/2),b.tooltipPos=[w.x-d.plotLeft,w.y-d.plotTop]):b.tooltipPos=[b.plotX,b.plotY];k&&(b.ttBelow=b.plotY>k[1])}}}),p.findAlignments=
function(a,b){null===b.align&&(b.align=20<a&&160>a?"left":200<a&&340>a?"right":"center");null===b.verticalAlign&&(b.verticalAlign=45>a||315<a?"bottom":135<a&&225>a?"top":"middle");return b},v&&(v.findAlignments=p.findAlignments),c(p,"alignDataLabel",function(a,b,c,e,f,h){var d=this.chart,g=w(e.inside,!!this.options.stacking);d.polar?(a=b.rectPlotX/Math.PI*180,d.inverted?(this.forceDL=d.isInsidePlot(b.plotX,Math.round(b.plotY),!1),g&&b.shapeArgs?(f=b.shapeArgs,f=this.yAxis.postTranslate((f.start+f.end)/
2-this.xAxis.startAngleRad,b.barX+b.pointWidth/2),f={x:f.x-d.plotLeft,y:f.y-d.plotTop}):b.tooltipPos&&(f={x:b.tooltipPos[0],y:b.tooltipPos[1]}),e.align=w(e.align,"center"),e.verticalAlign=w(e.verticalAlign,"middle")):this.findAlignments&&(e=this.findAlignments(a,e)),l.alignDataLabel.call(this,b,c,e,f,h),this.isRadialBar&&b.shapeArgs&&b.shapeArgs.start===b.shapeArgs.end&&c.hide(!0)):a.call(this,b,c,e,f,h)}));c(f,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?c.axes.forEach(function(a){var e=
a.isXAxis,f=a.center;if("colorAxis"!==a.coll){var g=b.chartX-f[0]-c.plotLeft;f=b.chartY-f[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(g,f):Math.sqrt(Math.pow(g,2)+Math.pow(f,2)),!0)})}}):d=a.call(this,b);return d});q.prototype.clipCircle=function(a,b,c,e){var d=C(),f=this.createElement("clipPath").attr({id:d}).add(this.defs);a=e?this.arc(a,b,c,e,0,2*Math.PI).add(f):this.circle(a,b,c).add(f);a.id=d;a.clipPath=f;return a};E(b,"getAxes",function(){this.pane||
(this.pane=[]);m(this.options.pane).forEach(function(b){new a(b,this)},this)});E(b,"afterDrawChartBox",function(){this.pane.forEach(function(a){a.render()})});E(h.Series,"afterInit",function(){var a=this.chart;a.inverted&&a.polar&&(this.isRadialSeries=!0,this.is("column")&&(this.isRadialBar=!0))});c(b.prototype,"get",function(a,b){return e(this.pane,function(a){return a.options.id===b})||a.call(this,b)})});A(c,"masters/highcharts-more.src.js",[],function(){})});
//# sourceMappingURL=highcharts-more.js.map

View File

@ -1,5 +1,5 @@
/*
Highcharts JS v7.1.2 (2019-06-03)
Highmaps JS v8.2.2 (2020-10-22)
Highmaps as a plugin for Highcharts or Highstock.
@ -7,86 +7,102 @@
License: www.highcharts.com/license
*/
(function(h){"object"===typeof module&&module.exports?(h["default"]=h,module.exports=h):"function"===typeof define&&define.amd?define("highcharts/modules/map",["highcharts"],function(z){h(z);h.Highcharts=z;return h}):h("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(h){function z(a,h,k,r){a.hasOwnProperty(h)||(a[h]=r.apply(null,k))}h=h?h._modules:{};z(h,"parts-map/MapAxis.js",[h["parts/Globals.js"]],function(a){var h=a.addEvent,k=a.Axis,r=a.pick;h(k,"getSeriesExtremes",function(){var a=
[];this.isXAxis&&(this.series.forEach(function(f,q){f.useMapGeometry&&(a[q]=f.xData,f.xData=[])}),this.seriesXData=a)});h(k,"afterGetSeriesExtremes",function(){var a=this.seriesXData,f,u,k;this.isXAxis&&(f=r(this.dataMin,Number.MAX_VALUE),u=r(this.dataMax,-Number.MAX_VALUE),this.series.forEach(function(e,b){e.useMapGeometry&&(f=Math.min(f,r(e.minX,f)),u=Math.max(u,r(e.maxX,u)),e.xData=a[b],k=!0)}),k&&(this.dataMin=f,this.dataMax=u),delete this.seriesXData)});h(k,"afterSetAxisTranslation",function(){var a=
this.chart,f;f=a.plotWidth/a.plotHeight;var a=a.xAxis[0],k;"yAxis"===this.coll&&void 0!==a.transA&&this.series.forEach(function(a){a.preserveAspectRatio&&(k=!0)});if(k&&(this.transA=a.transA=Math.min(this.transA,a.transA),f/=(a.max-a.min)/(this.max-this.min),f=1>f?this:a,a=(f.max-f.min)*f.transA,f.pixelPadding=f.len-a,f.minPixelPadding=f.pixelPadding/2,a=f.fixTo)){a=a[1]-f.toValue(a[0],!0);a*=f.transA;if(Math.abs(a)>f.minPixelPadding||f.min===f.dataMin&&f.max===f.dataMax)a=0;f.minPixelPadding-=a}});
h(k,"render",function(){this.fixTo=null})});z(h,"parts-map/ColorAxis.js",[h["parts/Globals.js"]],function(a){var h=a.addEvent,k=a.Axis,r=a.Chart,q=a.color,f,u=a.extend,x=a.isNumber,e=a.Legend,b=a.LegendSymbolMixin,d=a.noop,y=a.merge,p=a.pick;f=a.ColorAxis=function(){this.init.apply(this,arguments)};u(f.prototype,k.prototype);u(f.prototype,{defaultColorAxisOptions:{lineWidth:0,minPadding:0,maxPadding:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},
width:.01,color:"#999999"},labels:{overflow:"justify",rotation:0},minColor:"#e6ebf5",maxColor:"#003399",tickLength:5,showInLegend:!0},keepProps:["legendGroup","legendItemHeight","legendItemWidth","legendItem","legendSymbol"].concat(k.prototype.keepProps),init:function(c,g){var m="vertical"!==c.options.legend.layout,a;this.coll="colorAxis";a=this.buildOptions.call(c,this.defaultColorAxisOptions,g);k.prototype.init.call(this,c,a);g.dataClasses&&this.initDataClasses(g);this.initStops();this.horiz=m;
this.zoomEnabled=!1;this.defaultLegendLength=200},initDataClasses:function(c){var g=this.chart,m,a=0,l=g.options.chart.colorCount,n=this.options,b=c.dataClasses.length;this.dataClasses=m=[];this.legendItems=[];c.dataClasses.forEach(function(c,w){c=y(c);m.push(c);if(g.styledMode||!c.color)"category"===n.dataClassColor?(g.styledMode||(w=g.options.colors,l=w.length,c.color=w[a]),c.colorIndex=a,a++,a===l&&(a=0)):c.color=q(n.minColor).tweenTo(q(n.maxColor),2>b?.5:w/(b-1))})},hasData:function(){return!(!this.tickPositions||
!this.tickPositions.length)},setTickPositions:function(){if(!this.dataClasses)return k.prototype.setTickPositions.call(this)},initStops:function(){this.stops=this.options.stops||[[0,this.options.minColor],[1,this.options.maxColor]];this.stops.forEach(function(c){c.color=q(c[1])})},buildOptions:function(c,g){var a=this.options.legend,b="vertical"!==a.layout;return y(c,{side:b?2:1,reversed:!b},g,{opposite:!b,showEmpty:!1,title:null,visible:a.enabled})},setOptions:function(c){k.prototype.setOptions.call(this,
c);this.options.crosshair=this.options.marker},setAxisSize:function(){var c=this.legendSymbol,g=this.chart,a=g.options.legend||{},b,l;c?(this.left=a=c.attr("x"),this.top=b=c.attr("y"),this.width=l=c.attr("width"),this.height=c=c.attr("height"),this.right=g.chartWidth-a-l,this.bottom=g.chartHeight-b-c,this.len=this.horiz?l:c,this.pos=this.horiz?a:b):this.len=(this.horiz?a.symbolWidth:a.symbolHeight)||this.defaultLegendLength},normalizedValue:function(c){this.isLog&&(c=this.val2lin(c));return 1-(this.max-
c)/(this.max-this.min||1)},toColor:function(c,g){var a=this.stops,b,l,n=this.dataClasses,w,d;if(n)for(d=n.length;d--;){if(w=n[d],b=w.from,a=w.to,(void 0===b||c>=b)&&(void 0===a||c<=a)){l=w.color;g&&(g.dataClass=d,g.colorIndex=w.colorIndex);break}}else{c=this.normalizedValue(c);for(d=a.length;d--&&!(c>a[d][0]););b=a[d]||a[d+1];a=a[d+1]||b;c=1-(a[0]-c)/(a[0]-b[0]||1);l=b.color.tweenTo(a.color,c)}return l},getOffset:function(){var c=this.legendGroup,g=this.chart.axisOffset[this.side];c&&(this.axisParent=
c,k.prototype.getOffset.call(this),this.added||(this.added=!0,this.labelLeft=0,this.labelRight=this.width),this.chart.axisOffset[this.side]=g)},setLegendColor:function(){var c,g=this.reversed;c=g?1:0;g=g?0:1;c=this.horiz?[c,0,g,0]:[0,g,0,c];this.legendColor={linearGradient:{x1:c[0],y1:c[1],x2:c[2],y2:c[3]},stops:this.stops}},drawLegendSymbol:function(c,g){var a=c.padding,b=c.options,l=this.horiz,n=p(b.symbolWidth,l?this.defaultLegendLength:12),w=p(b.symbolHeight,l?12:this.defaultLegendLength),d=p(b.labelPadding,
l?16:30),b=p(b.itemDistance,10);this.setLegendColor();g.legendSymbol=this.chart.renderer.rect(0,c.baseline-11,n,w).attr({zIndex:1}).add(g.legendGroup);this.legendItemWidth=n+a+(l?b:d);this.legendItemHeight=w+a+(l?d:0)},setState:function(c){this.series.forEach(function(g){g.setState(c)})},visible:!0,setVisible:d,getSeriesExtremes:function(){var c=this.series,g=c.length;this.dataMin=Infinity;for(this.dataMax=-Infinity;g--;)c[g].getExtremes(),void 0!==c[g].valueMin&&(this.dataMin=Math.min(this.dataMin,
c[g].valueMin),this.dataMax=Math.max(this.dataMax,c[g].valueMax))},drawCrosshair:function(c,g){var a=g&&g.plotX,b=g&&g.plotY,l,n=this.pos,w=this.len;g&&(l=this.toPixels(g[g.series.colorKey]),l<n?l=n-2:l>n+w&&(l=n+w+2),g.plotX=l,g.plotY=this.len-l,k.prototype.drawCrosshair.call(this,c,g),g.plotX=a,g.plotY=b,this.cross&&!this.cross.addedToColorAxis&&this.legendGroup&&(this.cross.addClass("highcharts-coloraxis-marker").add(this.legendGroup),this.cross.addedToColorAxis=!0,this.chart.styledMode||this.cross.attr({fill:this.crosshair.color})))},
getPlotLinePath:function(c){var g=c.translatedValue;return x(g)?this.horiz?["M",g-4,this.top-6,"L",g+4,this.top-6,g,this.top,"Z"]:["M",this.left,g,"L",this.left-6,g+6,this.left-6,g-6,"Z"]:k.prototype.getPlotLinePath.apply(this,arguments)},update:function(c,g){var a=this.chart,b=a.legend,l=this.buildOptions.call(a,{},c);this.series.forEach(function(c){c.isDirtyData=!0});c.dataClasses&&b.allItems&&(b.allItems.forEach(function(c){c.isDataClass&&c.legendGroup&&c.legendGroup.destroy()}),a.isDirtyLegend=
!0);a.options[this.coll]=y(this.userOptions,l);k.prototype.update.call(this,l,g);this.legendItem&&(this.setLegendColor(),b.colorizeItem(this,!0))},remove:function(){this.legendItem&&this.chart.legend.destroyItem(this);k.prototype.remove.call(this)},getDataClassLegendSymbols:function(){var c=this,g=this.chart,m=this.legendItems,e=g.options.legend,l=e.valueDecimals,n=e.valueSuffix||"",w;m.length||this.dataClasses.forEach(function(e,v){var A=!0,f=e.from,t=e.to;w="";void 0===f?w="\x3c ":void 0===t&&(w=
"\x3e ");void 0!==f&&(w+=a.numberFormat(f,l)+n);void 0!==f&&void 0!==t&&(w+=" - ");void 0!==t&&(w+=a.numberFormat(t,l)+n);m.push(u({chart:g,name:w,options:{},drawLegendSymbol:b.drawRectangle,visible:!0,setState:d,isDataClass:!0,setVisible:function(){A=this.visible=!A;c.series.forEach(function(c){c.points.forEach(function(c){c.dataClass===v&&c.setVisible(A)})});g.legend.colorizeItem(this,A)}},e))});return m},name:""});["fill","stroke"].forEach(function(c){a.Fx.prototype[c+"Setter"]=function(){this.elem.attr(c,
q(this.start).tweenTo(q(this.end),this.pos),null,!0)}});h(r,"afterGetAxes",function(){var c=this.options.colorAxis;this.colorAxis=[];c&&new f(this,c)});h(e,"afterGetAllItems",function(c){var g=[],b=this.chart.colorAxis[0];b&&b.options&&b.options.showInLegend&&(b.options.dataClasses?g=b.getDataClassLegendSymbols():g.push(b),b.series.forEach(function(g){a.erase(c.allItems,g)}));for(b=g.length;b--;)c.allItems.unshift(g[b])});h(e,"afterColorizeItem",function(c){c.visible&&c.item.legendColor&&c.item.legendSymbol.attr({fill:c.item.legendColor})});
h(e,"afterUpdate",function(c,a,b){this.chart.colorAxis[0]&&this.chart.colorAxis[0].update({},b)})});z(h,"parts-map/ColorSeriesMixin.js",[h["parts/Globals.js"]],function(a){var h=a.defined,k=a.noop,r=a.seriesTypes;a.colorPointMixin={dataLabelOnNull:!0,isValid:function(){return null!==this.value&&Infinity!==this.value&&-Infinity!==this.value},setVisible:function(a){var f=this,q=a?"show":"hide";f.visible=!!a;["graphic","dataLabel"].forEach(function(a){if(f[a])f[a][q]()})},setState:function(q){a.Point.prototype.setState.call(this,
q);this.graphic&&this.graphic.attr({zIndex:"hover"===q?1:0})}};a.colorSeriesMixin={pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],optionalAxis:"colorAxis",trackerGroups:["group","markerGroup","dataLabelsGroup"],getSymbol:k,parallelArrays:["x","y","value"],colorKey:"value",pointAttribs:r.column.prototype.pointAttribs,translateColors:function(){var a=this,f=this.options.nullColor,k=this.colorAxis,h=this.colorKey;this.data.forEach(function(e){var b=e[h];if(b=e.options.color||(e.isNull?
f:k&&void 0!==b?k.toColor(b,e):e.color||a.color))e.color=b})},colorAttribs:function(a){var f={};h(a.color)&&(f[this.colorProp||"fill"]=a.color);return f}}});z(h,"parts-map/MapNavigation.js",[h["parts/Globals.js"]],function(a){function h(a){a&&(a.preventDefault&&a.preventDefault(),a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)}function k(a){this.init(a)}var r=a.addEvent,q=a.Chart,f=a.doc,u=a.extend,x=a.merge,e=a.pick;k.prototype.init=function(a){this.chart=a;a.mapNavButtons=[]};k.prototype.update=
function(b){var d=this.chart,f=d.options.mapNavigation,p,c,g,m,t,l=function(c){this.handler.call(d,c);h(c)},n=d.mapNavButtons;b&&(f=d.options.mapNavigation=x(d.options.mapNavigation,b));for(;n.length;)n.pop().destroy();e(f.enableButtons,f.enabled)&&!d.renderer.forExport&&a.objectEach(f.buttons,function(a,b){p=x(f.buttonOptions,a);d.styledMode||(c=p.theme,c.style=x(p.theme.style,p.style),m=(g=c.states)&&g.hover,t=g&&g.select);a=d.renderer.button(p.text,0,0,l,c,m,t,0,"zoomIn"===b?"topbutton":"bottombutton").addClass("highcharts-map-navigation highcharts-"+
{zoomIn:"zoom-in",zoomOut:"zoom-out"}[b]).attr({width:p.width,height:p.height,title:d.options.lang[b],padding:p.padding,zIndex:5}).add();a.handler=p.onclick;a.align(u(p,{width:a.width,height:2*a.height}),null,p.alignTo);r(a.element,"dblclick",h);n.push(a)});this.updateEvents(f)};k.prototype.updateEvents=function(a){var b=this.chart;e(a.enableDoubleClickZoom,a.enabled)||a.enableDoubleClickZoomTo?this.unbindDblClick=this.unbindDblClick||r(b.container,"dblclick",function(a){b.pointer.onContainerDblClick(a)}):
this.unbindDblClick&&(this.unbindDblClick=this.unbindDblClick());e(a.enableMouseWheelZoom,a.enabled)?this.unbindMouseWheel=this.unbindMouseWheel||r(b.container,void 0===f.onmousewheel?"DOMMouseScroll":"mousewheel",function(a){b.pointer.onContainerMouseWheel(a);h(a);return!1}):this.unbindMouseWheel&&(this.unbindMouseWheel=this.unbindMouseWheel())};u(q.prototype,{fitToBox:function(a,d){[["x","width"],["y","height"]].forEach(function(b){var e=b[0];b=b[1];a[e]+a[b]>d[e]+d[b]&&(a[b]>d[b]?(a[b]=d[b],a[e]=
d[e]):a[e]=d[e]+d[b]-a[b]);a[b]>d[b]&&(a[b]=d[b]);a[e]<d[e]&&(a[e]=d[e])});return a},mapZoom:function(a,d,f,q,c){var g=this.xAxis[0],b=g.max-g.min,t=e(d,g.min+b/2),l=b*a,b=this.yAxis[0],n=b.max-b.min,w=e(f,b.min+n/2),n=n*a,t=this.fitToBox({x:t-l*(q?(q-g.pos)/g.len:.5),y:w-n*(c?(c-b.pos)/b.len:.5),width:l,height:n},{x:g.dataMin,y:b.dataMin,width:g.dataMax-g.dataMin,height:b.dataMax-b.dataMin}),l=t.x<=g.dataMin&&t.width>=g.dataMax-g.dataMin&&t.y<=b.dataMin&&t.height>=b.dataMax-b.dataMin;q&&(g.fixTo=
[q-g.pos,d]);c&&(b.fixTo=[c-b.pos,f]);void 0===a||l?(g.setExtremes(void 0,void 0,!1),b.setExtremes(void 0,void 0,!1)):(g.setExtremes(t.x,t.x+t.width,!1),b.setExtremes(t.y,t.y+t.height,!1));this.redraw()}});r(q,"beforeRender",function(){this.mapNavigation=new k(this);this.mapNavigation.update()});a.MapNavigation=k});z(h,"parts-map/MapPointer.js",[h["parts/Globals.js"]],function(a){var h=a.extend,k=a.pick,r=a.Pointer;a=a.wrap;h(r.prototype,{onContainerDblClick:function(a){var f=this.chart;a=this.normalize(a);
f.options.mapNavigation.enableDoubleClickZoomTo?f.pointer.inClass(a.target,"highcharts-tracker")&&f.hoverPoint&&f.hoverPoint.zoomTo():f.isInsidePlot(a.chartX-f.plotLeft,a.chartY-f.plotTop)&&f.mapZoom(.5,f.xAxis[0].toValue(a.chartX),f.yAxis[0].toValue(a.chartY),a.chartX,a.chartY)},onContainerMouseWheel:function(a){var f=this.chart,h;a=this.normalize(a);h=a.detail||-(a.wheelDelta/120);f.isInsidePlot(a.chartX-f.plotLeft,a.chartY-f.plotTop)&&f.mapZoom(Math.pow(f.options.mapNavigation.mouseWheelSensitivity,
h),f.xAxis[0].toValue(a.chartX),f.yAxis[0].toValue(a.chartY),a.chartX,a.chartY)}});a(r.prototype,"zoomOption",function(a){var f=this.chart.options.mapNavigation;k(f.enableTouchZoom,f.enabled)&&(this.chart.options.chart.pinchType="xy");a.apply(this,[].slice.call(arguments,1))});a(r.prototype,"pinchTranslate",function(a,f,h,k,e,b,d){a.call(this,f,h,k,e,b,d);"map"===this.chart.options.chart.type&&this.hasZoom&&(a=k.scaleX>k.scaleY,this.pinchTranslateDirection(!a,f,h,k,e,b,d,a?k.scaleX:k.scaleY))})});
z(h,"parts-map/MapSeries.js",[h["parts/Globals.js"]],function(a){var h=a.colorPointMixin,k=a.extend,r=a.isNumber,q=a.merge,f=a.noop,u=a.pick,x=a.isArray,e=a.Point,b=a.Series,d=a.seriesType,y=a.seriesTypes,p=a.splat;d("map","scatter",{animation:!1,dataLabels:{crop:!1,formatter:function(){return this.point.value},inside:!0,overflow:!1,padding:0,verticalAlign:"middle"},marker:null,nullColor:"#f7f7f7",stickyTracking:!1,tooltip:{followPointer:!0,pointFormat:"{point.name}: {point.value}\x3cbr/\x3e"},turboThreshold:0,
allAreas:!0,borderColor:"#cccccc",borderWidth:1,joinBy:"hc-key",states:{hover:{halo:null,brightness:.2},normal:{animation:!0},select:{color:"#cccccc"},inactive:{opacity:1}}},q(a.colorSeriesMixin,{type:"map",getExtremesFromAll:!0,useMapGeometry:!0,forceDL:!0,searchPoint:f,directTouch:!0,preserveAspectRatio:!0,pointArrayMap:["value"],setOptions:function(a){a=b.prototype.setOptions.call(this,a);var c=a.joinBy;null===c&&(c="_i");c=this.joinBy=p(c);c[1]||(c[1]=c[0]);return a},getBox:function(c){var b=
Number.MAX_VALUE,d=-b,e=b,l=-b,n=b,w=b,f=this.xAxis,v=this.yAxis,h;(c||[]).forEach(function(c){if(c.path){"string"===typeof c.path&&(c.path=a.splitPath(c.path));var g=c.path||[],m=g.length,f=!1,v=-b,A=b,t=-b,k=b,B=c.properties;if(!c._foundBox){for(;m--;)r(g[m])&&(f?(v=Math.max(v,g[m]),A=Math.min(A,g[m])):(t=Math.max(t,g[m]),k=Math.min(k,g[m])),f=!f);c._midX=A+(v-A)*u(c.middleX,B&&B["hc-middle-x"],.5);c._midY=k+(t-k)*u(c.middleY,B&&B["hc-middle-y"],.5);c._maxX=v;c._minX=A;c._maxY=t;c._minY=k;c.labelrank=
u(c.labelrank,(v-A)*(t-k));c._foundBox=!0}d=Math.max(d,c._maxX);e=Math.min(e,c._minX);l=Math.max(l,c._maxY);n=Math.min(n,c._minY);w=Math.min(c._maxX-c._minX,c._maxY-c._minY,w);h=!0}});h&&(this.minY=Math.min(n,u(this.minY,b)),this.maxY=Math.max(l,u(this.maxY,-b)),this.minX=Math.min(e,u(this.minX,b)),this.maxX=Math.max(d,u(this.maxX,-b)),f&&void 0===f.options.minRange&&(f.minRange=Math.min(5*w,(this.maxX-this.minX)/5,f.minRange||b)),v&&void 0===v.options.minRange&&(v.minRange=Math.min(5*w,(this.maxY-
this.minY)/5,v.minRange||b)))},hasData:function(){return!!this.processedXData.length},getExtremes:function(){b.prototype.getExtremes.call(this,this.valueData);this.chart.hasRendered&&this.isDirtyData&&this.getBox(this.options.data);this.valueMin=this.dataMin;this.valueMax=this.dataMax;this.dataMin=this.minY;this.dataMax=this.maxY},translatePath:function(a){var c=!1,b=this.xAxis,d=this.yAxis,l=b.min,n=b.transA,b=b.minPixelPadding,w=d.min,e=d.transA,d=d.minPixelPadding,f,k=[];if(a)for(f=a.length;f--;)r(a[f])?
(k[f]=c?(a[f]-l)*n+b:(a[f]-w)*e+d,c=!c):k[f]=a[f];return k},setData:function(c,g,d,f){var l=this.options,n=this.chart.options.chart,e=n&&n.map,m=l.mapData,v=this.joinBy,t=l.keys||this.pointArrayMap,k=[],h={},p=this.chart.mapTransforms;!m&&e&&(m="string"===typeof e?a.maps[e]:e);c&&c.forEach(function(b,g){var n=0;if(r(b))c[g]={value:b};else if(x(b)){c[g]={};!l.keys&&b.length>t.length&&"string"===typeof b[0]&&(c[g]["hc-key"]=b[0],++n);for(var d=0;d<t.length;++d,++n)t[d]&&void 0!==b[n]&&(0<t[d].indexOf(".")?
a.Point.prototype.setNestedProperty(c[g],b[n],t[d]):c[g][t[d]]=b[n])}v&&"_i"===v[0]&&(c[g]._i=g)});this.getBox(c);(this.chart.mapTransforms=p=n&&n.mapTransforms||m&&m["hc-transform"]||p)&&a.objectEach(p,function(a){a.rotation&&(a.cosAngle=Math.cos(a.rotation),a.sinAngle=Math.sin(a.rotation))});if(m){"FeatureCollection"===m.type&&(this.mapTitle=m.title,m=a.geojson(m,this.type,this));this.mapData=m;this.mapMap={};for(p=0;p<m.length;p++)n=m[p],e=n.properties,n._i=p,v[0]&&e&&e[v[0]]&&(n[v[0]]=e[v[0]]),
h[n[v[0]]]=n;this.mapMap=h;c&&v[1]&&c.forEach(function(a){h[a[v[1]]]&&k.push(h[a[v[1]]])});l.allAreas?(this.getBox(m),c=c||[],v[1]&&c.forEach(function(a){k.push(a[v[1]])}),k="|"+k.map(function(a){return a&&a[v[0]]}).join("|")+"|",m.forEach(function(a){v[0]&&-1!==k.indexOf("|"+a[v[0]]+"|")||(c.push(q(a,{value:null})),f=!1)})):this.getBox(k)}b.prototype.setData.call(this,c,g,d,f)},drawGraph:f,drawDataLabels:f,doFullTranslate:function(){return this.isDirtyData||this.chart.isResizing||this.chart.renderer.isVML||
!this.baseTrans},translate:function(){var a=this,b=a.xAxis,d=a.yAxis,e=a.doFullTranslate();a.generatePoints();a.data.forEach(function(c){c.plotX=b.toPixels(c._midX,!0);c.plotY=d.toPixels(c._midY,!0);e&&(c.shapeType="path",c.shapeArgs={d:a.translatePath(c.path)})});a.translateColors()},pointAttribs:function(a,b){b=a.series.chart.styledMode?this.colorAttribs(a):y.column.prototype.pointAttribs.call(this,a,b);b["stroke-width"]=u(a.options[this.pointAttrToOptions&&this.pointAttrToOptions["stroke-width"]||
"borderWidth"],"inherit");return b},drawPoints:function(){var a=this,b=a.xAxis,d=a.yAxis,e=a.group,l=a.chart,n=l.renderer,f,A,v,k,h=this.baseTrans,p,r,q,x,G;a.transformGroup||(a.transformGroup=n.g().attr({scaleX:1,scaleY:1}).add(e),a.transformGroup.survive=!0);a.doFullTranslate()?(l.hasRendered&&!l.styledMode&&a.points.forEach(function(c){c.shapeArgs&&(c.shapeArgs.fill=a.pointAttribs(c,c.state).fill)}),a.group=a.transformGroup,y.column.prototype.drawPoints.apply(a),a.group=e,a.points.forEach(function(c){if(c.graphic){var b=
"";c.name&&(b+="highcharts-name-"+c.name.replace(/ /g,"-").toLowerCase());c.properties&&c.properties["hc-key"]&&(b+=" highcharts-key-"+c.properties["hc-key"].toLowerCase());b&&c.graphic.addClass(b);l.styledMode&&c.graphic.css(a.pointAttribs(c,c.selected&&"select"))}}),this.baseTrans={originX:b.min-b.minPixelPadding/b.transA,originY:d.min-d.minPixelPadding/d.transA+(d.reversed?0:d.len/d.transA),transAX:b.transA,transAY:d.transA},this.transformGroup.animate({translateX:0,translateY:0,scaleX:1,scaleY:1})):
(f=b.transA/h.transAX,A=d.transA/h.transAY,v=b.toPixels(h.originX,!0),k=d.toPixels(h.originY,!0),.99<f&&1.01>f&&.99<A&&1.01>A&&(A=f=1,v=Math.round(v),k=Math.round(k)),p=this.transformGroup,l.renderer.globalAnimation?(r=p.attr("translateX"),q=p.attr("translateY"),x=p.attr("scaleX"),G=p.attr("scaleY"),p.attr({animator:0}).animate({animator:1},{step:function(a,c){p.attr({translateX:r+(v-r)*c.pos,translateY:q+(k-q)*c.pos,scaleX:x+(f-x)*c.pos,scaleY:G+(A-G)*c.pos})}})):p.attr({translateX:v,translateY:k,
scaleX:f,scaleY:A}));l.styledMode||e.element.setAttribute("stroke-width",u(a.options[a.pointAttrToOptions&&a.pointAttrToOptions["stroke-width"]||"borderWidth"],1)/(f||1));this.drawMapDataLabels()},drawMapDataLabels:function(){b.prototype.drawDataLabels.call(this);this.dataLabelsGroup&&this.dataLabelsGroup.clip(this.chart.clipRect)},render:function(){var a=this,g=b.prototype.render;a.chart.renderer.isVML&&3E3<a.data.length?setTimeout(function(){g.call(a)}):g.call(a)},animate:function(a){var c=this.options.animation,
b=this.group,d=this.xAxis,l=this.yAxis,n=d.pos,e=l.pos;this.chart.renderer.isSVG&&(!0===c&&(c={duration:1E3}),a?b.attr({translateX:n+d.len/2,translateY:e+l.len/2,scaleX:.001,scaleY:.001}):(b.animate({translateX:n,translateY:e,scaleX:1,scaleY:1},c),this.animate=null))},animateDrilldown:function(a){var c=this.chart.plotBox,b=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],d=b.bBox,l=this.chart.options.drilldown.animation;a||(a=Math.min(d.width/c.width,d.height/c.height),b.shapeArgs=
{scaleX:a,scaleY:a,translateX:d.x,translateY:d.y},this.points.forEach(function(a){a.graphic&&a.graphic.attr(b.shapeArgs).animate({scaleX:1,scaleY:1,translateX:0,translateY:0},l)}),this.animate=null)},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,animateDrillupFrom:function(a){y.column.prototype.animateDrillupFrom.call(this,a)},animateDrillupTo:function(a){y.column.prototype.animateDrillupTo.call(this,a)}}),k({applyOptions:function(a,b){a=e.prototype.applyOptions.call(this,a,b);b=this.series;
var c=b.joinBy;b.mapData&&((c=void 0!==a[c[1]]&&b.mapMap[a[c[1]]])?(b.xyFromShape&&(a.x=c._midX,a.y=c._midY),k(a,c)):a.value=a.value||null);return a},onMouseOver:function(c){a.clearTimeout(this.colorInterval);if(null!==this.value||this.series.options.nullInteraction)e.prototype.onMouseOver.call(this,c);else this.series.onMouseOut(c)},zoomTo:function(){var a=this.series;a.xAxis.setExtremes(this._minX,this._maxX,!1);a.yAxis.setExtremes(this._minY,this._maxY,!1);a.chart.redraw()}},h))});z(h,"parts-map/MapLineSeries.js",
[h["parts/Globals.js"]],function(a){var h=a.seriesType,k=a.seriesTypes;h("mapline","map",{lineWidth:1,fillColor:"none"},{type:"mapline",colorProp:"stroke",pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},pointAttribs:function(a,h){a=k.map.prototype.pointAttribs.call(this,a,h);a.fill=this.options.fillColor;return a},drawLegendSymbol:k.line.prototype.drawLegendSymbol})});z(h,"parts-map/MapPointSeries.js",[h["parts/Globals.js"]],function(a){var h=a.merge,k=a.Point;a=a.seriesType;a("mappoint",
"scatter",{dataLabels:{crop:!1,defer:!1,enabled:!0,formatter:function(){return this.point.name},overflow:!1,style:{color:"#000000"}}},{type:"mappoint",forceDL:!0},{applyOptions:function(a,q){a=void 0!==a.lat&&void 0!==a.lon?h(a,this.series.chart.fromLatLonToPoint(a)):a;return k.prototype.applyOptions.call(this,a,q)}})});z(h,"parts-more/BubbleLegend.js",[h["parts/Globals.js"]],function(a){var h=a.Series,k=a.Legend,r=a.Chart,q=a.addEvent,f=a.wrap,u=a.color,x=a.isNumber,e=a.numberFormat,b=a.objectEach,
d=a.merge,y=a.noop,p=a.pick,c=a.stableSort,g=a.setOptions,m=a.arrayMin,t=a.arrayMax;g({legend:{bubbleLegend:{borderColor:void 0,borderWidth:2,className:void 0,color:void 0,connectorClassName:void 0,connectorColor:void 0,connectorDistance:60,connectorWidth:1,enabled:!1,labels:{className:void 0,allowOverlap:!1,format:"",formatter:void 0,align:"right",style:{fontSize:10,color:void 0},x:0,y:0},maxSize:60,minSize:10,legendIndex:0,ranges:{value:void 0,borderColor:void 0,color:void 0,connectorColor:void 0},
sizeBy:"area",sizeByAbsoluteValue:!1,zIndex:1,zThreshold:0}}});a.BubbleLegend=function(a,b){this.init(a,b)};a.BubbleLegend.prototype={init:function(a,b){this.options=a;this.visible=!0;this.chart=b.chart;this.legend=b},setState:y,addToLegend:function(a){a.splice(this.options.legendIndex,0,this)},drawLegendSymbol:function(a){var b=this.chart,l=this.options,g=p(a.options.itemDistance,20),d,e=l.ranges;d=l.connectorDistance;this.fontMetrics=b.renderer.fontMetrics(l.labels.style.fontSize.toString()+"px");
e&&e.length&&x(e[0].value)?(c(e,function(a,b){return b.value-a.value}),this.ranges=e,this.setOptions(),this.render(),b=this.getMaxLabelSize(),e=this.ranges[0].radius,a=2*e,d=d-e+b.width,d=0<d?d:0,this.maxLabel=b,this.movementX="left"===l.labels.align?d:0,this.legendItemWidth=a+d+g,this.legendItemHeight=a+this.fontMetrics.h/2):a.options.bubbleLegend.autoRanges=!0},setOptions:function(){var a=this.ranges,b=this.options,c=this.chart.series[b.seriesIndex],g=this.legend.baseline,e={"z-index":b.zIndex,
"stroke-width":b.borderWidth},f={"z-index":b.zIndex,"stroke-width":b.connectorWidth},m=this.getLabelStyles(),k=c.options.marker.fillOpacity,h=this.chart.styledMode;a.forEach(function(l,n){h||(e.stroke=p(l.borderColor,b.borderColor,c.color),e.fill=p(l.color,b.color,1!==k?u(c.color).setOpacity(k).get("rgba"):c.color),f.stroke=p(l.connectorColor,b.connectorColor,c.color));a[n].radius=this.getRangeRadius(l.value);a[n]=d(a[n],{center:a[0].radius-a[n].radius+g});h||d(!0,a[n],{bubbleStyle:d(!1,e),connectorStyle:d(!1,
f),labelStyle:m})},this)},getLabelStyles:function(){var a=this.options,c={},g="left"===a.labels.align,e=this.legend.options.rtl;b(a.labels.style,function(a,b){"color"!==b&&"fontSize"!==b&&"z-index"!==b&&(c[b]=a)});return d(!1,c,{"font-size":a.labels.style.fontSize,fill:p(a.labels.style.color,"#000000"),"z-index":a.zIndex,align:e||g?"right":"left"})},getRangeRadius:function(a){var b=this.options;return this.chart.series[this.options.seriesIndex].getRadius.call(this,b.ranges[b.ranges.length-1].value,
b.ranges[0].value,b.minSize,b.maxSize,a)},render:function(){var a=this.chart.renderer,b=this.options.zThreshold;this.symbols||(this.symbols={connectors:[],bubbleItems:[],labels:[]});this.legendSymbol=a.g("bubble-legend");this.legendItem=a.g("bubble-legend-item");this.legendSymbol.translateX=0;this.legendSymbol.translateY=0;this.ranges.forEach(function(a){a.value>=b&&this.renderRange(a)},this);this.legendSymbol.add(this.legendItem);this.legendItem.add(this.legendGroup);this.hideOverlappingLabels()},
renderRange:function(a){var b=this.options,c=b.labels,g=this.chart.renderer,d=this.symbols,l=d.labels,e=a.center,f=Math.abs(a.radius),m=b.connectorDistance,k=c.align,h=c.style.fontSize,m=this.legend.options.rtl||"left"===k?-m:m,c=b.connectorWidth,t=this.ranges[0].radius,p=e-f-b.borderWidth/2+c/2,y,h=h/2-(this.fontMetrics.h-h)/2,q=g.styledMode;"center"===k&&(m=0,b.connectorDistance=0,a.labelStyle.align="center");k=p+b.labels.y;y=t+m+b.labels.x;d.bubbleItems.push(g.circle(t,e+((p%1?1:.5)-(c%2?0:.5)),
f).attr(q?{}:a.bubbleStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-symbol "+(b.className||"")).add(this.legendSymbol));d.connectors.push(g.path(g.crispLine(["M",t,p,"L",t+m,p],b.connectorWidth)).attr(q?{}:a.connectorStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-connectors "+(b.connectorClassName||"")).add(this.legendSymbol));a=g.text(this.formatLabel(a),y,k+h).attr(q?{}:a.labelStyle).addClass("highcharts-bubble-legend-labels "+
(b.labels.className||"")).add(this.legendSymbol);l.push(a);a.placed=!0;a.alignAttr={x:y,y:k+h}},getMaxLabelSize:function(){var a,b;this.symbols.labels.forEach(function(c){b=c.getBBox(!0);a=a?b.width>a.width?b:a:b});return a||{}},formatLabel:function(b){var c=this.options,g=c.labels.formatter;return(c=c.labels.format)?a.format(c,b):g?g.call(b):e(b.value,1)},hideOverlappingLabels:function(){var a=this.chart,b=this.symbols;!this.options.labels.allowOverlap&&b&&(a.hideOverlappingLabels(b.labels),b.labels.forEach(function(a,
c){a.newOpacity?a.newOpacity!==a.oldOpacity&&b.connectors[c].show():b.connectors[c].hide()}))},getRanges:function(){var a=this.legend.bubbleLegend,b,c=a.options.ranges,g,e=Number.MAX_VALUE,f=-Number.MAX_VALUE;a.chart.series.forEach(function(a){a.isBubble&&!a.ignoreSeries&&(g=a.zData.filter(x),g.length&&(e=p(a.options.zMin,Math.min(e,Math.max(m(g),!1===a.options.displayNegative?a.options.zThreshold:-Number.MAX_VALUE))),f=p(a.options.zMax,Math.max(f,t(g)))))});b=e===f?[{value:f}]:[{value:e},{value:(e+
f)/2},{value:f,autoRanges:!0}];c.length&&c[0].radius&&b.reverse();b.forEach(function(a,g){c&&c[g]&&(b[g]=d(!1,c[g],a))});return b},predictBubbleSizes:function(){var a=this.chart,b=this.fontMetrics,c=a.legend.options,g="horizontal"===c.layout,d=g?a.legend.lastLineHeight:0,e=a.plotSizeX,f=a.plotSizeY,m=a.series[this.options.seriesIndex],a=Math.ceil(m.minPxSize),k=Math.ceil(m.maxPxSize),m=m.options.maxSize,h=Math.min(f,e);if(c.floating||!/%$/.test(m))b=k;else if(m=parseFloat(m),b=(h+d-b.h/2)*m/100/(m/
100+1),g&&f-b>=e||!g&&e-b>=f)b=k;return[a,Math.ceil(b)]},updateRanges:function(a,b){var c=this.legend.options.bubbleLegend;c.minSize=a;c.maxSize=b;c.ranges=this.getRanges()},correctSizes:function(){var a=this.legend,b=this.chart.series[this.options.seriesIndex];1<Math.abs(Math.ceil(b.maxPxSize)-this.options.maxSize)&&(this.updateRanges(this.options.minSize,b.maxPxSize),a.render())}};q(a.Legend,"afterGetAllItems",function(b){var c=this.bubbleLegend,g=this.options,d=g.bubbleLegend,e=this.chart.getVisibleBubbleSeriesIndex();
c&&c.ranges&&c.ranges.length&&(d.ranges.length&&(d.autoRanges=!!d.ranges[0].autoRanges),this.destroyItem(c));0<=e&&g.enabled&&d.enabled&&(d.seriesIndex=e,this.bubbleLegend=new a.BubbleLegend(d,this),this.bubbleLegend.addToLegend(b.allItems))});r.prototype.getVisibleBubbleSeriesIndex=function(){for(var a=this.series,b=0;b<a.length;){if(a[b]&&a[b].isBubble&&a[b].visible&&a[b].zData.length)return b;b++}return-1};k.prototype.getLinesHeights=function(){var a=this.allItems,b=[],c,g=a.length,d,e=0;for(d=
0;d<g;d++)if(a[d].legendItemHeight&&(a[d].itemHeight=a[d].legendItemHeight),a[d]===a[g-1]||a[d+1]&&a[d]._legendItemPos[1]!==a[d+1]._legendItemPos[1]){b.push({height:0});c=b[b.length-1];for(e;e<=d;e++)a[e].itemHeight>c.height&&(c.height=a[e].itemHeight);c.step=d}return b};k.prototype.retranslateItems=function(a){var b,c,g,d=this.options.rtl,e=0;this.allItems.forEach(function(f,m){b=f.legendGroup.translateX;c=f._legendItemPos[1];if((g=f.movementX)||d&&f.ranges)g=d?b-f.options.maxSize/2:b+g,f.legendGroup.attr({translateX:g});
m>a[e].step&&e++;f.legendGroup.attr({translateY:Math.round(c+a[e].height/2)});f._legendItemPos[1]=c+a[e].height/2})};q(h,"legendItemClick",function(){var a=this.chart,b=this.visible,c=this.chart.legend;c&&c.bubbleLegend&&(this.visible=!b,this.ignoreSeries=b,a=0<=a.getVisibleBubbleSeriesIndex(),c.bubbleLegend.visible!==a&&(c.update({bubbleLegend:{enabled:a}}),c.bubbleLegend.visible=a),this.visible=b)});f(r.prototype,"drawChartBox",function(a,c,g){var d=this.legend,e=0<=this.getVisibleBubbleSeriesIndex(),
f;d&&d.options.enabled&&d.bubbleLegend&&d.options.bubbleLegend.autoRanges&&e?(f=d.bubbleLegend.options,e=d.bubbleLegend.predictBubbleSizes(),d.bubbleLegend.updateRanges(e[0],e[1]),f.placed||(d.group.placed=!1,d.allItems.forEach(function(a){a.legendGroup.translateY=null})),d.render(),this.getMargins(),this.axes.forEach(function(a){a.render();f.placed||(a.setScale(),a.updateNames(),b(a.ticks,function(a){a.isNew=!0;a.isNewLabel=!0}))}),f.placed=!0,this.getMargins(),a.call(this,c,g),d.bubbleLegend.correctSizes(),
d.retranslateItems(d.getLinesHeights())):(a.call(this,c,g),d&&d.options.enabled&&d.bubbleLegend&&(d.render(),d.retranslateItems(d.getLinesHeights())))})});z(h,"parts-more/BubbleSeries.js",[h["parts/Globals.js"]],function(a){var h=a.arrayMax,k=a.arrayMin,r=a.Axis,q=a.color,f=a.isNumber,u=a.noop,x=a.pick,e=a.pInt,b=a.Point,d=a.Series,y=a.seriesType,p=a.seriesTypes;y("bubble","scatter",{dataLabels:{formatter:function(){return this.point.z},inside:!0,verticalAlign:"middle"},animationLimit:250,marker:{lineColor:null,
lineWidth:1,fillOpacity:.5,radius:null,states:{hover:{radiusPlus:0}},symbol:"circle"},minSize:8,maxSize:"20%",softThreshold:!1,states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0,zoneAxis:"z"},{pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],specialGroup:"group",bubblePadding:!0,zoneAxis:"z",directTouch:!0,isBubble:!0,pointAttribs:function(a,b){var c=this.options.marker.fillOpacity;
a=d.prototype.pointAttribs.call(this,a,b);1!==c&&(a.fill=q(a.fill).setOpacity(c).get("rgba"));return a},getRadii:function(a,b,d){var c,g=this.zData,e=d.minPxSize,f=d.maxPxSize,m=[],h;c=0;for(d=g.length;c<d;c++)h=g[c],m.push(this.getRadius(a,b,e,f,h));this.radii=m},getRadius:function(a,b,d,e,h){var c=this.options,g="width"!==c.sizeBy,m=c.zThreshold,k=b-a;c.sizeByAbsoluteValue&&null!==h&&(h=Math.abs(h-m),k=Math.max(b-m,Math.abs(a-m)),a=0);f(h)?h<a?d=d/2-1:(a=0<k?(h-a)/k:.5,g&&0<=a&&(a=Math.sqrt(a)),
d=Math.ceil(d+a*(e-d))/2):d=null;return d},animate:function(a){!a&&this.points.length<this.options.animationLimit&&(this.points.forEach(function(a){var b=a.graphic,c;b&&b.width&&(c={x:b.x,y:b.y,width:b.width,height:b.height},b.attr({x:a.plotX,y:a.plotY,width:1,height:1}),b.animate(c,this.options.animation))},this),this.animate=null)},hasData:function(){return!!this.processedXData.length},translate:function(){var b,d=this.data,e,h,k=this.radii;p.scatter.prototype.translate.call(this);for(b=d.length;b--;)e=
d[b],h=k?k[b]:0,f(h)&&h>=this.minPxSize/2?(e.marker=a.extend(e.marker,{radius:h,width:2*h,height:2*h}),e.dlBox={x:e.plotX-h,y:e.plotY-h,width:2*h,height:2*h}):e.shapeArgs=e.plotY=e.dlBox=void 0},alignDataLabel:p.column.prototype.alignDataLabel,buildKDTree:u,applyZones:u},{haloPath:function(a){return b.prototype.haloPath.call(this,0===a?0:(this.marker?this.marker.radius||0:0)+a)},ttBelow:!1});r.prototype.beforePadding=function(){var b=this,d=this.len,m=this.chart,p=0,l=d,n=this.isXAxis,y=n?"xData":
"yData",q=this.min,r={},u=Math.min(m.plotWidth,m.plotHeight),z=Number.MAX_VALUE,D=-Number.MAX_VALUE,E=this.max-q,C=d/E,F=[];this.series.forEach(function(c){var d=c.options;!c.bubblePadding||!c.visible&&m.options.chart.ignoreHiddenSeries||(b.allowZoomOutside=!0,F.push(c),n&&(["minSize","maxSize"].forEach(function(a){var b=d[a],c=/%$/.test(b),b=e(b);r[a]=c?u*b/100:b}),c.minPxSize=r.minSize,c.maxPxSize=Math.max(r.maxSize,r.minSize),c=c.zData.filter(a.isNumber),c.length&&(z=x(d.zMin,Math.min(z,Math.max(k(c),
!1===d.displayNegative?d.zThreshold:-Number.MAX_VALUE))),D=x(d.zMax,Math.max(D,h(c))))))});F.forEach(function(a){var c=a[y],d=c.length,e;n&&a.getRadii(z,D,a);if(0<E)for(;d--;)f(c[d])&&b.dataMin<=c[d]&&c[d]<=b.dataMax&&(e=a.radii[d],p=Math.min((c[d]-q)*C-e,p),l=Math.max((c[d]-q)*C+e,l))});F.length&&0<E&&!this.isLog&&(l-=d,C*=(d+Math.max(0,p)-Math.min(l,d))/d,[["min","userMin",p],["max","userMax",l]].forEach(function(a){void 0===x(b.options[a[0]],b[a[1]])&&(b[a[0]]+=a[2]/C)}))}});z(h,"parts-map/MapBubbleSeries.js",
[h["parts/Globals.js"]],function(a){var h=a.merge,k=a.Point,r=a.seriesType,q=a.seriesTypes;q.bubble&&r("mapbubble","bubble",{animationLimit:500,tooltip:{pointFormat:"{point.name}: {point.z}"}},{xyFromShape:!0,type:"mapbubble",pointArrayMap:["z"],getMapData:q.map.prototype.getMapData,getBox:q.map.prototype.getBox,setData:q.map.prototype.setData,setOptions:q.map.prototype.setOptions},{applyOptions:function(a,r){return a&&void 0!==a.lat&&void 0!==a.lon?k.prototype.applyOptions.call(this,h(a,this.series.chart.fromLatLonToPoint(a)),
r):q.map.prototype.pointClass.prototype.applyOptions.call(this,a,r)},isValid:function(){return"number"===typeof this.z},ttBelow:!1})});z(h,"parts-map/HeatmapSeries.js",[h["parts/Globals.js"]],function(a){var h=a.colorPointMixin,k=a.merge,r=a.noop,q=a.pick,f=a.Series,u=a.seriesType,x=a.seriesTypes;u("heatmap","scatter",{animation:!1,borderWidth:0,nullColor:"#f7f7f7",dataLabels:{formatter:function(){return this.point.value},inside:!0,verticalAlign:"middle",crop:!1,overflow:!1,padding:0},marker:null,
pointRange:null,tooltip:{pointFormat:"{point.x}, {point.y}: {point.value}\x3cbr/\x3e"},states:{hover:{halo:!1,brightness:.2}}},k(a.colorSeriesMixin,{pointArrayMap:["y","value"],hasPointSpecificOptions:!0,getExtremesFromAll:!0,directTouch:!0,init:function(){var a;x.scatter.prototype.init.apply(this,arguments);a=this.options;a.pointRange=q(a.pointRange,a.colsize||1);this.yAxis.axisPointRange=a.rowsize||1},translate:function(){var a=this.options,b=this.xAxis,d=this.yAxis,f=a.pointPadding||0,h=function(a,
b,c){return Math.min(Math.max(b,a),c)},c=this.pointPlacementToXValue();this.generatePoints();this.points.forEach(function(g){var e=(a.colsize||1)/2,k=(a.rowsize||1)/2,l=h(Math.round(b.len-b.translate(g.x-e,0,1,0,1,-c)),-b.len,2*b.len),e=h(Math.round(b.len-b.translate(g.x+e,0,1,0,1,-c)),-b.len,2*b.len),p=h(Math.round(d.translate(g.y-k,0,1,0,1)),-d.len,2*d.len),k=h(Math.round(d.translate(g.y+k,0,1,0,1)),-d.len,2*d.len),y=q(g.pointPadding,f);g.plotX=g.clientX=(l+e)/2;g.plotY=(p+k)/2;g.shapeType="rect";
g.shapeArgs={x:Math.min(l,e)+y,y:Math.min(p,k)+y,width:Math.abs(e-l)-2*y,height:Math.abs(k-p)-2*y}});this.translateColors()},drawPoints:function(){var a=this.chart.styledMode?"css":"attr";x.column.prototype.drawPoints.call(this);this.points.forEach(function(b){b.graphic[a](this.colorAttribs(b))},this)},hasData:function(){return!!this.processedXData.length},getValidPoints:function(a,b){return f.prototype.getValidPoints.call(this,a,b,!0)},animate:r,getBox:r,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,
alignDataLabel:x.column.prototype.alignDataLabel,getExtremes:function(){f.prototype.getExtremes.call(this,this.valueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;f.prototype.getExtremes.call(this)}}),a.extend({haloPath:function(a){if(!a)return[];var b=this.shapeArgs;return["M",b.x-a,b.y-a,"L",b.x-a,b.y+b.height+a,b.x+b.width+a,b.y+b.height+a,b.x+b.width+a,b.y-a,"Z"]}},h))});z(h,"parts-map/GeoJSON.js",[h["parts/Globals.js"]],function(a){function h(a,b){var d,e,f,c=!1,g=a.x,h=a.y;a=0;
for(d=b.length-1;a<b.length;d=a++)e=b[a][1]>h,f=b[d][1]>h,e!==f&&g<(b[d][0]-b[a][0])*(h-b[a][1])/(b[d][1]-b[a][1])+b[a][0]&&(c=!c);return c}var k=a.Chart,r=a.extend,q=a.format,f=a.merge,u=a.win,x=a.wrap;k.prototype.transformFromLatLon=function(e,b){if(void 0===u.proj4)return a.error(21,!1,this),{x:0,y:null};e=u.proj4(b.crs,[e.lon,e.lat]);var d=b.cosAngle||b.rotation&&Math.cos(b.rotation),f=b.sinAngle||b.rotation&&Math.sin(b.rotation);e=b.rotation?[e[0]*d+e[1]*f,-e[0]*f+e[1]*d]:e;return{x:((e[0]-(b.xoffset||
0))*(b.scale||1)+(b.xpan||0))*(b.jsonres||1)+(b.jsonmarginX||0),y:(((b.yoffset||0)-e[1])*(b.scale||1)+(b.ypan||0))*(b.jsonres||1)-(b.jsonmarginY||0)}};k.prototype.transformToLatLon=function(e,b){if(void 0===u.proj4)a.error(21,!1,this);else{e={x:((e.x-(b.jsonmarginX||0))/(b.jsonres||1)-(b.xpan||0))/(b.scale||1)+(b.xoffset||0),y:((-e.y-(b.jsonmarginY||0))/(b.jsonres||1)+(b.ypan||0))/(b.scale||1)+(b.yoffset||0)};var d=b.cosAngle||b.rotation&&Math.cos(b.rotation),f=b.sinAngle||b.rotation&&Math.sin(b.rotation);
b=u.proj4(b.crs,"WGS84",b.rotation?{x:e.x*d+e.y*-f,y:e.x*f+e.y*d}:e);return{lat:b.y,lon:b.x}}};k.prototype.fromPointToLatLon=function(e){var b=this.mapTransforms,d;if(b){for(d in b)if(b.hasOwnProperty(d)&&b[d].hitZone&&h({x:e.x,y:-e.y},b[d].hitZone.coordinates[0]))return this.transformToLatLon(e,b[d]);return this.transformToLatLon(e,b["default"])}a.error(22,!1,this)};k.prototype.fromLatLonToPoint=function(e){var b=this.mapTransforms,d,f;if(!b)return a.error(22,!1,this),{x:0,y:null};for(d in b)if(b.hasOwnProperty(d)&&
b[d].hitZone&&(f=this.transformFromLatLon(e,b[d]),h({x:f.x,y:-f.y},b[d].hitZone.coordinates[0])))return f;return this.transformFromLatLon(e,b["default"])};a.geojson=function(a,b,d){var f=[],e=[],c=function(a){var b,c=a.length;e.push("M");for(b=0;b<c;b++)1===b&&e.push("L"),e.push(a[b][0],-a[b][1])};b=b||"map";a.features.forEach(function(a){var d=a.geometry,g=d.type,d=d.coordinates;a=a.properties;var h;e=[];"map"===b||"mapbubble"===b?("Polygon"===g?(d.forEach(c),e.push("Z")):"MultiPolygon"===g&&(d.forEach(function(a){a.forEach(c)}),
e.push("Z")),e.length&&(h={path:e})):"mapline"===b?("LineString"===g?c(d):"MultiLineString"===g&&d.forEach(c),e.length&&(h={path:e})):"mappoint"===b&&"Point"===g&&(h={x:d[0],y:-d[1]});h&&f.push(r(h,{name:a.name||a.NAME,properties:a}))});d&&a.copyrightShort&&(d.chart.mapCredits=q(d.chart.options.credits.mapText,{geojson:a}),d.chart.mapCreditsFull=q(d.chart.options.credits.mapTextFull,{geojson:a}));return f};x(k.prototype,"addCredits",function(a,b){b=f(!0,this.options.credits,b);this.mapCredits&&(b.href=
null);a.call(this,b);this.credits&&this.mapCreditsFull&&this.credits.attr({title:this.mapCreditsFull})})});z(h,"parts-map/Map.js",[h["parts/Globals.js"]],function(a){function h(a,b,e,c,g,f,h,k){return["M",a+g,b,"L",a+e-f,b,"C",a+e-f/2,b,a+e,b+f/2,a+e,b+f,"L",a+e,b+c-h,"C",a+e,b+c-h/2,a+e-h/2,b+c,a+e-h,b+c,"L",a+k,b+c,"C",a+k/2,b+c,a,b+c-k/2,a,b+c-k,"L",a,b+g,"C",a,b+g/2,a+g/2,b,a+g,b,"Z"]}var k=a.Chart,r=a.defaultOptions,q=a.extend,f=a.merge,u=a.pick,x=a.Renderer,e=a.SVGRenderer,b=a.VMLRenderer;q(r.lang,
{zoomIn:"Zoom in",zoomOut:"Zoom out"});r.mapNavigation={buttonOptions:{alignTo:"plotBox",align:"left",verticalAlign:"top",x:0,width:18,height:18,padding:5,style:{fontSize:"15px",fontWeight:"bold"},theme:{"stroke-width":1,"text-align":"center"}},buttons:{zoomIn:{onclick:function(){this.mapZoom(.5)},text:"+",y:0},zoomOut:{onclick:function(){this.mapZoom(2)},text:"-",y:28}},mouseWheelSensitivity:1.1};a.splitPath=function(a){var b;a=a.replace(/([A-Za-z])/g," $1 ");a=a.replace(/^\s*/,"").replace(/\s*$/,
"");a=a.split(/[ ,]+/);for(b=0;b<a.length;b++)/[a-zA-Z]/.test(a[b])||(a[b]=parseFloat(a[b]));return a};a.maps={};e.prototype.symbols.topbutton=function(a,b,e,c,g){return h(a-1,b-1,e,c,g.r,g.r,0,0)};e.prototype.symbols.bottombutton=function(a,b,e,c,g){return h(a-1,b-1,e,c,0,0,g.r,g.r)};x===b&&["topbutton","bottombutton"].forEach(function(a){b.prototype.symbols[a]=e.prototype.symbols[a]});a.Map=a.mapChart=function(b,e,h){var c="string"===typeof b||b.nodeName,d=arguments[c?1:0],m=d,p={endOnTick:!1,visible:!1,
minPadding:0,maxPadding:0,startOnTick:!1},l,n=a.getOptions().credits;l=d.series;d.series=null;d=f({chart:{panning:"xy",type:"map"},credits:{mapText:u(n.mapText,' \u00a9 \x3ca href\x3d"{geojson.copyrightUrl}"\x3e{geojson.copyrightShort}\x3c/a\x3e'),mapTextFull:u(n.mapTextFull,"{geojson.copyright}")},tooltip:{followTouchMove:!1},xAxis:p,yAxis:f(p,{reversed:!0})},d,{chart:{inverted:!1,alignTicks:!1}});d.series=m.series=l;return c?new k(b,d,h):new k(d,e)}});z(h,"masters/modules/map.src.js",[],function(){})});
//# sourceMappingURL=map.js.map
(function(a){"object"===typeof module&&module.exports?(a["default"]=a,module.exports=a):"function"===typeof define&&define.amd?define("highcharts/modules/map",["highcharts"],function(B){a(B);a.Highcharts=B;return a}):a("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(a){function B(a,n,h,q){a.hasOwnProperty(n)||(a[n]=q.apply(null,h))}a=a?a._modules:{};B(a,"Core/Axis/MapAxis.js",[a["Core/Axis/Axis.js"],a["Core/Utilities.js"]],function(a,n){var h=n.addEvent,q=n.pick,c=function(){return function(a){this.axis=
a}}();n=function(){function a(){}a.compose=function(a){a.keepProps.push("mapAxis");h(a,"init",function(){this.mapAxis||(this.mapAxis=new c(this))});h(a,"getSeriesExtremes",function(){if(this.mapAxis){var a=[];this.isXAxis&&(this.series.forEach(function(c,m){c.useMapGeometry&&(a[m]=c.xData,c.xData=[])}),this.mapAxis.seriesXData=a)}});h(a,"afterGetSeriesExtremes",function(){if(this.mapAxis){var a=this.mapAxis.seriesXData||[],c;if(this.isXAxis){var m=q(this.dataMin,Number.MAX_VALUE);var k=q(this.dataMax,
-Number.MAX_VALUE);this.series.forEach(function(p,u){p.useMapGeometry&&(m=Math.min(m,q(p.minX,m)),k=Math.max(k,q(p.maxX,k)),p.xData=a[u],c=!0)});c&&(this.dataMin=m,this.dataMax=k);this.mapAxis.seriesXData=void 0}}});h(a,"afterSetAxisTranslation",function(){if(this.mapAxis){var a=this.chart,c=a.plotWidth/a.plotHeight;a=a.xAxis[0];var m;"yAxis"===this.coll&&"undefined"!==typeof a.transA&&this.series.forEach(function(a){a.preserveAspectRatio&&(m=!0)});if(m&&(this.transA=a.transA=Math.min(this.transA,
a.transA),c/=(a.max-a.min)/(this.max-this.min),c=1>c?this:a,a=(c.max-c.min)*c.transA,c.mapAxis.pixelPadding=c.len-a,c.minPixelPadding=c.mapAxis.pixelPadding/2,a=c.mapAxis.fixTo)){a=a[1]-c.toValue(a[0],!0);a*=c.transA;if(Math.abs(a)>c.minPixelPadding||c.min===c.dataMin&&c.max===c.dataMax)a=0;c.minPixelPadding-=a}}});h(a,"render",function(){this.mapAxis&&(this.mapAxis.fixTo=void 0)})};return a}();n.compose(a);return n});B(a,"Mixins/ColorSeries.js",[],function(){return{colorPointMixin:{setVisible:function(a){var n=
this,h=a?"show":"hide";n.visible=n.options.visible=!!a;["graphic","dataLabel"].forEach(function(a){if(n[a])n[a][h]()});this.series.buildKDTree()}},colorSeriesMixin:{optionalAxis:"colorAxis",colorAxis:0,translateColors:function(){var a=this,n=this.options.nullColor,h=this.colorAxis,q=this.colorKey;(this.data.length?this.data:this.points).forEach(function(c){var r=c.getNestedProperty(q);(r=c.options.color||(c.isNull||null===c.value?n:h&&"undefined"!==typeof r?h.toColor(r,c):c.color||a.color))&&c.color!==
r&&(c.color=r,"point"===a.options.legendType&&c.legendItem&&a.chart.legend.colorizeItem(c,c.visible))})}}}});B(a,"Core/Axis/ColorAxis.js",[a["Core/Axis/Axis.js"],a["Core/Chart/Chart.js"],a["Core/Color/Color.js"],a["Mixins/ColorSeries.js"],a["Core/Animation/Fx.js"],a["Core/Globals.js"],a["Core/Legend.js"],a["Mixins/LegendSymbol.js"],a["Series/LineSeries.js"],a["Core/Series/Point.js"],a["Core/Utilities.js"]],function(a,n,h,q,c,r,D,y,A,m,k){var p=this&&this.__extends||function(){var b=function(f,l){b=
Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(l,f){l.__proto__=f}||function(l,f){for(var b in f)f.hasOwnProperty(b)&&(l[b]=f[b])};return b(f,l)};return function(f,l){function e(){this.constructor=f}b(f,l);f.prototype=null===l?Object.create(l):(e.prototype=l.prototype,new e)}}(),u=h.parse;h=q.colorPointMixin;q=q.colorSeriesMixin;var E=r.noop,x=k.addEvent,C=k.erase,g=k.extend,e=k.isNumber,b=k.merge,t=k.pick,d=k.splat;"";g(A.prototype,q);g(m.prototype,h);n.prototype.collectionsWithUpdate.push("colorAxis");
n.prototype.collectionsWithInit.colorAxis=[n.prototype.addColorAxis];var z=function(d){function f(l,f){var b=d.call(this,l,f)||this;b.beforePadding=!1;b.chart=void 0;b.coll="colorAxis";b.dataClasses=void 0;b.legendItem=void 0;b.legendItems=void 0;b.name="";b.options=void 0;b.stops=void 0;b.visible=!0;b.init(l,f);return b}p(f,d);f.buildOptions=function(l,f,e){l=l.options.legend||{};var d=e.layout?"vertical"!==e.layout:"vertical"!==l.layout;return b(f,{side:d?2:1,reversed:!d},e,{opposite:!d,showEmpty:!1,
title:null,visible:l.enabled&&(e?!1!==e.visible:!0)})};f.prototype.init=function(l,b){var e=f.buildOptions(l,f.defaultOptions,b);this.coll="colorAxis";d.prototype.init.call(this,l,e);b.dataClasses&&this.initDataClasses(b);this.initStops();this.horiz=!e.opposite;this.zoomEnabled=!1};f.prototype.initDataClasses=function(l){var f=this.chart,e,d=0,t=f.options.chart.colorCount,g=this.options,a=l.dataClasses.length;this.dataClasses=e=[];this.legendItems=[];l.dataClasses.forEach(function(l,p){l=b(l);e.push(l);
if(f.styledMode||!l.color)"category"===g.dataClassColor?(f.styledMode||(p=f.options.colors,t=p.length,l.color=p[d]),l.colorIndex=d,d++,d===t&&(d=0)):l.color=u(g.minColor).tweenTo(u(g.maxColor),2>a?.5:p/(a-1))})};f.prototype.hasData=function(){return!!(this.tickPositions||[]).length};f.prototype.setTickPositions=function(){if(!this.dataClasses)return d.prototype.setTickPositions.call(this)};f.prototype.initStops=function(){this.stops=this.options.stops||[[0,this.options.minColor],[1,this.options.maxColor]];
this.stops.forEach(function(l){l.color=u(l[1])})};f.prototype.setOptions=function(l){d.prototype.setOptions.call(this,l);this.options.crosshair=this.options.marker};f.prototype.setAxisSize=function(){var l=this.legendSymbol,b=this.chart,e=b.options.legend||{},d,t;l?(this.left=e=l.attr("x"),this.top=d=l.attr("y"),this.width=t=l.attr("width"),this.height=l=l.attr("height"),this.right=b.chartWidth-e-t,this.bottom=b.chartHeight-d-l,this.len=this.horiz?t:l,this.pos=this.horiz?e:d):this.len=(this.horiz?
e.symbolWidth:e.symbolHeight)||f.defaultLegendLength};f.prototype.normalizedValue=function(l){this.logarithmic&&(l=this.logarithmic.log2lin(l));return 1-(this.max-l)/(this.max-this.min||1)};f.prototype.toColor=function(l,f){var b=this.dataClasses,e=this.stops,d;if(b)for(d=b.length;d--;){var t=b[d];var g=t.from;e=t.to;if(("undefined"===typeof g||l>=g)&&("undefined"===typeof e||l<=e)){var a=t.color;f&&(f.dataClass=d,f.colorIndex=t.colorIndex);break}}else{l=this.normalizedValue(l);for(d=e.length;d--&&
!(l>e[d][0]););g=e[d]||e[d+1];e=e[d+1]||g;l=1-(e[0]-l)/(e[0]-g[0]||1);a=g.color.tweenTo(e.color,l)}return a};f.prototype.getOffset=function(){var l=this.legendGroup,f=this.chart.axisOffset[this.side];l&&(this.axisParent=l,d.prototype.getOffset.call(this),this.added||(this.added=!0,this.labelLeft=0,this.labelRight=this.width),this.chart.axisOffset[this.side]=f)};f.prototype.setLegendColor=function(){var l=this.reversed,f=l?1:0;l=l?0:1;f=this.horiz?[f,0,l,0]:[0,l,0,f];this.legendColor={linearGradient:{x1:f[0],
y1:f[1],x2:f[2],y2:f[3]},stops:this.stops}};f.prototype.drawLegendSymbol=function(l,b){var e=l.padding,d=l.options,g=this.horiz,a=t(d.symbolWidth,g?f.defaultLegendLength:12),p=t(d.symbolHeight,g?12:f.defaultLegendLength),c=t(d.labelPadding,g?16:30);d=t(d.itemDistance,10);this.setLegendColor();b.legendSymbol=this.chart.renderer.rect(0,l.baseline-11,a,p).attr({zIndex:1}).add(b.legendGroup);this.legendItemWidth=a+e+(g?d:c);this.legendItemHeight=p+e+(g?c:0)};f.prototype.setState=function(f){this.series.forEach(function(l){l.setState(f)})};
f.prototype.setVisible=function(){};f.prototype.getSeriesExtremes=function(){var f=this.series,b=f.length,e;this.dataMin=Infinity;for(this.dataMax=-Infinity;b--;){var d=f[b];var g=d.colorKey=t(d.options.colorKey,d.colorKey,d.pointValKey,d.zoneAxis,"y");var a=d.pointArrayMap;var p=d[g+"Min"]&&d[g+"Max"];if(d[g+"Data"])var c=d[g+"Data"];else if(a){c=[];a=a.indexOf(g);var k=d.yData;if(0<=a&&k)for(e=0;e<k.length;e++)c.push(t(k[e][a],k[e]))}else c=d.yData;p?(d.minColorValue=d[g+"Min"],d.maxColorValue=
d[g+"Max"]):(c=A.prototype.getExtremes.call(d,c),d.minColorValue=c.dataMin,d.maxColorValue=c.dataMax);"undefined"!==typeof d.minColorValue&&(this.dataMin=Math.min(this.dataMin,d.minColorValue),this.dataMax=Math.max(this.dataMax,d.maxColorValue));p||A.prototype.applyExtremes.call(d)}};f.prototype.drawCrosshair=function(f,b){var l=b&&b.plotX,e=b&&b.plotY,g=this.pos,t=this.len;if(b){var a=this.toPixels(b.getNestedProperty(b.series.colorKey));a<g?a=g-2:a>g+t&&(a=g+t+2);b.plotX=a;b.plotY=this.len-a;d.prototype.drawCrosshair.call(this,
f,b);b.plotX=l;b.plotY=e;this.cross&&!this.cross.addedToColorAxis&&this.legendGroup&&(this.cross.addClass("highcharts-coloraxis-marker").add(this.legendGroup),this.cross.addedToColorAxis=!0,!this.chart.styledMode&&this.crosshair&&this.cross.attr({fill:this.crosshair.color}))}};f.prototype.getPlotLinePath=function(f){var l=this.left,b=f.translatedValue,g=this.top;return e(b)?this.horiz?[["M",b-4,g-6],["L",b+4,g-6],["L",b,g],["Z"]]:[["M",l,b],["L",l-6,b+6],["L",l-6,b-6],["Z"]]:d.prototype.getPlotLinePath.call(this,
f)};f.prototype.update=function(l,e){var g=this.chart,t=g.legend,a=f.buildOptions(g,{},l);this.series.forEach(function(f){f.isDirtyData=!0});(l.dataClasses&&t.allItems||this.dataClasses)&&this.destroyItems();g.options[this.coll]=b(this.userOptions,a);d.prototype.update.call(this,a,e);this.legendItem&&(this.setLegendColor(),t.colorizeItem(this,!0))};f.prototype.destroyItems=function(){var f=this.chart;this.legendItem?f.legend.destroyItem(this):this.legendItems&&this.legendItems.forEach(function(l){f.legend.destroyItem(l)});
f.isDirtyLegend=!0};f.prototype.remove=function(f){this.destroyItems();d.prototype.remove.call(this,f)};f.prototype.getDataClassLegendSymbols=function(){var f=this,b=f.chart,d=f.legendItems,e=b.options.legend,t=e.valueDecimals,a=e.valueSuffix||"",p;d.length||f.dataClasses.forEach(function(l,e){var c=!0,k=l.from,v=l.to,z=b.numberFormatter;p="";"undefined"===typeof k?p="< ":"undefined"===typeof v&&(p="> ");"undefined"!==typeof k&&(p+=z(k,t)+a);"undefined"!==typeof k&&"undefined"!==typeof v&&(p+=" - ");
"undefined"!==typeof v&&(p+=z(v,t)+a);d.push(g({chart:b,name:p,options:{},drawLegendSymbol:y.drawRectangle,visible:!0,setState:E,isDataClass:!0,setVisible:function(){c=f.visible=!c;f.series.forEach(function(f){f.points.forEach(function(f){f.dataClass===e&&f.setVisible(c)})});b.legend.colorizeItem(this,c)}},l))});return d};f.defaultLegendLength=200;f.defaultOptions={lineWidth:0,minPadding:0,maxPadding:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},
width:.01,color:"#999999"},labels:{overflow:"justify",rotation:0},minColor:"#e6ebf5",maxColor:"#003399",tickLength:5,showInLegend:!0};f.keepProps=["legendGroup","legendItemHeight","legendItemWidth","legendItem","legendSymbol"];return f}(a);Array.prototype.push.apply(a.keepProps,z.keepProps);r.ColorAxis=z;["fill","stroke"].forEach(function(b){c.prototype[b+"Setter"]=function(){this.elem.attr(b,u(this.start).tweenTo(u(this.end),this.pos),null,!0)}});x(n,"afterGetAxes",function(){var b=this,f=b.options;
this.colorAxis=[];f.colorAxis&&(f.colorAxis=d(f.colorAxis),f.colorAxis.forEach(function(f,d){f.index=d;new z(b,f)}))});x(A,"bindAxes",function(){var b=this.axisTypes;b?-1===b.indexOf("colorAxis")&&b.push("colorAxis"):this.axisTypes=["colorAxis"]});x(D,"afterGetAllItems",function(b){var f=[],l,d;(this.chart.colorAxis||[]).forEach(function(d){(l=d.options)&&l.showInLegend&&(l.dataClasses&&l.visible?f=f.concat(d.getDataClassLegendSymbols()):l.visible&&f.push(d),d.series.forEach(function(f){if(!f.options.showInLegend||
l.dataClasses)"point"===f.options.legendType?f.points.forEach(function(f){C(b.allItems,f)}):C(b.allItems,f)}))});for(d=f.length;d--;)b.allItems.unshift(f[d])});x(D,"afterColorizeItem",function(b){b.visible&&b.item.legendColor&&b.item.legendSymbol.attr({fill:b.item.legendColor})});x(D,"afterUpdate",function(){var b=this.chart.colorAxis;b&&b.forEach(function(f,b,d){f.update({},d)})});x(A,"afterTranslate",function(){(this.chart.colorAxis&&this.chart.colorAxis.length||this.colorAttribs)&&this.translateColors()});
return z});B(a,"Mixins/ColorMapSeries.js",[a["Core/Globals.js"],a["Core/Series/Point.js"],a["Core/Utilities.js"]],function(a,n,h){var q=h.defined;return{colorMapPointMixin:{dataLabelOnNull:!0,isValid:function(){return null!==this.value&&Infinity!==this.value&&-Infinity!==this.value},setState:function(a){n.prototype.setState.call(this,a);this.graphic&&this.graphic.attr({zIndex:"hover"===a?1:0})}},colorMapSeriesMixin:{pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],trackerGroups:["group",
"markerGroup","dataLabelsGroup"],getSymbol:a.noop,parallelArrays:["x","y","value"],colorKey:"value",pointAttribs:a.seriesTypes.column.prototype.pointAttribs,colorAttribs:function(a){var c={};q(a.color)&&(c[this.colorProp||"fill"]=a.color);return c}}}});B(a,"Maps/MapNavigation.js",[a["Core/Chart/Chart.js"],a["Core/Globals.js"],a["Core/Utilities.js"]],function(a,n,h){function q(a){a&&(a.preventDefault&&a.preventDefault(),a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)}function c(a){this.init(a)}
var r=n.doc,D=h.addEvent,y=h.extend,w=h.merge,m=h.objectEach,k=h.pick;c.prototype.init=function(a){this.chart=a;a.mapNavButtons=[]};c.prototype.update=function(a){var p=this.chart,c=p.options.mapNavigation,r,h,g,e,b,t=function(b){this.handler.call(p,b);q(b)},d=p.mapNavButtons;a&&(c=p.options.mapNavigation=w(p.options.mapNavigation,a));for(;d.length;)d.pop().destroy();k(c.enableButtons,c.enabled)&&!p.renderer.forExport&&m(c.buttons,function(a,k){r=w(c.buttonOptions,a);p.styledMode||(h=r.theme,h.style=
w(r.theme.style,r.style),e=(g=h.states)&&g.hover,b=g&&g.select);a=p.renderer.button(r.text,0,0,t,h,e,b,0,"zoomIn"===k?"topbutton":"bottombutton").addClass("highcharts-map-navigation highcharts-"+{zoomIn:"zoom-in",zoomOut:"zoom-out"}[k]).attr({width:r.width,height:r.height,title:p.options.lang[k],padding:r.padding,zIndex:5}).add();a.handler=r.onclick;D(a.element,"dblclick",q);d.push(a);var f=r,l=D(p,"load",function(){a.align(y(f,{width:a.width,height:2*a.height}),null,f.alignTo);l()})});this.updateEvents(c)};
c.prototype.updateEvents=function(a){var c=this.chart;k(a.enableDoubleClickZoom,a.enabled)||a.enableDoubleClickZoomTo?this.unbindDblClick=this.unbindDblClick||D(c.container,"dblclick",function(a){c.pointer.onContainerDblClick(a)}):this.unbindDblClick&&(this.unbindDblClick=this.unbindDblClick());k(a.enableMouseWheelZoom,a.enabled)?this.unbindMouseWheel=this.unbindMouseWheel||D(c.container,"undefined"===typeof r.onmousewheel?"DOMMouseScroll":"mousewheel",function(a){c.pointer.onContainerMouseWheel(a);
q(a);return!1}):this.unbindMouseWheel&&(this.unbindMouseWheel=this.unbindMouseWheel())};y(a.prototype,{fitToBox:function(a,c){[["x","width"],["y","height"]].forEach(function(p){var k=p[0];p=p[1];a[k]+a[p]>c[k]+c[p]&&(a[p]>c[p]?(a[p]=c[p],a[k]=c[k]):a[k]=c[k]+c[p]-a[p]);a[p]>c[p]&&(a[p]=c[p]);a[k]<c[k]&&(a[k]=c[k])});return a},mapZoom:function(a,c,r,m,h){var g=this.xAxis[0],e=g.max-g.min,b=k(c,g.min+e/2),t=e*a;e=this.yAxis[0];var d=e.max-e.min,p=k(r,e.min+d/2);d*=a;b=this.fitToBox({x:b-t*(m?(m-g.pos)/
g.len:.5),y:p-d*(h?(h-e.pos)/e.len:.5),width:t,height:d},{x:g.dataMin,y:e.dataMin,width:g.dataMax-g.dataMin,height:e.dataMax-e.dataMin});t=b.x<=g.dataMin&&b.width>=g.dataMax-g.dataMin&&b.y<=e.dataMin&&b.height>=e.dataMax-e.dataMin;m&&g.mapAxis&&(g.mapAxis.fixTo=[m-g.pos,c]);h&&e.mapAxis&&(e.mapAxis.fixTo=[h-e.pos,r]);"undefined"===typeof a||t?(g.setExtremes(void 0,void 0,!1),e.setExtremes(void 0,void 0,!1)):(g.setExtremes(b.x,b.x+b.width,!1),e.setExtremes(b.y,b.y+b.height,!1));this.redraw()}});D(a,
"beforeRender",function(){this.mapNavigation=new c(this);this.mapNavigation.update()});n.MapNavigation=c});B(a,"Maps/MapPointer.js",[a["Core/Pointer.js"],a["Core/Utilities.js"]],function(a,n){var h=n.extend,q=n.pick;n=n.wrap;h(a.prototype,{onContainerDblClick:function(a){var c=this.chart;a=this.normalize(a);c.options.mapNavigation.enableDoubleClickZoomTo?c.pointer.inClass(a.target,"highcharts-tracker")&&c.hoverPoint&&c.hoverPoint.zoomTo():c.isInsidePlot(a.chartX-c.plotLeft,a.chartY-c.plotTop)&&c.mapZoom(.5,
c.xAxis[0].toValue(a.chartX),c.yAxis[0].toValue(a.chartY),a.chartX,a.chartY)},onContainerMouseWheel:function(a){var c=this.chart;a=this.normalize(a);var h=a.detail||-(a.wheelDelta/120);c.isInsidePlot(a.chartX-c.plotLeft,a.chartY-c.plotTop)&&c.mapZoom(Math.pow(c.options.mapNavigation.mouseWheelSensitivity,h),c.xAxis[0].toValue(a.chartX),c.yAxis[0].toValue(a.chartY),a.chartX,a.chartY)}});n(a.prototype,"zoomOption",function(a){var c=this.chart.options.mapNavigation;q(c.enableTouchZoom,c.enabled)&&(this.chart.options.chart.pinchType=
"xy");a.apply(this,[].slice.call(arguments,1))});n(a.prototype,"pinchTranslate",function(a,h,n,q,w,m,k){a.call(this,h,n,q,w,m,k);"map"===this.chart.options.chart.type&&this.hasZoom&&(a=q.scaleX>q.scaleY,this.pinchTranslateDirection(!a,h,n,q,w,m,k,a?q.scaleX:q.scaleY))})});B(a,"Maps/Map.js",[a["Core/Chart/Chart.js"],a["Core/Globals.js"],a["Core/Options.js"],a["Core/Renderer/SVG/SVGRenderer.js"],a["Core/Utilities.js"]],function(a,n,h,q,c){function r(a,c,k,m,h,g,e,b){return[["M",a+h,c],["L",a+k-g,c],
["C",a+k-g/2,c,a+k,c+g/2,a+k,c+g],["L",a+k,c+m-e],["C",a+k,c+m-e/2,a+k-e/2,c+m,a+k-e,c+m],["L",a+b,c+m],["C",a+b/2,c+m,a,c+m-b/2,a,c+m-b],["L",a,c+h],["C",a,c+h/2,a+h/2,c,a+h,c],["Z"]]}h=h.defaultOptions;var D=c.extend,w=c.getOptions,A=c.merge,m=c.pick;c=n.Renderer;var k=n.VMLRenderer;D(h.lang,{zoomIn:"Zoom in",zoomOut:"Zoom out"});h.mapNavigation={buttonOptions:{alignTo:"plotBox",align:"left",verticalAlign:"top",x:0,width:18,height:18,padding:5,style:{fontSize:"15px",fontWeight:"bold"},theme:{"stroke-width":1,
"text-align":"center"}},buttons:{zoomIn:{onclick:function(){this.mapZoom(.5)},text:"+",y:0},zoomOut:{onclick:function(){this.mapZoom(2)},text:"-",y:28}},mouseWheelSensitivity:1.1};h=n.splitPath=function(a){"string"===typeof a&&(a=a.replace(/([A-Za-z])/g," $1 ").replace(/^\s*/,"").replace(/\s*$/,""),a=a.split(/[ ,;]+/).map(function(a){return/[A-za-z]/.test(a)?a:parseFloat(a)}));return q.prototype.pathToSegments(a)};n.maps={};q.prototype.symbols.topbutton=function(a,c,k,m,h){h=h&&h.r||0;return r(a-
1,c-1,k,m,h,h,0,0)};q.prototype.symbols.bottombutton=function(a,c,k,m,h){h=h&&h.r||0;return r(a-1,c-1,k,m,0,0,h,h)};c===k&&["topbutton","bottombutton"].forEach(function(a){k.prototype.symbols[a]=q.prototype.symbols[a]});return{mapChart:n.Map=n.mapChart=function(c,k,h){var p="string"===typeof c||c.nodeName,n=arguments[p?1:0],g=n,e={endOnTick:!1,visible:!1,minPadding:0,maxPadding:0,startOnTick:!1},b=w().credits;var t=n.series;n.series=null;n=A({chart:{panning:{enabled:!0,type:"xy"},type:"map"},credits:{mapText:m(b.mapText,
' \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>'),mapTextFull:m(b.mapTextFull,"{geojson.copyright}")},tooltip:{followTouchMove:!1},xAxis:e,yAxis:A(e,{reversed:!0})},n,{chart:{inverted:!1,alignTicks:!1}});n.series=g.series=t;return p?new a(c,n,h):new a(n,k)},maps:n.maps,splitPath:h}});B(a,"Series/MapSeries.js",[a["Core/Series/Series.js"],a["Mixins/ColorMapSeries.js"],a["Core/Globals.js"],a["Mixins/LegendSymbol.js"],a["Maps/Map.js"],a["Core/Series/Point.js"],a["Core/Renderer/SVG/SVGRenderer.js"],
a["Core/Utilities.js"]],function(a,n,h,q,c,r,D,y){var w=n.colorMapPointMixin,m=h.noop,k=c.maps,p=c.splitPath,u=y.extend,E=y.fireEvent,x=y.getNestedProperty,C=y.isArray,g=y.isNumber,e=y.merge,b=y.objectEach,t=y.pick,d=y.splat,z=h.Series,v=a.seriesTypes;a.seriesType("map","scatter",{animation:!1,dataLabels:{crop:!1,formatter:function(){return this.point.value},inside:!0,overflow:!1,padding:0,verticalAlign:"middle"},marker:null,nullColor:"#f7f7f7",stickyTracking:!1,tooltip:{followPointer:!0,pointFormat:"{point.name}: {point.value}<br/>"},
turboThreshold:0,allAreas:!0,borderColor:"#cccccc",borderWidth:1,joinBy:"hc-key",states:{hover:{halo:null,brightness:.2},normal:{animation:!0},select:{color:"#cccccc"},inactive:{opacity:1}}},e(n.colorMapSeriesMixin,{type:"map",getExtremesFromAll:!0,useMapGeometry:!0,forceDL:!0,searchPoint:m,directTouch:!0,preserveAspectRatio:!0,pointArrayMap:["value"],setOptions:function(a){a=z.prototype.setOptions.call(this,a);var f=a.joinBy;null===f&&(f="_i");f=this.joinBy=d(f);f[1]||(f[1]=f[0]);return a},getBox:function(a){var f=
Number.MAX_VALUE,b=-f,d=f,e=-f,g=f,c=f,k=this.xAxis,h=this.yAxis,z;(a||[]).forEach(function(a){if(a.path){"string"===typeof a.path?a.path=p(a.path):"M"===a.path[0]&&(a.path=D.prototype.pathToSegments(a.path));var l=a.path||[],k=-f,h=f,m=-f,v=f,n=a.properties;a._foundBox||(l.forEach(function(a){var f=a[a.length-2];a=a[a.length-1];"number"===typeof f&&"number"===typeof a&&(h=Math.min(h,f),k=Math.max(k,f),v=Math.min(v,a),m=Math.max(m,a))}),a._midX=h+(k-h)*t(a.middleX,n&&n["hc-middle-x"],.5),a._midY=
v+(m-v)*t(a.middleY,n&&n["hc-middle-y"],.5),a._maxX=k,a._minX=h,a._maxY=m,a._minY=v,a.labelrank=t(a.labelrank,(k-h)*(m-v)),a._foundBox=!0);b=Math.max(b,a._maxX);d=Math.min(d,a._minX);e=Math.max(e,a._maxY);g=Math.min(g,a._minY);c=Math.min(a._maxX-a._minX,a._maxY-a._minY,c);z=!0}});z&&(this.minY=Math.min(g,t(this.minY,f)),this.maxY=Math.max(e,t(this.maxY,-f)),this.minX=Math.min(d,t(this.minX,f)),this.maxX=Math.max(b,t(this.maxX,-f)),k&&"undefined"===typeof k.options.minRange&&(k.minRange=Math.min(5*
c,(this.maxX-this.minX)/5,k.minRange||f)),h&&"undefined"===typeof h.options.minRange&&(h.minRange=Math.min(5*c,(this.maxY-this.minY)/5,h.minRange||f)))},hasData:function(){return!!this.processedXData.length},getExtremes:function(){var a=z.prototype.getExtremes.call(this,this.valueData),b=a.dataMin;a=a.dataMax;this.chart.hasRendered&&this.isDirtyData&&this.getBox(this.options.data);g(b)&&(this.valueMin=b);g(a)&&(this.valueMax=a);return{dataMin:this.minY,dataMax:this.maxY}},translatePath:function(a){var f=
this.xAxis,b=this.yAxis,d=f.min,e=f.transA,g=f.minPixelPadding,c=b.min,t=b.transA,k=b.minPixelPadding,h=[];a&&a.forEach(function(a){"M"===a[0]?h.push(["M",(a[1]-(d||0))*e+g,(a[2]-(c||0))*t+k]):"L"===a[0]?h.push(["L",(a[1]-(d||0))*e+g,(a[2]-(c||0))*t+k]):"C"===a[0]?h.push(["C",(a[1]-(d||0))*e+g,(a[2]-(c||0))*t+k,(a[3]-(d||0))*e+g,(a[4]-(c||0))*t+k,(a[5]-(d||0))*e+g,(a[6]-(c||0))*t+k]):"Q"===a[0]?h.push(["Q",(a[1]-(d||0))*e+g,(a[2]-(c||0))*t+k,(a[3]-(d||0))*e+g,(a[4]-(c||0))*t+k]):"Z"===a[0]&&h.push(["Z"])});
return h},setData:function(a,d,c,t){var f=this.options,l=this.chart.options.chart,p=l&&l.map,m=f.mapData,v=this.joinBy,n=f.keys||this.pointArrayMap,F=[],q={},u=this.chart.mapTransforms;!m&&p&&(m="string"===typeof p?k[p]:p);a&&a.forEach(function(b,d){var e=0;if(g(b))a[d]={value:b};else if(C(b)){a[d]={};!f.keys&&b.length>n.length&&"string"===typeof b[0]&&(a[d]["hc-key"]=b[0],++e);for(var l=0;l<n.length;++l,++e)n[l]&&"undefined"!==typeof b[e]&&(0<n[l].indexOf(".")?r.prototype.setNestedProperty(a[d],
b[e],n[l]):a[d][n[l]]=b[e])}v&&"_i"===v[0]&&(a[d]._i=d)});this.getBox(a);(this.chart.mapTransforms=u=l&&l.mapTransforms||m&&m["hc-transform"]||u)&&b(u,function(a){a.rotation&&(a.cosAngle=Math.cos(a.rotation),a.sinAngle=Math.sin(a.rotation))});if(m){"FeatureCollection"===m.type&&(this.mapTitle=m.title,m=h.geojson(m,this.type,this));this.mapData=m;this.mapMap={};for(u=0;u<m.length;u++)l=m[u],p=l.properties,l._i=u,v[0]&&p&&p[v[0]]&&(l[v[0]]=p[v[0]]),q[l[v[0]]]=l;this.mapMap=q;if(a&&v[1]){var G=v[1];
a.forEach(function(a){a=x(G,a);q[a]&&F.push(q[a])})}if(f.allAreas){this.getBox(m);a=a||[];if(v[1]){var w=v[1];a.forEach(function(a){F.push(x(w,a))})}F="|"+F.map(function(a){return a&&a[v[0]]}).join("|")+"|";m.forEach(function(f){v[0]&&-1!==F.indexOf("|"+f[v[0]]+"|")||(a.push(e(f,{value:null})),t=!1)})}else this.getBox(F)}z.prototype.setData.call(this,a,d,c,t)},drawGraph:m,drawDataLabels:m,doFullTranslate:function(){return this.isDirtyData||this.chart.isResizing||this.chart.renderer.isVML||!this.baseTrans},
translate:function(){var a=this,b=a.xAxis,d=a.yAxis,e=a.doFullTranslate();a.generatePoints();a.data.forEach(function(f){g(f._midX)&&g(f._midY)&&(f.plotX=b.toPixels(f._midX,!0),f.plotY=d.toPixels(f._midY,!0));e&&(f.shapeType="path",f.shapeArgs={d:a.translatePath(f.path)})});E(a,"afterTranslate")},pointAttribs:function(a,b){b=a.series.chart.styledMode?this.colorAttribs(a):v.column.prototype.pointAttribs.call(this,a,b);b["stroke-width"]=t(a.options[this.pointAttrToOptions&&this.pointAttrToOptions["stroke-width"]||
"borderWidth"],"inherit");return b},drawPoints:function(){var a=this,b=a.xAxis,d=a.yAxis,e=a.group,g=a.chart,c=g.renderer,k=this.baseTrans;a.transformGroup||(a.transformGroup=c.g().attr({scaleX:1,scaleY:1}).add(e),a.transformGroup.survive=!0);if(a.doFullTranslate())g.hasRendered&&!g.styledMode&&a.points.forEach(function(b){b.shapeArgs&&(b.shapeArgs.fill=a.pointAttribs(b,b.state).fill)}),a.group=a.transformGroup,v.column.prototype.drawPoints.apply(a),a.group=e,a.points.forEach(function(b){if(b.graphic){var f=
"";b.name&&(f+="highcharts-name-"+b.name.replace(/ /g,"-").toLowerCase());b.properties&&b.properties["hc-key"]&&(f+=" highcharts-key-"+b.properties["hc-key"].toLowerCase());f&&b.graphic.addClass(f);g.styledMode&&b.graphic.css(a.pointAttribs(b,b.selected&&"select"||void 0))}}),this.baseTrans={originX:b.min-b.minPixelPadding/b.transA,originY:d.min-d.minPixelPadding/d.transA+(d.reversed?0:d.len/d.transA),transAX:b.transA,transAY:d.transA},this.transformGroup.animate({translateX:0,translateY:0,scaleX:1,
scaleY:1});else{var h=b.transA/k.transAX;var m=d.transA/k.transAY;var p=b.toPixels(k.originX,!0);var z=d.toPixels(k.originY,!0);.99<h&&1.01>h&&.99<m&&1.01>m&&(m=h=1,p=Math.round(p),z=Math.round(z));var n=this.transformGroup;if(g.renderer.globalAnimation){var r=n.attr("translateX");var q=n.attr("translateY");var u=n.attr("scaleX");var w=n.attr("scaleY");n.attr({animator:0}).animate({animator:1},{step:function(a,b){n.attr({translateX:r+(p-r)*b.pos,translateY:q+(z-q)*b.pos,scaleX:u+(h-u)*b.pos,scaleY:w+
(m-w)*b.pos})}})}else n.attr({translateX:p,translateY:z,scaleX:h,scaleY:m})}g.styledMode||e.element.setAttribute("stroke-width",t(a.options[a.pointAttrToOptions&&a.pointAttrToOptions["stroke-width"]||"borderWidth"],1)/(h||1));this.drawMapDataLabels()},drawMapDataLabels:function(){z.prototype.drawDataLabels.call(this);this.dataLabelsGroup&&this.dataLabelsGroup.clip(this.chart.clipRect)},render:function(){var a=this,b=z.prototype.render;a.chart.renderer.isVML&&3E3<a.data.length?setTimeout(function(){b.call(a)}):
b.call(a)},animate:function(a){var b=this.options.animation,d=this.group,f=this.xAxis,e=this.yAxis,g=f.pos,c=e.pos;this.chart.renderer.isSVG&&(!0===b&&(b={duration:1E3}),a?d.attr({translateX:g+f.len/2,translateY:c+e.len/2,scaleX:.001,scaleY:.001}):d.animate({translateX:g,translateY:c,scaleX:1,scaleY:1},b))},animateDrilldown:function(a){var b=this.chart.plotBox,d=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],f=d.bBox,e=this.chart.options.drilldown.animation;a||(a=Math.min(f.width/
b.width,f.height/b.height),d.shapeArgs={scaleX:a,scaleY:a,translateX:f.x,translateY:f.y},this.points.forEach(function(a){a.graphic&&a.graphic.attr(d.shapeArgs).animate({scaleX:1,scaleY:1,translateX:0,translateY:0},e)}))},drawLegendSymbol:q.drawRectangle,animateDrillupFrom:function(a){v.column.prototype.animateDrillupFrom.call(this,a)},animateDrillupTo:function(a){v.column.prototype.animateDrillupTo.call(this,a)}}),u({applyOptions:function(a,b){var d=this.series;a=r.prototype.applyOptions.call(this,
a,b);b=d.joinBy;d.mapData&&d.mapMap&&(b=r.prototype.getNestedProperty.call(a,b[1]),(b="undefined"!==typeof b&&d.mapMap[b])?(d.xyFromShape&&(a.x=b._midX,a.y=b._midY),u(a,b)):a.value=a.value||null);return a},onMouseOver:function(a){y.clearTimeout(this.colorInterval);if(null!==this.value||this.series.options.nullInteraction)r.prototype.onMouseOver.call(this,a);else this.series.onMouseOut(a)},zoomTo:function(){var a=this.series;a.xAxis.setExtremes(this._minX,this._maxX,!1);a.yAxis.setExtremes(this._minY,
this._maxY,!1);a.chart.redraw()}},w));""});B(a,"Series/MapLineSeries.js",[a["Core/Series/Series.js"]],function(a){var n=a.seriesTypes;a.seriesType("mapline","map",{lineWidth:1,fillColor:"none"},{type:"mapline",colorProp:"stroke",pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},pointAttribs:function(a,q){a=n.map.prototype.pointAttribs.call(this,a,q);a.fill=this.options.fillColor;return a},drawLegendSymbol:n.line.prototype.drawLegendSymbol});""});B(a,"Series/MapPointSeries.js",[a["Core/Series/Series.js"],
a["Core/Globals.js"],a["Core/Series/Point.js"],a["Core/Utilities.js"]],function(a,n,h,q){var c=q.merge,r=n.Series;a.seriesType("mappoint","scatter",{dataLabels:{crop:!1,defer:!1,enabled:!0,formatter:function(){return this.point.name},overflow:!1,style:{color:"#000000"}}},{type:"mappoint",forceDL:!0,drawDataLabels:function(){r.prototype.drawDataLabels.call(this);this.dataLabelsGroup&&this.dataLabelsGroup.clip(this.chart.clipRect)}},{applyOptions:function(a,n){a="undefined"!==typeof a.lat&&"undefined"!==
typeof a.lon?c(a,this.series.chart.fromLatLonToPoint(a)):a;return h.prototype.applyOptions.call(this,a,n)}});""});B(a,"Series/Bubble/BubbleLegend.js",[a["Core/Chart/Chart.js"],a["Core/Color/Color.js"],a["Core/Globals.js"],a["Core/Legend.js"],a["Core/Utilities.js"]],function(a,n,h,q,c){var r=n.parse;n=c.addEvent;var w=c.arrayMax,y=c.arrayMin,A=c.isNumber,m=c.merge,k=c.objectEach,p=c.pick,u=c.setOptions,E=c.stableSort,x=c.wrap;"";var C=h.Series,g=h.noop;u({legend:{bubbleLegend:{borderColor:void 0,borderWidth:2,
className:void 0,color:void 0,connectorClassName:void 0,connectorColor:void 0,connectorDistance:60,connectorWidth:1,enabled:!1,labels:{className:void 0,allowOverlap:!1,format:"",formatter:void 0,align:"right",style:{fontSize:10,color:void 0},x:0,y:0},maxSize:60,minSize:10,legendIndex:0,ranges:{value:void 0,borderColor:void 0,color:void 0,connectorColor:void 0},sizeBy:"area",sizeByAbsoluteValue:!1,zIndex:1,zThreshold:0}}});u=function(){function a(a,e){this.options=this.symbols=this.visible=this.ranges=
this.movementX=this.maxLabel=this.legendSymbol=this.legendItemWidth=this.legendItemHeight=this.legendItem=this.legendGroup=this.legend=this.fontMetrics=this.chart=void 0;this.setState=g;this.init(a,e)}a.prototype.init=function(a,e){this.options=a;this.visible=!0;this.chart=e.chart;this.legend=e};a.prototype.addToLegend=function(a){a.splice(this.options.legendIndex,0,this)};a.prototype.drawLegendSymbol=function(a){var b=this.chart,d=this.options,e=p(a.options.itemDistance,20),g=d.ranges;var f=d.connectorDistance;
this.fontMetrics=b.renderer.fontMetrics(d.labels.style.fontSize.toString()+"px");g&&g.length&&A(g[0].value)?(E(g,function(a,b){return b.value-a.value}),this.ranges=g,this.setOptions(),this.render(),b=this.getMaxLabelSize(),g=this.ranges[0].radius,a=2*g,f=f-g+b.width,f=0<f?f:0,this.maxLabel=b,this.movementX="left"===d.labels.align?f:0,this.legendItemWidth=a+f+e,this.legendItemHeight=a+this.fontMetrics.h/2):a.options.bubbleLegend.autoRanges=!0};a.prototype.setOptions=function(){var a=this.ranges,e=
this.options,d=this.chart.series[e.seriesIndex],g=this.legend.baseline,c={"z-index":e.zIndex,"stroke-width":e.borderWidth},f={"z-index":e.zIndex,"stroke-width":e.connectorWidth},l=this.getLabelStyles(),k=d.options.marker.fillOpacity,h=this.chart.styledMode;a.forEach(function(b,t){h||(c.stroke=p(b.borderColor,e.borderColor,d.color),c.fill=p(b.color,e.color,1!==k?r(d.color).setOpacity(k).get("rgba"):d.color),f.stroke=p(b.connectorColor,e.connectorColor,d.color));a[t].radius=this.getRangeRadius(b.value);
a[t]=m(a[t],{center:a[0].radius-a[t].radius+g});h||m(!0,a[t],{bubbleStyle:m(!1,c),connectorStyle:m(!1,f),labelStyle:l})},this)};a.prototype.getLabelStyles=function(){var a=this.options,e={},d="left"===a.labels.align,g=this.legend.options.rtl;k(a.labels.style,function(a,b){"color"!==b&&"fontSize"!==b&&"z-index"!==b&&(e[b]=a)});return m(!1,e,{"font-size":a.labels.style.fontSize,fill:p(a.labels.style.color,"#000000"),"z-index":a.zIndex,align:g||d?"right":"left"})};a.prototype.getRangeRadius=function(a){var b=
this.options;return this.chart.series[this.options.seriesIndex].getRadius.call(this,b.ranges[b.ranges.length-1].value,b.ranges[0].value,b.minSize,b.maxSize,a)};a.prototype.render=function(){var a=this.chart.renderer,e=this.options.zThreshold;this.symbols||(this.symbols={connectors:[],bubbleItems:[],labels:[]});this.legendSymbol=a.g("bubble-legend");this.legendItem=a.g("bubble-legend-item");this.legendSymbol.translateX=0;this.legendSymbol.translateY=0;this.ranges.forEach(function(a){a.value>=e&&this.renderRange(a)},
this);this.legendSymbol.add(this.legendItem);this.legendItem.add(this.legendGroup);this.hideOverlappingLabels()};a.prototype.renderRange=function(a){var b=this.options,d=b.labels,e=this.chart.renderer,g=this.symbols,f=g.labels,c=a.center,k=Math.abs(a.radius),h=b.connectorDistance||0,m=d.align,p=d.style.fontSize;h=this.legend.options.rtl||"left"===m?-h:h;d=b.connectorWidth;var n=this.ranges[0].radius||0,r=c-k-b.borderWidth/2+d/2;p=p/2-(this.fontMetrics.h-p)/2;var q=e.styledMode;"center"===m&&(h=0,
b.connectorDistance=0,a.labelStyle.align="center");m=r+b.labels.y;var u=n+h+b.labels.x;g.bubbleItems.push(e.circle(n,c+((r%1?1:.5)-(d%2?0:.5)),k).attr(q?{}:a.bubbleStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-symbol "+(b.className||"")).add(this.legendSymbol));g.connectors.push(e.path(e.crispLine([["M",n,r],["L",n+h,r]],b.connectorWidth)).attr(q?{}:a.connectorStyle).addClass((q?"highcharts-color-"+this.options.seriesIndex+" ":"")+"highcharts-bubble-legend-connectors "+
(b.connectorClassName||"")).add(this.legendSymbol));a=e.text(this.formatLabel(a),u,m+p).attr(q?{}:a.labelStyle).addClass("highcharts-bubble-legend-labels "+(b.labels.className||"")).add(this.legendSymbol);f.push(a);a.placed=!0;a.alignAttr={x:u,y:m+p}};a.prototype.getMaxLabelSize=function(){var a,e;this.symbols.labels.forEach(function(b){e=b.getBBox(!0);a=a?e.width>a.width?e:a:e});return a||{}};a.prototype.formatLabel=function(a){var b=this.options,d=b.labels.formatter;b=b.labels.format;var e=this.chart.numberFormatter;
return b?c.format(b,a):d?d.call(a):e(a.value,1)};a.prototype.hideOverlappingLabels=function(){var a=this.chart,e=this.symbols;!this.options.labels.allowOverlap&&e&&(a.hideOverlappingLabels(e.labels),e.labels.forEach(function(a,b){a.newOpacity?a.newOpacity!==a.oldOpacity&&e.connectors[b].show():e.connectors[b].hide()}))};a.prototype.getRanges=function(){var a=this.legend.bubbleLegend,e=a.options.ranges,d,g=Number.MAX_VALUE,c=-Number.MAX_VALUE;a.chart.series.forEach(function(a){a.isBubble&&!a.ignoreSeries&&
(d=a.zData.filter(A),d.length&&(g=p(a.options.zMin,Math.min(g,Math.max(y(d),!1===a.options.displayNegative?a.options.zThreshold:-Number.MAX_VALUE))),c=p(a.options.zMax,Math.max(c,w(d)))))});var f=g===c?[{value:c}]:[{value:g},{value:(g+c)/2},{value:c,autoRanges:!0}];e.length&&e[0].radius&&f.reverse();f.forEach(function(a,b){e&&e[b]&&(f[b]=m(!1,e[b],a))});return f};a.prototype.predictBubbleSizes=function(){var a=this.chart,e=this.fontMetrics,d=a.legend.options,g="horizontal"===d.layout,c=g?a.legend.lastLineHeight:
0,f=a.plotSizeX,k=a.plotSizeY,h=a.series[this.options.seriesIndex];a=Math.ceil(h.minPxSize);var m=Math.ceil(h.maxPxSize);h=h.options.maxSize;var p=Math.min(k,f);if(d.floating||!/%$/.test(h))e=m;else if(h=parseFloat(h),e=(p+c-e.h/2)*h/100/(h/100+1),g&&k-e>=f||!g&&f-e>=k)e=m;return[a,Math.ceil(e)]};a.prototype.updateRanges=function(a,e){var b=this.legend.options.bubbleLegend;b.minSize=a;b.maxSize=e;b.ranges=this.getRanges()};a.prototype.correctSizes=function(){var a=this.legend,e=this.chart.series[this.options.seriesIndex];
1<Math.abs(Math.ceil(e.maxPxSize)-this.options.maxSize)&&(this.updateRanges(this.options.minSize,e.maxPxSize),a.render())};return a}();n(q,"afterGetAllItems",function(a){var b=this.bubbleLegend,e=this.options,d=e.bubbleLegend,g=this.chart.getVisibleBubbleSeriesIndex();b&&b.ranges&&b.ranges.length&&(d.ranges.length&&(d.autoRanges=!!d.ranges[0].autoRanges),this.destroyItem(b));0<=g&&e.enabled&&d.enabled&&(d.seriesIndex=g,this.bubbleLegend=new h.BubbleLegend(d,this),this.bubbleLegend.addToLegend(a.allItems))});
a.prototype.getVisibleBubbleSeriesIndex=function(){for(var a=this.series,b=0;b<a.length;){if(a[b]&&a[b].isBubble&&a[b].visible&&a[b].zData.length)return b;b++}return-1};q.prototype.getLinesHeights=function(){var a=this.allItems,b=[],g=a.length,d,c=0;for(d=0;d<g;d++)if(a[d].legendItemHeight&&(a[d].itemHeight=a[d].legendItemHeight),a[d]===a[g-1]||a[d+1]&&a[d]._legendItemPos[1]!==a[d+1]._legendItemPos[1]){b.push({height:0});var k=b[b.length-1];for(c;c<=d;c++)a[c].itemHeight>k.height&&(k.height=a[c].itemHeight);
k.step=d}return b};q.prototype.retranslateItems=function(a){var b,e,d,g=this.options.rtl,c=0;this.allItems.forEach(function(f,k){b=f.legendGroup.translateX;e=f._legendItemPos[1];if((d=f.movementX)||g&&f.ranges)d=g?b-f.options.maxSize/2:b+d,f.legendGroup.attr({translateX:d});k>a[c].step&&c++;f.legendGroup.attr({translateY:Math.round(e+a[c].height/2)});f._legendItemPos[1]=e+a[c].height/2})};n(C,"legendItemClick",function(){var a=this.chart,b=this.visible,g=this.chart.legend;g&&g.bubbleLegend&&(this.visible=
!b,this.ignoreSeries=b,a=0<=a.getVisibleBubbleSeriesIndex(),g.bubbleLegend.visible!==a&&(g.update({bubbleLegend:{enabled:a}}),g.bubbleLegend.visible=a),this.visible=b)});x(a.prototype,"drawChartBox",function(a,b,g){var d=this.legend,e=0<=this.getVisibleBubbleSeriesIndex();if(d&&d.options.enabled&&d.bubbleLegend&&d.options.bubbleLegend.autoRanges&&e){var c=d.bubbleLegend.options;e=d.bubbleLegend.predictBubbleSizes();d.bubbleLegend.updateRanges(e[0],e[1]);c.placed||(d.group.placed=!1,d.allItems.forEach(function(a){a.legendGroup.translateY=
null}));d.render();this.getMargins();this.axes.forEach(function(a){a.visible&&a.render();c.placed||(a.setScale(),a.updateNames(),k(a.ticks,function(a){a.isNew=!0;a.isNewLabel=!0}))});c.placed=!0;this.getMargins();a.call(this,b,g);d.bubbleLegend.correctSizes();d.retranslateItems(d.getLinesHeights())}else a.call(this,b,g),d&&d.options.enabled&&d.bubbleLegend&&(d.render(),d.retranslateItems(d.getLinesHeights()))});h.BubbleLegend=u;return h.BubbleLegend});B(a,"Series/Bubble/BubbleSeries.js",[a["Core/Axis/Axis.js"],
a["Core/Series/Series.js"],a["Core/Color/Color.js"],a["Core/Globals.js"],a["Core/Series/Point.js"],a["Core/Utilities.js"]],function(a,n,h,q,c,r){var w=h.parse;h=q.noop;var y=r.arrayMax,A=r.arrayMin,m=r.clamp,k=r.extend,p=r.isNumber,u=r.pick,E=r.pInt,x=q.Series,C=n.seriesTypes;"";n.seriesType("bubble","scatter",{dataLabels:{formatter:function(){return this.point.z},inside:!0,verticalAlign:"middle"},animationLimit:250,marker:{lineColor:null,lineWidth:1,fillOpacity:.5,radius:null,states:{hover:{radiusPlus:0}},
symbol:"circle"},minSize:8,maxSize:"20%",softThreshold:!1,states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0,zoneAxis:"z"},{pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],specialGroup:"group",bubblePadding:!0,zoneAxis:"z",directTouch:!0,isBubble:!0,pointAttribs:function(a,e){var b=this.options.marker.fillOpacity;a=x.prototype.pointAttribs.call(this,a,e);1!==b&&(a.fill=w(a.fill).setOpacity(b).get("rgba"));
return a},getRadii:function(a,e,b){var g=this.zData,d=this.yData,c=b.minPxSize,k=b.maxPxSize,f=[];var l=0;for(b=g.length;l<b;l++){var h=g[l];f.push(this.getRadius(a,e,c,k,h,d[l]))}this.radii=f},getRadius:function(a,e,b,c,d,k){var g=this.options,f="width"!==g.sizeBy,l=g.zThreshold,h=e-a,m=.5;if(null===k||null===d)return null;if(p(d)){g.sizeByAbsoluteValue&&(d=Math.abs(d-l),h=Math.max(e-l,Math.abs(a-l)),a=0);if(d<a)return b/2-1;0<h&&(m=(d-a)/h)}f&&0<=m&&(m=Math.sqrt(m));return Math.ceil(b+m*(c-b))/
2},animate:function(a){!a&&this.points.length<this.options.animationLimit&&this.points.forEach(function(a){var b=a.graphic;b&&b.width&&(this.hasRendered||b.attr({x:a.plotX,y:a.plotY,width:1,height:1}),b.animate(this.markerAttribs(a),this.options.animation))},this)},hasData:function(){return!!this.processedXData.length},translate:function(){var a,e=this.data,b=this.radii;C.scatter.prototype.translate.call(this);for(a=e.length;a--;){var c=e[a];var d=b?b[a]:0;p(d)&&d>=this.minPxSize/2?(c.marker=k(c.marker,
{radius:d,width:2*d,height:2*d}),c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=void 0}},alignDataLabel:C.column.prototype.alignDataLabel,buildKDTree:h,applyZones:h},{haloPath:function(a){return c.prototype.haloPath.call(this,0===a?0:(this.marker?this.marker.radius||0:0)+a)},ttBelow:!1});a.prototype.beforePadding=function(){var a=this,e=this.len,b=this.chart,c=0,d=e,k=this.isXAxis,h=k?"xData":"yData",f=this.min,l={},n=Math.min(b.plotWidth,b.plotHeight),r=Number.MAX_VALUE,
q=-Number.MAX_VALUE,w=this.max-f,x=e/w,C=[];this.series.forEach(function(d){var e=d.options;!d.bubblePadding||!d.visible&&b.options.chart.ignoreHiddenSeries||(a.allowZoomOutside=!0,C.push(d),k&&(["minSize","maxSize"].forEach(function(a){var b=e[a],d=/%$/.test(b);b=E(b);l[a]=d?n*b/100:b}),d.minPxSize=l.minSize,d.maxPxSize=Math.max(l.maxSize,l.minSize),d=d.zData.filter(p),d.length&&(r=u(e.zMin,m(A(d),!1===e.displayNegative?e.zThreshold:-Number.MAX_VALUE,r)),q=u(e.zMax,Math.max(q,y(d))))))});C.forEach(function(b){var e=
b[h],g=e.length;k&&b.getRadii(r,q,b);if(0<w)for(;g--;)if(p(e[g])&&a.dataMin<=e[g]&&e[g]<=a.max){var l=b.radii?b.radii[g]:0;c=Math.min((e[g]-f)*x-l,c);d=Math.max((e[g]-f)*x+l,d)}});C.length&&0<w&&!this.logarithmic&&(d-=e,x*=(e+Math.max(0,c)-Math.min(d,e))/e,[["min","userMin",c],["max","userMax",d]].forEach(function(b){"undefined"===typeof u(a.options[b[0]],a[b[1]])&&(a[b[0]]+=b[2]/x)}))};""});B(a,"Series/MapBubbleSeries.js",[a["Core/Series/Series.js"],a["Core/Series/Point.js"],a["Core/Utilities.js"]],
function(a,n,h){var q=h.merge,c=a.seriesTypes;c.bubble&&a.seriesType("mapbubble","bubble",{animationLimit:500,tooltip:{pointFormat:"{point.name}: {point.z}"}},{xyFromShape:!0,type:"mapbubble",pointArrayMap:["z"],getMapData:c.map.prototype.getMapData,getBox:c.map.prototype.getBox,setData:c.map.prototype.setData,setOptions:c.map.prototype.setOptions},{applyOptions:function(a,h){return a&&"undefined"!==typeof a.lat&&"undefined"!==typeof a.lon?n.prototype.applyOptions.call(this,q(a,this.series.chart.fromLatLonToPoint(a)),
h):c.map.prototype.pointClass.prototype.applyOptions.call(this,a,h)},isValid:function(){return"number"===typeof this.z},ttBelow:!1});""});B(a,"Series/HeatmapSeries.js",[a["Core/Series/Series.js"],a["Mixins/ColorMapSeries.js"],a["Core/Globals.js"],a["Mixins/LegendSymbol.js"],a["Core/Renderer/SVG/SVGRenderer.js"],a["Core/Utilities.js"]],function(a,n,h,q,c,r){var w=n.colorMapPointMixin;n=n.colorMapSeriesMixin;var y=h.noop,A=r.clamp,m=r.extend,k=r.fireEvent,p=r.isNumber,u=r.merge,E=r.pick,x=h.Series;
r=a.seriesTypes;var C=c.prototype.symbols;"";a.seriesType("heatmap","scatter",{animation:!1,borderWidth:0,nullColor:"#f7f7f7",dataLabels:{formatter:function(){return this.point.value},inside:!0,verticalAlign:"middle",crop:!1,overflow:!1,padding:0},marker:{symbol:"rect",radius:0,lineColor:void 0,states:{hover:{lineWidthPlus:0},select:{}}},clip:!0,pointRange:null,tooltip:{pointFormat:"{point.x}, {point.y}: {point.value}<br/>"},states:{hover:{halo:!1,brightness:.2}}},u(n,{pointArrayMap:["y","value"],
hasPointSpecificOptions:!0,getExtremesFromAll:!0,directTouch:!0,init:function(){x.prototype.init.apply(this,arguments);var a=this.options;a.pointRange=E(a.pointRange,a.colsize||1);this.yAxis.axisPointRange=a.rowsize||1;m(C,{ellipse:C.circle,rect:C.square})},getSymbol:x.prototype.getSymbol,setClip:function(a){var e=this.chart;x.prototype.setClip.apply(this,arguments);(!1!==this.options.clip||a)&&this.markerGroup.clip((a||this.clipBox)&&this.sharedClipKey?e[this.sharedClipKey]:e.clipRect)},translate:function(){var a=
this.options,e=a.marker&&a.marker.symbol||"",b=C[e]?e:"rect";a=this.options;var c=-1!==["circle","square"].indexOf(b);this.generatePoints();this.points.forEach(function(a){var d=a.getCellAttributes(),g={x:Math.min(d.x1,d.x2),y:Math.min(d.y1,d.y2),width:Math.max(Math.abs(d.x2-d.x1),0),height:Math.max(Math.abs(d.y2-d.y1),0)};var f=a.hasImage=0===(a.marker&&a.marker.symbol||e||"").indexOf("url");if(c){var k=Math.abs(g.width-g.height);g.x=Math.min(d.x1,d.x2)+(g.width<g.height?0:k/2);g.y=Math.min(d.y1,
d.y2)+(g.width<g.height?k/2:0);g.width=g.height=Math.min(g.width,g.height)}k={plotX:(d.x1+d.x2)/2,plotY:(d.y1+d.y2)/2,clientX:(d.x1+d.x2)/2,shapeType:"path",shapeArgs:u(!0,g,{d:C[b](g.x,g.y,g.width,g.height)})};f&&(a.marker={width:g.width,height:g.height});m(a,k)});k(this,"afterTranslate")},pointAttribs:function(a,e){var b=x.prototype.pointAttribs.call(this,a,e),c=this.options||{},d=this.chart.options.plotOptions||{},g=d.series||{},k=d.heatmap||{};d=c.borderColor||k.borderColor||g.borderColor;g=c.borderWidth||
k.borderWidth||g.borderWidth||b["stroke-width"];b.stroke=a&&a.marker&&a.marker.lineColor||c.marker&&c.marker.lineColor||d||this.color;b["stroke-width"]=g;e&&(a=u(c.states[e],c.marker&&c.marker.states[e],a.options.states&&a.options.states[e]||{}),e=a.brightness,b.fill=a.color||h.color(b.fill).brighten(e||0).get(),b.stroke=a.lineColor);return b},markerAttribs:function(a,e){var b=a.marker||{},c=this.options.marker||{},d=a.shapeArgs||{},g={};if(a.hasImage)return{x:a.plotX,y:a.plotY};if(e){var k=c.states[e]||
{};var f=b.states&&b.states[e]||{};[["width","x"],["height","y"]].forEach(function(a){g[a[0]]=(f[a[0]]||k[a[0]]||d[a[0]])+(f[a[0]+"Plus"]||k[a[0]+"Plus"]||0);g[a[1]]=d[a[1]]+(d[a[0]]-g[a[0]])/2})}return e?g:d},drawPoints:function(){var a=this;if((this.options.marker||{}).enabled||this._hasPointMarkers)x.prototype.drawPoints.call(this),this.points.forEach(function(e){e.graphic&&e.graphic[a.chart.styledMode?"css":"animate"](a.colorAttribs(e))})},hasData:function(){return!!this.processedXData.length},
getValidPoints:function(a,e){return x.prototype.getValidPoints.call(this,a,e,!0)},getBox:y,drawLegendSymbol:q.drawRectangle,alignDataLabel:r.column.prototype.alignDataLabel,getExtremes:function(){var a=x.prototype.getExtremes.call(this,this.valueData),e=a.dataMin;a=a.dataMax;p(e)&&(this.valueMin=e);p(a)&&(this.valueMax=a);return x.prototype.getExtremes.call(this)}}),u(w,{applyOptions:function(a,e){a=h.Point.prototype.applyOptions.call(this,a,e);a.formatPrefix=a.isNull||null===a.value?"null":"point";
return a},isValid:function(){return Infinity!==this.value&&-Infinity!==this.value},haloPath:function(a){if(!a)return[];var e=this.shapeArgs;return["M",e.x-a,e.y-a,"L",e.x-a,e.y+e.height+a,e.x+e.width+a,e.y+e.height+a,e.x+e.width+a,e.y-a,"Z"]},getCellAttributes:function(){var a=this.series,e=a.options,b=(e.colsize||1)/2,c=(e.rowsize||1)/2,d=a.xAxis,k=a.yAxis,h=this.options.marker||a.options.marker;a=a.pointPlacementToXValue();var f=E(this.pointPadding,e.pointPadding,0),l={x1:A(Math.round(d.len-(d.translate(this.x-
b,!1,!0,!1,!0,-a)||0)),-d.len,2*d.len),x2:A(Math.round(d.len-(d.translate(this.x+b,!1,!0,!1,!0,-a)||0)),-d.len,2*d.len),y1:A(Math.round(k.translate(this.y-c,!1,!0,!1,!0)||0),-k.len,2*k.len),y2:A(Math.round(k.translate(this.y+c,!1,!0,!1,!0)||0),-k.len,2*k.len)};[["width","x"],["height","y"]].forEach(function(a){var b=a[0];a=a[1];var d=a+"1",e=a+"2",c=Math.abs(l[d]-l[e]),k=h&&h.lineWidth||0,g=Math.abs(l[d]+l[e])/2;h[b]&&h[b]<c&&(l[d]=g-h[b]/2-k/2,l[e]=g+h[b]/2+k/2);f&&("y"===a&&(d=e,e=a+"1"),l[d]+=
f,l[e]-=f)});return l}}));""});B(a,"Extensions/GeoJSON.js",[a["Core/Chart/Chart.js"],a["Core/Globals.js"],a["Core/Utilities.js"]],function(a,n,h){function q(a,c){var k,h=!1,m=a.x,n=a.y;a=0;for(k=c.length-1;a<c.length;k=a++){var q=c[a][1]>n;var g=c[k][1]>n;q!==g&&m<(c[k][0]-c[a][0])*(n-c[a][1])/(c[k][1]-c[a][1])+c[a][0]&&(h=!h)}return h}var c=n.win,r=h.error,w=h.extend,y=h.format,A=h.merge;h=h.wrap;"";a.prototype.transformFromLatLon=function(a,k){var h,m=(null===(h=this.userOptions.chart)||void 0===
h?void 0:h.proj4)||c.proj4;if(!m)return r(21,!1,this),{x:0,y:null};a=m(k.crs,[a.lon,a.lat]);h=k.cosAngle||k.rotation&&Math.cos(k.rotation);m=k.sinAngle||k.rotation&&Math.sin(k.rotation);a=k.rotation?[a[0]*h+a[1]*m,-a[0]*m+a[1]*h]:a;return{x:((a[0]-(k.xoffset||0))*(k.scale||1)+(k.xpan||0))*(k.jsonres||1)+(k.jsonmarginX||0),y:(((k.yoffset||0)-a[1])*(k.scale||1)+(k.ypan||0))*(k.jsonres||1)-(k.jsonmarginY||0)}};a.prototype.transformToLatLon=function(a,k){if("undefined"===typeof c.proj4)r(21,!1,this);
else{a={x:((a.x-(k.jsonmarginX||0))/(k.jsonres||1)-(k.xpan||0))/(k.scale||1)+(k.xoffset||0),y:((-a.y-(k.jsonmarginY||0))/(k.jsonres||1)+(k.ypan||0))/(k.scale||1)+(k.yoffset||0)};var h=k.cosAngle||k.rotation&&Math.cos(k.rotation),m=k.sinAngle||k.rotation&&Math.sin(k.rotation);k=c.proj4(k.crs,"WGS84",k.rotation?{x:a.x*h+a.y*-m,y:a.x*m+a.y*h}:a);return{lat:k.y,lon:k.x}}};a.prototype.fromPointToLatLon=function(a){var c=this.mapTransforms,h;if(c){for(h in c)if(Object.hasOwnProperty.call(c,h)&&c[h].hitZone&&
q({x:a.x,y:-a.y},c[h].hitZone.coordinates[0]))return this.transformToLatLon(a,c[h]);return this.transformToLatLon(a,c["default"])}r(22,!1,this)};a.prototype.fromLatLonToPoint=function(a){var c=this.mapTransforms,h;if(!c)return r(22,!1,this),{x:0,y:null};for(h in c)if(Object.hasOwnProperty.call(c,h)&&c[h].hitZone){var m=this.transformFromLatLon(a,c[h]);if(q({x:m.x,y:-m.y},c[h].hitZone.coordinates[0]))return m}return this.transformFromLatLon(a,c["default"])};n.geojson=function(a,c,h){var k=[],m=[],
n=function(a){a.forEach(function(a,c){0===c?m.push(["M",a[0],-a[1]]):m.push(["L",a[0],-a[1]])})};c=c||"map";a.features.forEach(function(a){var g=a.geometry,e=g.type;g=g.coordinates;a=a.properties;var b;m=[];"map"===c||"mapbubble"===c?("Polygon"===e?(g.forEach(n),m.push(["Z"])):"MultiPolygon"===e&&(g.forEach(function(a){a.forEach(n)}),m.push(["Z"])),m.length&&(b={path:m})):"mapline"===c?("LineString"===e?n(g):"MultiLineString"===e&&g.forEach(n),m.length&&(b={path:m})):"mappoint"===c&&"Point"===e&&
(b={x:g[0],y:-g[1]});b&&k.push(w(b,{name:a.name||a.NAME,properties:a}))});h&&a.copyrightShort&&(h.chart.mapCredits=y(h.chart.options.credits.mapText,{geojson:a}),h.chart.mapCreditsFull=y(h.chart.options.credits.mapTextFull,{geojson:a}));return k};h(a.prototype,"addCredits",function(a,c){c=A(!0,this.options.credits,c);this.mapCredits&&(c.href=null);a.call(this,c);this.credits&&this.mapCreditsFull&&this.credits.attr({title:this.mapCreditsFull})})});B(a,"masters/modules/map.src.js",[],function(){})});
//# sourceMappingURL=map.js.map

View File

@ -1,5 +1,5 @@
/*
Highcharts JS v7.1.2 (2019-06-03)
Highcharts JS v8.2.2 (2020-10-22)
Plugin for displaying a message when there is no data visible in chart.
@ -8,7 +8,7 @@
License: www.highcharts.com/license
*/
(function(a){"object"===typeof module&&module.exports?(a["default"]=a,module.exports=a):"function"===typeof define&&define.amd?define("highcharts/modules/no-data-to-display",["highcharts"],function(c){a(c);a.Highcharts=c;return a}):a("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(a){function c(a,e,c,d){a.hasOwnProperty(e)||(a[e]=d.apply(null,c))}a=a?a._modules:{};c(a,"modules/no-data-to-display.src.js",[a["parts/Globals.js"]],function(a){var c=a.Chart.prototype,f=a.getOptions(),d=a.extend;
d(f.lang,{noData:"No data to display"});f.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"},style:{fontWeight:"bold",fontSize:"12px",color:"#666666"}};c.showNoData=function(a){var b=this.options;a=a||b&&b.lang.noData;b=b&&b.noData;!this.noDataLabel&&this.renderer&&(this.noDataLabel=this.renderer.label(a,0,0,null,null,null,b.useHTML,null,"no-data"),this.styledMode||this.noDataLabel.attr(b.attr).css(b.style),this.noDataLabel.add(),this.noDataLabel.align(d(this.noDataLabel.getBBox(),b.position),
!1,"plotBox"))};c.hideNoData=function(){this.noDataLabel&&(this.noDataLabel=this.noDataLabel.destroy())};c.hasData=function(){for(var a=this.series||[],b=a.length;b--;)if(a[b].hasData()&&!a[b].options.isInternal)return!0;return this.loadingShown};a.addEvent(a.Chart,"render",function(){this.hasData()?this.hideNoData():this.showNoData()})});c(a,"masters/modules/no-data-to-display.src.js",[],function(){})});
//# sourceMappingURL=no-data-to-display.js.map
(function(a){"object"===typeof module&&module.exports?(a["default"]=a,module.exports=a):"function"===typeof define&&define.amd?define("highcharts/modules/no-data-to-display",["highcharts"],function(d){a(d);a.Highcharts=d;return a}):a("undefined"!==typeof Highcharts?Highcharts:void 0)})(function(a){function d(a,c,d,e){a.hasOwnProperty(c)||(a[c]=e.apply(null,d))}a=a?a._modules:{};d(a,"Extensions/NoDataToDisplay.js",[a["Core/Chart/Chart.js"],a["Core/Utilities.js"]],function(a,c){var d=c.addEvent,e=c.extend,
f=c.getOptions;c=a.prototype;f=f();e(f.lang,{noData:"No data to display"});f.noData={attr:{zIndex:1},position:{x:0,y:0,align:"center",verticalAlign:"middle"},style:{fontWeight:"bold",fontSize:"12px",color:"#666666"}};c.showNoData=function(a){var b=this.options;a=a||b&&b.lang.noData;b=b&&(b.noData||{});this.renderer&&(this.noDataLabel||(this.noDataLabel=this.renderer.label(a,0,0,void 0,void 0,void 0,b.useHTML,void 0,"no-data").add()),this.styledMode||this.noDataLabel.attr(b.attr).css(b.style||{}),
this.noDataLabel.align(e(this.noDataLabel.getBBox(),b.position||{}),!1,"plotBox"))};c.hideNoData=function(){this.noDataLabel&&(this.noDataLabel=this.noDataLabel.destroy())};c.hasData=function(){for(var a=this.series||[],b=a.length;b--;)if(a[b].hasData()&&!a[b].options.isInternal)return!0;return this.loadingShown};d(a,"render",function(){this.hasData()?this.hideNoData():this.showNoData()})});d(a,"masters/modules/no-data-to-display.src.js",[],function(){})});
//# sourceMappingURL=no-data-to-display.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,125 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.domain.mutators;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.Message;
import com.djrapitops.plan.settings.locale.lang.HtmlLang;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ActivityIndexTest {
@Test
void localeIsUsedWhenGivenForGetGroups() {
Locale locale = createTestLocale();
String[] expected = new String[]{"A", "B", "C", "D", "E"};
String[] result = ActivityIndex.getGroups(locale);
assertArrayEquals(expected, result);
}
private Locale createTestLocale() {
Locale locale = new Locale();
locale.put(HtmlLang.INDEX_VERY_ACTIVE, new Message("A"));
locale.put(HtmlLang.INDEX_ACTIVE, new Message("B"));
locale.put(HtmlLang.INDEX_REGULAR, new Message("C"));
locale.put(HtmlLang.INDEX_IRREGULAR, new Message("D"));
locale.put(HtmlLang.INDEX_INACTIVE, new Message("E"));
return locale;
}
@Test
void localeDefaultGroups() {
String[] expected = new String[]{"Very Active", "Active", "Regular", "Irregular", "Inactive"};
String[] result = ActivityIndex.getDefaultGroups();
assertArrayEquals(expected, result);
}
@Test
void veryActiveGroup() {
String expected = HtmlLang.INDEX_VERY_ACTIVE.getDefault();
String result = new ActivityIndex(ActivityIndex.VERY_ACTIVE, 0).getGroup();
assertEquals(expected, result);
}
@Test
void activeGroup() {
String expected = HtmlLang.INDEX_ACTIVE.getDefault();
String result = new ActivityIndex(ActivityIndex.ACTIVE, 0).getGroup();
assertEquals(expected, result);
}
@Test
void regularGroup() {
String expected = HtmlLang.INDEX_REGULAR.getDefault();
String result = new ActivityIndex(ActivityIndex.REGULAR, 0).getGroup();
assertEquals(expected, result);
}
@Test
void irregularGroup() {
String expected = HtmlLang.INDEX_IRREGULAR.getDefault();
String result = new ActivityIndex(ActivityIndex.IRREGULAR, 0).getGroup();
assertEquals(expected, result);
}
@Test
void inactiveGroup() {
String expected = HtmlLang.INDEX_INACTIVE.getDefault();
String result = new ActivityIndex(0.0, 0).getGroup();
assertEquals(expected, result);
}
@Test
void veryActiveGroupWithLocale() {
String expected = "A";
String result = new ActivityIndex(ActivityIndex.VERY_ACTIVE, 0).getGroup(createTestLocale());
assertEquals(expected, result);
}
@Test
void activeGroupWithLocale() {
String expected = "B";
String result = new ActivityIndex(ActivityIndex.ACTIVE, 0).getGroup(createTestLocale());
assertEquals(expected, result);
}
@Test
void regularGroupWithLocale() {
String expected = "C";
String result = new ActivityIndex(ActivityIndex.REGULAR, 0).getGroup(createTestLocale());
assertEquals(expected, result);
}
@Test
void irregularGroupWithLocale() {
String expected = "D";
String result = new ActivityIndex(ActivityIndex.IRREGULAR, 0).getGroup(createTestLocale());
assertEquals(expected, result);
}
@Test
void inactiveGroupWithLocale() {
String expected = "E";
String result = new ActivityIndex(0.0, 0).getGroup(createTestLocale());
assertEquals(expected, result);
}
}

Some files were not shown because too many files have changed in this diff Show More