diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java index cb0f23465..f881433b5 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java @@ -16,6 +16,8 @@ */ package com.djrapitops.plan.extension.icon; +import java.util.Objects; + /** * Object that represents an icon on the website. *

@@ -72,6 +74,19 @@ public class Icon { return this; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Icon icon = (Icon) o; + return type == icon.type && Objects.equals(getName(), icon.getName()) && getColor() == icon.getColor(); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, getName(), getColor()); + } + @Override public String toString() { return "Icon{" + type.name() + ", '" + name + '\'' + ", " + color.name() + '}'; diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java index a8f941345..4c0751a43 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java @@ -21,10 +21,8 @@ import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Icon; import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; /** * Object for giving Plan table data. @@ -100,6 +98,52 @@ public final class Table { return tableColumnFormats; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Table table = (Table) o; + return Arrays.equals(getColumns(), table.getColumns()) + && Arrays.equals(getIcons(), table.getIcons()) + && Arrays.equals(getTableColumnFormats(), table.getTableColumnFormats()) + && valuesEqual(table); + } + + private boolean valuesEqual(Table other) { + List rows1 = getRows(); + List rows2 = other.getRows(); + if (rows1.size() != rows2.size()) return false; + for (int i = 0; i < rows1.size(); i++) { + Object[] values1 = rows1.get(i); + Object[] values2 = rows2.get(i); + for (int j = 0; j < getMaxColumnSize(); j++) { + if (!Objects.equals(Objects.toString(values1[0]), Objects.toString(values2[0]))) { + return false; + } + } + } + return true; + } + + @Override + public int hashCode() { + int result = Objects.hash(getRows()); + result = 31 * result + Arrays.hashCode(getColumns()); + result = 31 * result + Arrays.hashCode(getIcons()); + result = 31 * result + Arrays.hashCode(getTableColumnFormats()); + return result; + } + + @Override + public String toString() { + return "Table{" + + "columns=" + Arrays.toString(columns) + + ", icons=" + Arrays.toString(icons) + + ", tableColumnFormats=" + Arrays.toString(tableColumnFormats) + + ", rows=" + rows.stream().map(Arrays::toString).collect(Collectors.toList()) + + '}'; + } + /** * Factory for creating new {@link Table} objects. */ diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitTaskModule.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitTaskModule.java index bb6bf3744..b5292682e 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitTaskModule.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitTaskModule.java @@ -19,7 +19,6 @@ package com.djrapitops.plan.modules.bukkit; import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.addons.placeholderapi.PlaceholderCacheRefreshTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask; -import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.configuration.AddressAllowList; @@ -94,10 +93,6 @@ public interface BukkitTaskModule { @IntoSet TaskSystem.Task bindResourceWriteTask(ResourceWriteTask resourceWriteTask); - @Binds - @IntoSet - TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); - @Binds @IntoSet TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask); diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeTaskModule.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeTaskModule.java index 51579a0be..73651757a 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeTaskModule.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeTaskModule.java @@ -18,7 +18,6 @@ package com.djrapitops.plan.modules.bungee; import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.delivery.web.ResourceWriteTask; -import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.configuration.AddressAllowList; @@ -81,10 +80,6 @@ public interface BungeeTaskModule { @IntoSet TaskSystem.Task bindResourceWriteTask(ResourceWriteTask resourceWriteTask); - @Binds - @IntoSet - TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); - @Binds @IntoSet TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask); diff --git a/Plan/common/build.gradle b/Plan/common/build.gradle index 2d7d24bc5..23b857816 100644 --- a/Plan/common/build.gradle +++ b/Plan/common/build.gradle @@ -136,6 +136,7 @@ task yarnStart(type: YarnTask) { task copyYarnBuildResults { inputs.files(fileTree("$rootDir/react/dashboard/build")) outputs.dir("$rootDir/common/build/resources/main/assets/plan/web") + outputs.dir("$rootDir/common/build/resources/test/assets/plan/web") dependsOn yarnBundle doLast { @@ -144,6 +145,10 @@ task copyYarnBuildResults { from "$rootDir/react/dashboard/build" into "$rootDir/common/build/resources/main/assets/plan/web" } + copy { + into "$rootDir/common/build/resources/main/assets/plan/web" + into "$rootDir/common/build/resources/test/assets/plan/web" + } } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/PlayerListDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/PlayerListDto.java new file mode 100644 index 000000000..8355d8001 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/PlayerListDto.java @@ -0,0 +1,51 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.datatransfer; + +import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionDescriptionDto; + +import java.util.List; + +/** + * @author AuroraLS3 + */ +public class PlayerListDto { + + private final List players; + private final List extensionDescriptors; + + public PlayerListDto(List players, List extensionDescriptors) { + this.players = players; + this.extensionDescriptors = extensionDescriptors; + } + + public List getPlayers() { + return players; + } + + public List getExtensionDescriptors() { + return extensionDescriptors; + } + + @Override + public String toString() { + return "PlayerListDto{" + + "players=" + players + + ", extensionDescriptors=" + extensionDescriptors + + '}'; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java new file mode 100644 index 000000000..891f7ce86 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java @@ -0,0 +1,174 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.datatransfer; + +import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionValueDataDto; + +import java.util.Map; +import java.util.UUID; + +/** + * Represents a row for players table. + * + * @author AuroraLS3 + */ +public class TablePlayerDto { + + private UUID playerUUID; + private String playerName; + private double activityIndex; + private Long playtimeActive; + private Long sessionCount; + private Long lastSeen; + private Long registered; + private String country; + + private Map extensionValues; + + private TablePlayerDto() { + // Builder constructor + } + + public static TablePlayerDtoBuilder builder() { + return new TablePlayerDtoBuilder(); + } + + public String getPlayerName() { + return playerName; + } + + private void setPlayerName(String playerName) { + this.playerName = playerName; + } + + public double getActivityIndex() { + return activityIndex; + } + + private void setActivityIndex(double activityIndex) { + this.activityIndex = activityIndex; + } + + public Long getPlaytimeActive() { + return playtimeActive; + } + + private void setPlaytimeActive(Long playtimeActive) { + this.playtimeActive = playtimeActive; + } + + public Long getSessionCount() { + return sessionCount; + } + + private void setSessionCount(Long sessionCount) { + this.sessionCount = sessionCount; + } + + public Long getLastSeen() { + return lastSeen; + } + + private void setLastSeen(Long lastSeen) { + this.lastSeen = lastSeen; + } + + public Long getRegistered() { + return registered; + } + + private void setRegistered(Long registered) { + this.registered = registered; + } + + public String getCountry() { + return country; + } + + private void setCountry(String country) { + this.country = country; + } + + public Map getExtensionValues() { + return extensionValues; + } + + private void setExtensionValues(Map extensionValues) { + this.extensionValues = extensionValues; + } + + public UUID getPlayerUUID() { + return playerUUID; + } + + public void setPlayerUUID(UUID playerUUID) { + this.playerUUID = playerUUID; + } + + public static final class TablePlayerDtoBuilder { + private final TablePlayerDto tablePlayerDto; + + private TablePlayerDtoBuilder() {tablePlayerDto = new TablePlayerDto();} + + public TablePlayerDtoBuilder withUuid(UUID playerUUID) { + tablePlayerDto.setPlayerUUID(playerUUID); + return this; + } + + public TablePlayerDtoBuilder withName(String name) { + tablePlayerDto.setPlayerName(name); + return this; + } + + public TablePlayerDtoBuilder withActivityIndex(double activityIndex) { + tablePlayerDto.setActivityIndex(activityIndex); + return this; + } + + public TablePlayerDtoBuilder withPlaytimeActive(Long playtimeActive) { + tablePlayerDto.setPlaytimeActive(playtimeActive); + return this; + } + + public TablePlayerDtoBuilder withSessionCount(Long sessionCount) { + tablePlayerDto.setSessionCount(sessionCount); + return this; + } + + public TablePlayerDtoBuilder withLastSeen(Long lastSeen) { + tablePlayerDto.setLastSeen(lastSeen); + return this; + } + + public TablePlayerDtoBuilder withRegistered(Long registered) { + tablePlayerDto.setRegistered(registered); + return this; + } + + public TablePlayerDtoBuilder withCountry(String country) { + tablePlayerDto.setCountry(country); + return this; + } + + public TablePlayerDtoBuilder withExtensionValues(Map extensionValues) { + tablePlayerDto.setExtensionValues(extensionValues); + return this; + } + + public TablePlayerDto build() {return tablePlayerDto;} + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/ExtensionTabDataDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/ExtensionTabDataDto.java index 899a4f941..8a1129d39 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/ExtensionTabDataDto.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/ExtensionTabDataDto.java @@ -16,12 +16,14 @@ */ package com.djrapitops.plan.delivery.domain.datatransfer.extension; -import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; public class ExtensionTabDataDto { @@ -36,25 +38,29 @@ public class ExtensionTabDataDto { tableData = extensionTabData.getTableData().stream().map(ExtensionTableDataDto::new).collect(Collectors.toList()); } - private List constructValues(List order, ExtensionTabData tabData) { + public static Optional mapToValue(ExtensionTabData tabData, String key) { Formatters formatters = Formatters.getInstance(); - Formatter decimalFormatter = formatters.decimals(); - Formatter percentageFormatter = formatters.percentage(); - Map> numberFormatters = new EnumMap<>(FormatType.class); - numberFormatters.put(FormatType.DATE_SECOND, formatters.secondLong()); - numberFormatters.put(FormatType.DATE_YEAR, formatters.yearLong()); - numberFormatters.put(FormatType.TIME_MILLISECONDS, formatters.timeAmount()); - numberFormatters.put(FormatType.NONE, Object::toString); + Optional booleanValue = tabData.getBoolean(key).map(data -> new ExtensionValueDataDto(data.getDescription(), "BOOLEAN", data.getFormattedValue())); + if (booleanValue.isPresent()) return booleanValue; + Optional doubleValue = tabData.getDouble(key).map(data -> new ExtensionValueDataDto(data.getDescription(), "DOUBLE", data.getFormattedValue(formatters.decimals()))); + if (doubleValue.isPresent()) return doubleValue; + Optional percentage = tabData.getPercentage(key).map(data -> new ExtensionValueDataDto(data.getDescription(), "PERCENTAGE", data.getFormattedValue(formatters.percentage()))); + if (percentage.isPresent()) return percentage; + Optional number = tabData.getNumber(key).map(data -> new ExtensionValueDataDto(data.getDescription(), data.getFormatType() == FormatType.NONE ? "NUMBER" : data.getFormatType().name(), data.getFormattedValue(formatters.getNumberFormatter(data.getFormatType())))); + if (number.isPresent()) return number; + Optional string = tabData.getString(key).map(data -> new ExtensionValueDataDto(data.getDescription(), data.isPlayerName() ? "LINK" : "STRING", data.getFormattedValue())); + if (string.isPresent()) return string; + + // If component is not found either return empty Optional. + return tabData.getComponent(key).map(data -> new ExtensionValueDataDto(data.getDescription(), "COMPONENT", data.getFormattedValue())); + } + + private List constructValues(List order, ExtensionTabData tabData) { List extensionValues = new ArrayList<>(); for (String key : order) { - tabData.getBoolean(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), "BOOLEAN", data.getFormattedValue()))); - tabData.getDouble(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), "DOUBLE", data.getFormattedValue(decimalFormatter)))); - tabData.getPercentage(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), "PERCENTAGE", data.getFormattedValue(percentageFormatter)))); - tabData.getNumber(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), data.getFormatType() == FormatType.NONE ? "NUMBER" : data.getFormatType().name(), data.getFormattedValue(numberFormatters.get(data.getFormatType()))))); - tabData.getString(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), data.isPlayerName() ? "HTML" : "STRING", data.getFormattedValue()))); - tabData.getComponent(key).ifPresent(data -> extensionValues.add(new ExtensionValueDataDto(data.getDescription(), "COMPONENT", data.getFormattedValue()))); + mapToValue(tabData, key).ifPresent(extensionValues::add); } return extensionValues; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/TableDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/TableDto.java index d2b099500..c7d9b7dfe 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/TableDto.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/extension/TableDto.java @@ -16,8 +16,10 @@ */ package com.djrapitops.plan.delivery.domain.datatransfer.extension; -import com.djrapitops.plan.delivery.rendering.html.structure.HtmlTable; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.Html; import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.extension.table.TableColumnFormat; import java.util.ArrayList; import java.util.Arrays; @@ -41,11 +43,48 @@ public class TableDto { .map(IconDto::new) .collect(Collectors.toList()); - rows = HtmlTable.mapToRows(table.getRows(), table.getTableColumnFormats()).stream() + rows = mapToRows(table.getRows(), table.getTableColumnFormats()).stream() .map(row -> constructRow(columns, row)) .collect(Collectors.toList()); } + public static List mapToRows(List rows, TableColumnFormat[] tableColumnFormats) { + return rows.stream() + .map(row -> { + List mapped = new ArrayList<>(row.length); + for (int i = 0; i < row.length; i++) { + Object value = row[i]; + if (value == null) { + mapped.add(null); + } else { + TableColumnFormat format = tableColumnFormats[i]; + mapped.add(new TableCellDto(applyFormat(format, value), value)); + } + } + return mapped.toArray(new TableCellDto[0]); + }) + .collect(Collectors.toList()); + } + + public static String applyFormat(TableColumnFormat format, Object value) { + try { + switch (format) { + case TIME_MILLISECONDS: + return Formatters.getInstance().timeAmount().apply(Long.parseLong(value.toString())); + case DATE_YEAR: + return Formatters.getInstance().yearLong().apply(Long.parseLong(value.toString())); + case DATE_SECOND: + return Formatters.getInstance().secondLong().apply(Long.parseLong(value.toString())); + case PLAYER_NAME: + return Html.LINK.create("../player/" + Html.encodeToURL(value.toString())); + default: + return value.toString(); + } + } catch (Exception e) { + return Objects.toString(value); + } + } + private List constructRow(List columns, TableCellDto[] row) { List constructedRow = new ArrayList<>(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/GraphThresholds.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/GraphThresholds.java new file mode 100644 index 000000000..1e3b252d0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/GraphThresholds.java @@ -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 . + */ +package com.djrapitops.plan.delivery.domain.datatransfer.preferences; + +import java.util.Objects; + +/** + * @author AuroraLS3 + */ +public class GraphThresholds { + + private double highThreshold; + private double mediumThreshold; + + public GraphThresholds(double highThreshold, double mediumThreshold) { + this.highThreshold = highThreshold; + this.mediumThreshold = mediumThreshold; + } + + public double getHighThreshold() { + return highThreshold; + } + + public void setHighThreshold(int highThreshold) { + this.highThreshold = highThreshold; + } + + public double getMediumThreshold() { + return mediumThreshold; + } + + public void setMediumThreshold(int mediumThreshold) { + this.mediumThreshold = mediumThreshold; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GraphThresholds that = (GraphThresholds) o; + return getHighThreshold() == that.getHighThreshold() && getMediumThreshold() == that.getMediumThreshold(); + } + + @Override + public int hashCode() { + return Objects.hash(getHighThreshold(), getMediumThreshold()); + } + + @Override + public String toString() { + return "GraphThresholds{" + + "highThreshold=" + highThreshold + + ", mediumThreshold=" + mediumThreshold + + '}'; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/Preferences.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/Preferences.java new file mode 100644 index 000000000..0aec2613c --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/Preferences.java @@ -0,0 +1,212 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.datatransfer.preferences; + +import java.util.Objects; + +/** + * @author AuroraLS3 + */ +public class Preferences { + + private boolean recentDaysInDateFormat; + private String dateFormatFull; + private String dateFormatNoSeconds; + private String dateFormatClock; + private TimeFormat timeFormat; + private String decimalFormat; + private int firstDay; + + private String playerHeadImageUrl; + + private GraphThresholds tpsThresholds; + private GraphThresholds diskThresholds; + + private Preferences() { + } + + public static Builder builder() { + return new Preferences.Builder(); + } + + public boolean isRecentDaysInDateFormat() { + return recentDaysInDateFormat; + } + + private void setRecentDaysInDateFormat(boolean recentDaysInDateFormat) { + this.recentDaysInDateFormat = recentDaysInDateFormat; + } + + public String getDateFormatFull() { + return dateFormatFull; + } + + private void setDateFormatFull(String dateFormatFull) { + this.dateFormatFull = dateFormatFull; + } + + public String getDateFormatNoSeconds() { + return dateFormatNoSeconds; + } + + private void setDateFormatNoSeconds(String dateFormatNoSeconds) { + this.dateFormatNoSeconds = dateFormatNoSeconds; + } + + public String getDateFormatClock() { + return dateFormatClock; + } + + private void setDateFormatClock(String dateFormatClock) { + this.dateFormatClock = dateFormatClock; + } + + public TimeFormat getTimeFormat() { + return timeFormat; + } + + private void setTimeFormat(TimeFormat timeFormat) { + this.timeFormat = timeFormat; + } + + public String getDecimalFormat() { + return decimalFormat; + } + + private void setDecimalFormat(String decimalFormat) { + this.decimalFormat = decimalFormat; + } + + public int getFirstDay() { + return firstDay; + } + + private void setFirstDay(int firstDay) { + this.firstDay = firstDay; + } + + public String getPlayerHeadImageUrl() { + return playerHeadImageUrl; + } + + private void setPlayerHeadImageUrl(String playerHeadImageUrl) { + this.playerHeadImageUrl = playerHeadImageUrl; + } + + public GraphThresholds getTpsThresholds() { + return tpsThresholds; + } + + private void setTpsThresholds(GraphThresholds tpsThresholds) { + this.tpsThresholds = tpsThresholds; + } + + public GraphThresholds getDiskThresholds() { + return diskThresholds; + } + + private void setDiskThresholds(GraphThresholds diskThresholds) { + this.diskThresholds = diskThresholds; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Preferences that = (Preferences) o; + return isRecentDaysInDateFormat() == that.isRecentDaysInDateFormat() && getFirstDay() == that.getFirstDay() && Objects.equals(getDateFormatFull(), that.getDateFormatFull()) && Objects.equals(getDateFormatNoSeconds(), that.getDateFormatNoSeconds()) && Objects.equals(getDateFormatClock(), that.getDateFormatClock()) && Objects.equals(getTimeFormat(), that.getTimeFormat()) && Objects.equals(getDecimalFormat(), that.getDecimalFormat()) && Objects.equals(getPlayerHeadImageUrl(), that.getPlayerHeadImageUrl()) && Objects.equals(getTpsThresholds(), that.getTpsThresholds()) && Objects.equals(getDiskThresholds(), that.getDiskThresholds()); + } + + @Override + public int hashCode() { + return Objects.hash(isRecentDaysInDateFormat(), getDateFormatFull(), getDateFormatNoSeconds(), getDateFormatClock(), getTimeFormat(), getDecimalFormat(), getFirstDay(), getPlayerHeadImageUrl(), getTpsThresholds(), getDiskThresholds()); + } + + @Override + public String toString() { + return "Preferences{" + + "recentDaysInDateFormat=" + recentDaysInDateFormat + + ", dateFormatFull='" + dateFormatFull + '\'' + + ", dateFormatNoSeconds='" + dateFormatNoSeconds + '\'' + + ", dateFormatClock='" + dateFormatClock + '\'' + + ", timeFormat=" + timeFormat + + ", decimalFormat='" + decimalFormat + '\'' + + ", firstDay=" + firstDay + + ", playerHeadImageUrl='" + playerHeadImageUrl + '\'' + + ", tpsThresholds=" + tpsThresholds + + ", diskThresholds=" + diskThresholds + + '}'; + } + + public static final class Builder { + private final Preferences preferences; + + private Builder() {preferences = new Preferences();} + + public Builder withRecentDaysInDateFormat(boolean recentDaysInDateFormat) { + preferences.setRecentDaysInDateFormat(recentDaysInDateFormat); + return this; + } + + public Builder withDateFormatFull(String dateFormatFull) { + preferences.setDateFormatFull(dateFormatFull); + return this; + } + + public Builder withDateFormatNoSeconds(String dateFormatNoSeconds) { + preferences.setDateFormatNoSeconds(dateFormatNoSeconds); + return this; + } + + public Builder withDateFormatClock(String dateFormatClock) { + preferences.setDateFormatClock(dateFormatClock); + return this; + } + + public Builder withTimeFormat(TimeFormat timeFormat) { + preferences.setTimeFormat(timeFormat); + return this; + } + + public Builder withDecimalFormat(String decimalFormat) { + preferences.setDecimalFormat(decimalFormat); + return this; + } + + public Builder withFirstDay(int firstDay) { + preferences.setFirstDay(firstDay); + return this; + } + + public Builder withPlayerHeadImageUrl(String playerHeadImageUrl) { + preferences.setPlayerHeadImageUrl(playerHeadImageUrl); + return this; + } + + public Builder withTpsThresholds(GraphThresholds tpsThresholds) { + preferences.setTpsThresholds(tpsThresholds); + return this; + } + + public Builder withDiskThresholds(GraphThresholds diskThresholds) { + preferences.setDiskThresholds(diskThresholds); + return this; + } + + public Preferences build() {return preferences;} + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/TimeFormat.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/TimeFormat.java new file mode 100644 index 000000000..2f33ea4d5 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/preferences/TimeFormat.java @@ -0,0 +1,210 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.datatransfer.preferences; + +import java.util.Objects; + +/** + * @author AuroraLS3 + */ +public class TimeFormat { + private String year; + private String years; + private String month; + private String months; + private String day; + private String days; + private String hours; + private String minutes; + private String seconds; + private String zero; + + private TimeFormat() { + // Constructor is private for builder + } + + public static Builder builder() { + return new Builder(); + } + + public String getYear() { + return year; + } + + private void setYear(String year) { + this.year = year; + } + + public String getYears() { + return years; + } + + private void setYears(String years) { + this.years = years; + } + + public String getMonth() { + return month; + } + + private void setMonth(String month) { + this.month = month; + } + + public String getMonths() { + return months; + } + + private void setMonths(String months) { + this.months = months; + } + + public String getDay() { + return day; + } + + private void setDay(String day) { + this.day = day; + } + + public String getDays() { + return days; + } + + private void setDays(String days) { + this.days = days; + } + + public String getHours() { + return hours; + } + + private void setHours(String hours) { + this.hours = hours; + } + + public String getMinutes() { + return minutes; + } + + private void setMinutes(String minutes) { + this.minutes = minutes; + } + + public String getSeconds() { + return seconds; + } + + private void setSeconds(String seconds) { + this.seconds = seconds; + } + + public String getZero() { + return zero; + } + + private void setZero(String zero) { + this.zero = zero; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeFormat that = (TimeFormat) o; + return Objects.equals(getYear(), that.getYear()) && Objects.equals(getYears(), that.getYears()) && Objects.equals(getMonth(), that.getMonth()) && Objects.equals(getMonths(), that.getMonths()) && Objects.equals(getDay(), that.getDay()) && Objects.equals(getDays(), that.getDays()) && Objects.equals(getHours(), that.getHours()) && Objects.equals(getMinutes(), that.getMinutes()) && Objects.equals(getSeconds(), that.getSeconds()) && Objects.equals(getZero(), that.getZero()); + } + + @Override + public int hashCode() { + return Objects.hash(getYear(), getYears(), getMonth(), getMonths(), getDay(), getDays(), getHours(), getMinutes(), getSeconds(), getZero()); + } + + @Override + public String toString() { + return "TimeFormat{" + + "year='" + year + '\'' + + ", years='" + years + '\'' + + ", month='" + month + '\'' + + ", months='" + months + '\'' + + ", day='" + day + '\'' + + ", days='" + days + '\'' + + ", hours='" + hours + '\'' + + ", minutes='" + minutes + '\'' + + ", seconds='" + seconds + '\'' + + ", zero='" + zero + '\'' + + '}'; + } + + public static final class Builder { + private final TimeFormat timeFormat; + + private Builder() {timeFormat = new TimeFormat();} + + public Builder withYear(String year) { + timeFormat.setYear(year); + return this; + } + + public Builder withYears(String years) { + timeFormat.setYears(years); + return this; + } + + public Builder withMonth(String month) { + timeFormat.setMonth(month); + return this; + } + + public Builder withMonths(String months) { + timeFormat.setMonths(months); + return this; + } + + public Builder withDay(String day) { + timeFormat.setDay(day); + return this; + } + + public Builder withDays(String days) { + timeFormat.setDays(days); + return this; + } + + public Builder withHours(String hours) { + timeFormat.setHours(hours); + return this; + } + + public Builder withMinutes(String minutes) { + timeFormat.setMinutes(minutes); + return this; + } + + public Builder withSeconds(String seconds) { + timeFormat.setSeconds(seconds); + return this; + } + + public Builder withZero(String zero) { + timeFormat.setZero(zero); + return this; + } + + public TimeFormat build() {return timeFormat;} + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java index 43a68d3f5..894e92f37 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java @@ -20,7 +20,6 @@ import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plan.settings.config.paths.ExportSettings; -import com.djrapitops.plan.settings.config.paths.PluginSettings; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; @@ -86,11 +85,6 @@ public class ExportScheduler extends PluginRunnable { } private void scheduleReactExport() { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND) || - config.isFalse(ExportSettings.SERVER_PAGE) && - config.isFalse(ExportSettings.PLAYER_PAGES) && - config.isFalse(ExportSettings.PLAYERS_PAGE)) {return;} - runnableFactory.create( new ExportTask(exporter, Exporter::exportReact, errorLogger) ).runTaskLaterAsynchronously(TimeAmount.toTicks(5, TimeUnit.SECONDS)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java index 0bfd13b7f..4752ddd11 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java @@ -16,23 +16,16 @@ */ package com.djrapitops.plan.delivery.export; -import com.djrapitops.plan.delivery.rendering.pages.Page; -import com.djrapitops.plan.delivery.rendering.pages.PageFactory; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException; import com.djrapitops.plan.delivery.web.resolver.request.Request; -import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver; import com.djrapitops.plan.exceptions.WebUserAuthException; import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.file.PlanFiles; -import com.djrapitops.plan.storage.file.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; @@ -54,25 +47,19 @@ public class NetworkPageExporter extends FileExporter { private final PlanFiles files; private final PlanConfig config; private final DBSystem dbSystem; - private final PageFactory pageFactory; private final RootJSONResolver jsonHandler; - private final Theme theme; @Inject public NetworkPageExporter( PlanFiles files, PlanConfig config, DBSystem dbSystem, - PageFactory pageFactory, - RootJSONResolver jsonHandler, - Theme theme + RootJSONResolver jsonHandler ) { this.files = files; this.config = config; this.dbSystem = dbSystem; - this.pageFactory = pageFactory; this.jsonHandler = jsonHandler; - this.theme = theme; } /** @@ -89,35 +76,10 @@ public class NetworkPageExporter extends FileExporter { ExportPaths exportPaths = new ExportPaths(); exportPaths.put("./players", toRelativePathFromRoot("players")); - exportRequiredResources(exportPaths, toDirectory); exportJSON(exportPaths, toDirectory, server); - exportHtml(exportPaths, toDirectory); exportReactRedirects(toDirectory); } - private void exportHtml(ExportPaths exportPaths, Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - Path to = toDirectory - .resolve("network") - .resolve("index.html"); - - Page page = pageFactory.networkPage(); - - // Fixes refreshingJsonRequest ignoring old data of export - String html = StringUtils.replaceEach(page.toHtml(), - new String[]{"loadPlayersOnlineGraph, 'network-overview', true);", - "· Performance", - "" - }, - new String[]{"loadPlayersOnlineGraph, 'network-overview');", - "· Performance (Unavailable with Export)", - "" - }); - - export(to, exportPaths.resolveExportPaths(html)); - } - public static String[] getRedirections() { return new String[]{ "network", @@ -134,8 +96,6 @@ public class NetworkPageExporter extends FileExporter { } private void exportReactRedirects(Path toDirectory) throws IOException { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) return; - exportReactRedirects(toDirectory, files, config, getRedirections()); } @@ -170,7 +130,8 @@ public class NetworkPageExporter extends FileExporter { "sessions", "extensionData?server=" + serverUUID, "retention", - "joinAddresses" + "joinAddresses", + "playersTable" ); } @@ -210,75 +171,6 @@ public class NetworkPageExporter extends FileExporter { } } - private void exportRequiredResources(ExportPaths exportPaths, Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - exportResources(exportPaths, toDirectory, - "./img/Flaticon_circle.png", - "./css/sb-admin-2.css", - "./css/style.css", - "./css/noauth.css", - "./vendor/datatables/datatables.min.js", - "./vendor/datatables/datatables.min.css", - "./vendor/highcharts/modules/map.js", - "./vendor/highcharts/mapdata/world.js", - "./vendor/highcharts/modules/drilldown.js", - "./vendor/highcharts/highcharts.js", - "./vendor/highcharts/modules/no-data-to-display.js", - "./vendor/masonry/masonry.pkgd.min.js", - "./vendor/fontawesome-free/css/all.min.css", - "./vendor/fontawesome-free/webfonts/fa-brands-400.eot", - "./vendor/fontawesome-free/webfonts/fa-brands-400.ttf", - "./vendor/fontawesome-free/webfonts/fa-brands-400.woff", - "./vendor/fontawesome-free/webfonts/fa-brands-400.woff2", - "./vendor/fontawesome-free/webfonts/fa-regular-400.eot", - "./vendor/fontawesome-free/webfonts/fa-regular-400.ttf", - "./vendor/fontawesome-free/webfonts/fa-regular-400.woff", - "./vendor/fontawesome-free/webfonts/fa-regular-400.woff2", - "./vendor/fontawesome-free/webfonts/fa-solid-900.eot", - "./vendor/fontawesome-free/webfonts/fa-solid-900.ttf", - "./vendor/fontawesome-free/webfonts/fa-solid-900.woff", - "./vendor/fontawesome-free/webfonts/fa-solid-900.woff2", - "./js/domUtils.js", - "./js/sb-admin-2.js", - "./js/xmlhttprequests.js", - "./js/color-selector.js", - "./js/sessionAccordion.js", - "./js/pingTable.js", - "./js/graphs.js", - "./js/network-values.js" - ); - } - - private void exportResources(ExportPaths exportPaths, Path toDirectory, String... resourceNames) throws IOException { - for (String resourceName : resourceNames) { - String nonRelativePath = toNonRelativePath(resourceName); - exportResource(toDirectory, nonRelativePath); - exportPaths.put(resourceName, toRelativePathFromRoot(nonRelativePath)); - } - } - - private void exportResource(Path toDirectory, String resourceName) throws IOException { - WebResource resource = ResourceService.getInstance().getResource("Plan", resourceName, - () -> files.getResourceFromJar("web/" + resourceName).asWebResource()); - Path to = toDirectory.resolve(resourceName); - - if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) { - export(to, theme.replaceThemeColors(resource.asString())); - } else if ("js/network-values.js".equalsIgnoreCase(resourceName) || "js/sessionAccordion.js".equalsIgnoreCase(resourceName)) { - String relativePlayerLink = toRelativePathFromRoot("player"); - String relativeServerLink = toRelativePathFromRoot("server/"); - export(to, StringUtils.replaceEach(resource.asString(), - new String[]{"../player", "./player", "./server/", "server/"}, - new String[]{relativePlayerLink, relativePlayerLink, relativeServerLink, relativeServerLink} - )); - } else if (Resource.isTextResource(resourceName)) { - export(to, resource.asString()); - } else { - export(to, resource); - } - } - private String toRelativePathFromRoot(String resourceName) { // Network html is exported at /network//index.html or /server/index.html return "../" + toNonRelativePath(resourceName); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java index 525bea3a2..2db3a0412 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java @@ -16,25 +16,16 @@ */ package com.djrapitops.plan.delivery.export; -import com.djrapitops.plan.delivery.domain.container.PlayerContainer; -import com.djrapitops.plan.delivery.rendering.pages.Page; -import com.djrapitops.plan.delivery.rendering.pages.PageFactory; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException; import com.djrapitops.plan.delivery.web.resolver.request.Request; -import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver; import com.djrapitops.plan.exceptions.WebUserAuthException; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; -import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; import com.djrapitops.plan.storage.file.PlanFiles; -import com.djrapitops.plan.storage.file.Resource; import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; @@ -56,25 +47,19 @@ public class PlayerPageExporter extends FileExporter { private final PlanFiles files; private final PlanConfig config; private final DBSystem dbSystem; - private final PageFactory pageFactory; private final RootJSONResolver jsonHandler; - private final Theme theme; @Inject public PlayerPageExporter( PlanFiles files, PlanConfig config, DBSystem dbSystem, - PageFactory pageFactory, - RootJSONResolver jsonHandler, - Theme theme + RootJSONResolver jsonHandler ) { this.files = files; this.config = config; this.dbSystem = dbSystem; - this.pageFactory = pageFactory; this.jsonHandler = jsonHandler; - this.theme = theme; } public static String[] getRedirections(UUID playerUUID) { @@ -106,33 +91,14 @@ public class PlayerPageExporter extends FileExporter { ExportPaths exportPaths = new ExportPaths(); exportPaths.put("../network", toRelativePathFromRoot("network")); exportPaths.put("../server/", toRelativePathFromRoot("server")); - exportRequiredResources(exportPaths, toDirectory); Path playerDirectory = toDirectory.resolve("player/" + toFileName(playerUUID.toString())); exportJSON(exportPaths, playerDirectory, playerUUID); - exportHtml(exportPaths, playerDirectory, playerUUID); exportReactRedirects(toDirectory, playerUUID); exportPaths.clear(); } - private void exportHtml(ExportPaths exportPaths, Path playerDirectory, UUID playerUUID) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - Path to = playerDirectory.resolve("index.html"); - - try { - Database db = dbSystem.getDatabase(); - PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); - Page page = pageFactory.playerPage(player); - export(to, exportPaths.resolveExportPaths(page.toHtml())); - } catch (IllegalStateException notFound) { - throw new NotFoundException(notFound.getMessage()); - } - } - private void exportReactRedirects(Path toDirectory, UUID playerUUID) throws IOException { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) return; - exportReactRedirects(toDirectory, files, config, getRedirections(playerUUID)); } @@ -163,70 +129,6 @@ public class PlayerPageExporter extends FileExporter { } } - private void exportRequiredResources(ExportPaths exportPaths, Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - // Style - exportResources(exportPaths, toDirectory, - "../img/Flaticon_circle.png", - "../css/sb-admin-2.css", - "../css/style.css", - "../css/noauth.css", - "../vendor/datatables/datatables.min.js", - "../vendor/datatables/datatables.min.css", - "../vendor/highcharts/modules/map.js", - "../vendor/highcharts/mapdata/world.js", - "../vendor/highcharts/modules/drilldown.js", - "../vendor/highcharts/highcharts.js", - "../vendor/highcharts/modules/no-data-to-display.js", - "../vendor/fullcalendar/fullcalendar.min.css", - "../vendor/momentjs/moment.js", - "../vendor/masonry/masonry.pkgd.min.js", - "../vendor/fullcalendar/fullcalendar.min.js", - "../vendor/fontawesome-free/css/all.min.css", - "../vendor/fontawesome-free/webfonts/fa-brands-400.eot", - "../vendor/fontawesome-free/webfonts/fa-brands-400.ttf", - "../vendor/fontawesome-free/webfonts/fa-brands-400.woff", - "../vendor/fontawesome-free/webfonts/fa-brands-400.woff2", - "../vendor/fontawesome-free/webfonts/fa-regular-400.eot", - "../vendor/fontawesome-free/webfonts/fa-regular-400.ttf", - "../vendor/fontawesome-free/webfonts/fa-regular-400.woff", - "../vendor/fontawesome-free/webfonts/fa-regular-400.woff2", - "../vendor/fontawesome-free/webfonts/fa-solid-900.eot", - "../vendor/fontawesome-free/webfonts/fa-solid-900.ttf", - "../vendor/fontawesome-free/webfonts/fa-solid-900.woff", - "../vendor/fontawesome-free/webfonts/fa-solid-900.woff2", - "../js/sb-admin-2.js", - "../js/xmlhttprequests.js", - "../js/color-selector.js", - "../js/sessionAccordion.js", - "../js/graphs.js", - "../js/player-values.js" - ); - } - - private void exportResources(ExportPaths exportPaths, Path toDirectory, String... resourceNames) throws IOException { - for (String resourceName : resourceNames) { - String nonRelativePath = toNonRelativePath(resourceName); - exportResource(toDirectory, nonRelativePath); - exportPaths.put(resourceName, toRelativePathFromRoot(nonRelativePath)); - } - } - - private void exportResource(Path toDirectory, String resourceName) throws IOException { - WebResource resource = ResourceService.getInstance().getResource("Plan", resourceName, - () -> files.getResourceFromJar("web/" + resourceName).asWebResource()); - Path to = toDirectory.resolve(resourceName); - - if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) { - export(to, theme.replaceThemeColors(resource.asString())); - } else if (Resource.isTextResource(resourceName)) { - export(to, resource.asString()); - } else { - export(to, resource); - } - } - private String toRelativePathFromRoot(String resourceName) { // Player html is exported at /player//index.html return "../../" + toNonRelativePath(resourceName); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java index df5786f50..6edf89c77 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java @@ -16,23 +16,16 @@ */ package com.djrapitops.plan.delivery.export; -import com.djrapitops.plan.delivery.rendering.pages.Page; -import com.djrapitops.plan.delivery.rendering.pages.PageFactory; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException; import com.djrapitops.plan.delivery.web.resolver.request.Request; -import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver; import com.djrapitops.plan.exceptions.WebUserAuthException; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.file.PlanFiles; -import com.djrapitops.plan.storage.file.Resource; import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; @@ -53,28 +46,23 @@ public class PlayersPageExporter extends FileExporter { private final PlanFiles files; private final PlanConfig config; private final DBSystem dbSystem; - private final PageFactory pageFactory; private final RootJSONResolver jsonHandler; - private final Theme theme; private final ServerInfo serverInfo; private final ExportPaths exportPaths; + private static final String PLAYERS_TABLE = "playersTable"; @Inject public PlayersPageExporter( PlanFiles files, PlanConfig config, DBSystem dbSystem, - PageFactory pageFactory, RootJSONResolver jsonHandler, - Theme theme, ServerInfo serverInfo ) { this.files = files; this.config = config; this.dbSystem = dbSystem; - this.pageFactory = pageFactory; this.jsonHandler = jsonHandler; - this.theme = theme; this.serverInfo = serverInfo; exportPaths = new ExportPaths(); @@ -85,121 +73,42 @@ public class PlayersPageExporter extends FileExporter { if (dbState == Database.State.CLOSED || dbState == Database.State.CLOSING) return; exportPaths.put("href=\"/\"", "href=\"" + toRelativePathFromRoot(serverInfo.getServer().isProxy() ? "network" : "server") + '"'); - exportRequiredResources(toDirectory); exportJSON(toDirectory); - exportHtml(toDirectory); exportReactRedirects(toDirectory); exportPaths.clear(); } - private void exportHtml(Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - Path to = toDirectory - .resolve("players") - .resolve("index.html"); - - Page page = pageFactory.playersPage(); - - // Fixes refreshingJsonRequest ignoring old data of export - String html = StringUtils.replaceEach(page.toHtml(), - new String[]{ - "}, 'playerlist', true);", - "" - }, - new String[]{ - "}, 'playerlist');", - "" - }); - - export(to, exportPaths.resolveExportPaths(html)); - } - private void exportReactRedirects(Path toDirectory) throws IOException { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) return; - String[] redirections = {"players"}; exportReactRedirects(toDirectory, files, config, redirections); } private void exportJSON(Path toDirectory) throws IOException { - Response response = getJSONResponse("players") + Response response = getJSONResponse() .orElseThrow(() -> new NotFoundException("players page was not properly exported: not found")); - String jsonResourceName = toFileName(toJSONResourceName("players")) + ".json"; + String jsonResourceName = toFileName(toJSONResourceName()) + ".json"; export(toDirectory.resolve("data").resolve(jsonResourceName), // Replace ../player in urls to fix player page links StringUtils.replace(response.getAsString(), "../player", toRelativePathFromRoot("player")) ); - exportPaths.put("./v1/players", toRelativePathFromRoot("data/" + jsonResourceName)); + exportPaths.put("./v1/" + PLAYERS_TABLE, toRelativePathFromRoot("data/" + jsonResourceName)); } - private String toJSONResourceName(String resource) { - return StringUtils.replaceEach(resource, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); + private String toJSONResourceName() { + return StringUtils.replaceEach(PLAYERS_TABLE, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); } - private Optional getJSONResponse(String resource) { + private Optional getJSONResponse() { try { - return jsonHandler.getResolver().resolve(new Request("GET", "/v1/" + resource, null, Collections.emptyMap())); + return jsonHandler.getResolver().resolve(new Request("GET", "/v1/" + PLAYERS_TABLE, null, Collections.emptyMap())); } catch (WebUserAuthException e) { // The rest of the exceptions should not be thrown throw new IllegalStateException("Unexpected exception thrown: " + e.toString(), e); } } - private void exportRequiredResources(Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - // Style - exportResources(toDirectory, - "img/Flaticon_circle.png", - "css/sb-admin-2.css", - "css/style.css", - "css/noauth.css", - "vendor/datatables/datatables.min.js", - "vendor/datatables/datatables.min.css", - "vendor/fontawesome-free/css/all.min.css", - "vendor/fontawesome-free/webfonts/fa-brands-400.eot", - "vendor/fontawesome-free/webfonts/fa-brands-400.ttf", - "vendor/fontawesome-free/webfonts/fa-brands-400.woff", - "vendor/fontawesome-free/webfonts/fa-brands-400.woff2", - "vendor/fontawesome-free/webfonts/fa-regular-400.eot", - "vendor/fontawesome-free/webfonts/fa-regular-400.ttf", - "vendor/fontawesome-free/webfonts/fa-regular-400.woff", - "vendor/fontawesome-free/webfonts/fa-regular-400.woff2", - "vendor/fontawesome-free/webfonts/fa-solid-900.eot", - "vendor/fontawesome-free/webfonts/fa-solid-900.ttf", - "vendor/fontawesome-free/webfonts/fa-solid-900.woff", - "vendor/fontawesome-free/webfonts/fa-solid-900.woff2", - "js/sb-admin-2.js", - "js/xmlhttprequests.js", - "js/color-selector.js" - ); - } - - private void exportResources(Path toDirectory, String... resourceNames) throws IOException { - for (String resourceName : resourceNames) { - exportResource(toDirectory, resourceName); - } - } - - private void exportResource(Path toDirectory, String resourceName) throws IOException { - WebResource resource = ResourceService.getInstance().getResource("Plan", resourceName, - () -> files.getResourceFromJar("web/" + resourceName).asWebResource()); - Path to = toDirectory.resolve(resourceName); - - if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) { - export(to, theme.replaceThemeColors(resource.asString())); - } else if (Resource.isTextResource(resourceName)) { - export(to, resource.asString()); - } else { - export(to, resource); - } - - exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); - } - private String toRelativePathFromRoot(String resourceName) { // Players html is exported at /players/index.html or /server/index.html return "../" + toNonRelativePath(resourceName); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ReactExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ReactExporter.java index 4ed952f01..518242031 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ReactExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ReactExporter.java @@ -84,6 +84,7 @@ public class ReactExporter extends FileExporter { exportJson(toDirectory, "metadata"); exportJson(toDirectory, "version"); exportJson(toDirectory, "networkMetadata"); + exportJson(toDirectory, "preferences"); } private void exportLocaleJson(Path toDirectory) throws IOException { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java index 816652a55..08fa0aa43 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java @@ -16,25 +16,18 @@ */ package com.djrapitops.plan.delivery.export; -import com.djrapitops.plan.delivery.rendering.pages.Page; -import com.djrapitops.plan.delivery.rendering.pages.PageFactory; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException; import com.djrapitops.plan.delivery.web.resolver.request.Request; -import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver; import com.djrapitops.plan.exceptions.WebUserAuthException; import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.file.PlanFiles; -import com.djrapitops.plan.storage.file.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; @@ -55,10 +48,8 @@ public class ServerPageExporter extends FileExporter { private final PlanFiles files; private final PlanConfig config; - private final PageFactory pageFactory; private final DBSystem dbSystem; private final RootJSONResolver jsonHandler; - private final Theme theme; private final ServerInfo serverInfo; private final ExportPaths exportPaths; @@ -67,18 +58,14 @@ public class ServerPageExporter extends FileExporter { public ServerPageExporter( PlanFiles files, PlanConfig config, - PageFactory pageFactory, DBSystem dbSystem, RootJSONResolver jsonHandler, - Theme theme, ServerInfo serverInfo // To know if current server is a Proxy ) { this.files = files; this.config = config; - this.pageFactory = pageFactory; this.dbSystem = dbSystem; this.jsonHandler = jsonHandler; - this.theme = theme; this.serverInfo = serverInfo; exportPaths = new ExportPaths(); @@ -97,41 +84,11 @@ public class ServerPageExporter extends FileExporter { if (dbState == Database.State.CLOSED || dbState == Database.State.CLOSING) return; exportPaths.put("../network", toRelativePathFromRoot("network")); - exportRequiredResources(toDirectory); exportJSON(toDirectory, server); - exportHtml(toDirectory, server); exportReactRedirects(toDirectory, server.getUuid()); exportPaths.clear(); } - private void exportHtml(Path toDirectory, Server server) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - ServerUUID serverUUID = server.getUuid(); - Path to = toDirectory - .resolve(serverInfo.getServer().isProxy() ? "server/" + toFileName(server.getName()) : "server") - .resolve("index.html"); - - Page page = pageFactory.serverPage(serverUUID); - - // Fixes refreshingJsonRequest ignoring old data of export - String html = StringUtils.replaceEach(page.toHtml(), - new String[]{ - "loadOptimizedPerformanceGraph, 'performance', true);", - "loadserverCalendar, 'online-activity-overview', true);", - "}, 'playerlist', true);", - "" - }, - new String[]{ - "loadOptimizedPerformanceGraph, 'performance');", - "loadserverCalendar, 'online-activity-overview');", - "}, 'playerlist');", - "" - }); - - export(to, exportPaths.resolveExportPaths(html)); - } - public static String[] getRedirections(ServerUUID serverUUID) { String server = "server/"; return new String[]{ @@ -151,8 +108,6 @@ public class ServerPageExporter extends FileExporter { } private void exportReactRedirects(Path toDirectory, ServerUUID serverUUID) throws IOException { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) return; - exportReactRedirects(toDirectory, files, config, getRedirections(serverUUID)); } @@ -186,7 +141,7 @@ public class ServerPageExporter extends FileExporter { "graph?type=joinAddressByDay&server=" + serverUUID, "graph?type=serverCalendar&server=" + serverUUID, "graph?type=punchCard&server=" + serverUUID, - "players?server=" + serverUUID, + "playersTable?server=" + serverUUID, "kills?server=" + serverUUID, "pingTable?server=" + serverUUID, "sessions?server=" + serverUUID, @@ -233,71 +188,6 @@ public class ServerPageExporter extends FileExporter { } } - private void exportRequiredResources(Path toDirectory) throws IOException { - if (config.isFalse(PluginSettings.LEGACY_FRONTEND)) return; - - // Style - exportResources(toDirectory, - "../img/Flaticon_circle.png", - "../css/sb-admin-2.css", - "../css/style.css", - "../vendor/datatables/datatables.min.js", - "../vendor/datatables/datatables.min.css", - "../vendor/highcharts/modules/map.js", - "../vendor/highcharts/mapdata/world.js", - "../vendor/highcharts/modules/drilldown.js", - "../vendor/highcharts/highcharts.js", - "../vendor/highcharts/modules/no-data-to-display.js", - "../vendor/fullcalendar/fullcalendar.min.css", - "../vendor/momentjs/moment.js", - "../vendor/masonry/masonry.pkgd.min.js", - "../vendor/fullcalendar/fullcalendar.min.js", - "../vendor/fontawesome-free/css/all.min.css", - "../vendor/fontawesome-free/webfonts/fa-brands-400.eot", - "../vendor/fontawesome-free/webfonts/fa-brands-400.ttf", - "../vendor/fontawesome-free/webfonts/fa-brands-400.woff", - "../vendor/fontawesome-free/webfonts/fa-brands-400.woff2", - "../vendor/fontawesome-free/webfonts/fa-regular-400.eot", - "../vendor/fontawesome-free/webfonts/fa-regular-400.ttf", - "../vendor/fontawesome-free/webfonts/fa-regular-400.woff", - "../vendor/fontawesome-free/webfonts/fa-regular-400.woff2", - "../vendor/fontawesome-free/webfonts/fa-solid-900.eot", - "../vendor/fontawesome-free/webfonts/fa-solid-900.ttf", - "../vendor/fontawesome-free/webfonts/fa-solid-900.woff", - "../vendor/fontawesome-free/webfonts/fa-solid-900.woff2", - "../js/domUtils.js", - "../js/sb-admin-2.js", - "../js/xmlhttprequests.js", - "../js/color-selector.js", - "../js/sessionAccordion.js", - "../js/pingTable.js", - "../js/graphs.js", - "../js/server-values.js" - ); - } - - private void exportResources(Path toDirectory, String... resourceNames) throws IOException { - for (String resourceName : resourceNames) { - String nonRelativePath = toNonRelativePath(resourceName); - exportResource(toDirectory, nonRelativePath); - exportPaths.put(resourceName, toRelativePathFromRoot(nonRelativePath)); - } - } - - private void exportResource(Path toDirectory, String resourceName) throws IOException { - WebResource resource = ResourceService.getInstance().getResource("Plan", resourceName, - () -> files.getResourceFromJar("web/" + resourceName).asWebResource()); - Path to = toDirectory.resolve(resourceName); - - if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) { - export(to, theme.replaceThemeColors(resource.asString())); - } else if (Resource.isTextResource(resourceName)) { - export(to, resource.asString()); - } else { - export(to, resource); - } - } - private String toRelativePathFromRoot(String resourceName) { // Server html is exported at /server//index.html or /server/index.html return (serverInfo.getServer().isProxy() ? "../../" : "../") + toNonRelativePath(resourceName); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java index 93ac6208e..84d5c682a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.delivery.formatting; import com.djrapitops.plan.delivery.domain.DateHolder; import com.djrapitops.plan.delivery.formatting.time.*; +import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plan.settings.locale.Locale; @@ -160,6 +161,20 @@ public class Formatters { return value -> byteSizeFormatter.apply((double) value); } + public Formatter getNumberFormatter(FormatType type) { + switch (type) { + case DATE_SECOND: + return secondLong(); + case DATE_YEAR: + return yearLong(); + case TIME_MILLISECONDS: + return timeAmount(); + case NONE: + default: + return Object::toString; + } + } + static class Holder { static final AtomicReference formatters = new AtomicReference<>(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Contributors.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Contributors.java index 09f74895d..6d84767a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Contributors.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Contributors.java @@ -113,31 +113,12 @@ public class Contributors { // Static method class } - public static String generateContributorHtml() { - int estimatedLength = CONTRIBUTOR_ARRAY.length * 40 + 50; - StringBuilder html = new StringBuilder(estimatedLength); - Arrays.stream(CONTRIBUTOR_ARRAY).sorted() - .forEach(contributor -> contributor.appendHtml(html)); - return html.toString(); - } - public static List getContributors() { return Arrays.stream(CONTRIBUTOR_ARRAY).sorted().collect(Collectors.toList()); } enum For { - CODE("fa-code"), - LANG("fa-language"); - - private final String icon; - - For(String icon) { - this.icon = icon; - } - - String toHtml() { - return " "; - } + CODE, LANG } private static class Contributor implements Comparable { @@ -149,15 +130,6 @@ public class Contributors { this.contributed = contributed; } - public void appendHtml(StringBuilder html) { - html.append("

  • ") - .append(name); - for (For contribution : contributed) { - html.append(contribution.toHtml()); - } - html.append("
  • "); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java index c9ae1129d..382d482da 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java @@ -18,7 +18,6 @@ package com.djrapitops.plan.delivery.rendering.html; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; -import org.apache.commons.text.TextStringBuilder; import java.io.Serializable; import java.net.URLEncoder; @@ -31,39 +30,8 @@ import java.util.Map; */ public enum Html { - COLOR_0(""), - COLOR_1(""), - COLOR_2(""), - COLOR_3(""), - COLOR_4(""), - COLOR_5(""), - COLOR_6(""), - COLOR_7(""), - COLOR_8(""), - COLOR_9(""), - COLOR_A(""), - COLOR_B(""), - COLOR_C(""), - COLOR_D(""), - COLOR_E(""), - COLOR_F(""), - - SPAN("${0}"), LINK("${1}"), - LINK_EXTERNAL("${1}"), - - BACK_BUTTON_NETWORK("" + - "" + - "" + - "" + - "Network page" + - ""), - BACK_BUTTON_SERVER("" + - "" + - "" + - "" + - "Server page" + - ""); + LINK_EXTERNAL("${1}"); private final String html; @@ -71,105 +39,6 @@ public enum Html { this.html = html; } - /** - * Changes Minecraft color codes to HTML span elements with correct color class assignments. - * - * @param string String to replace Minecraft color codes from - * @return String with span elements. - */ - public static String swapColorCodesToSpan(String string) { - return swapColorCodesToSpan(string, string.contains("§") ? "§" : "§"); - } - - private static String swapColorCodesToSpan(String string, String splitWith) { - if (string == null) return null; - if (!string.contains(splitWith)) return string; - - Html[] replacer = new Html[]{ - Html.COLOR_0, Html.COLOR_1, Html.COLOR_2, Html.COLOR_3, - Html.COLOR_4, Html.COLOR_5, Html.COLOR_6, Html.COLOR_7, - Html.COLOR_8, Html.COLOR_9, Html.COLOR_A, Html.COLOR_B, - Html.COLOR_C, Html.COLOR_D, Html.COLOR_E, Html.COLOR_F - }; - Map colorMap = new HashMap<>(); - - for (Html html : replacer) { - colorMap.put(Character.toLowerCase(html.name().charAt(6)), html.create()); - colorMap.put('k', ""); - colorMap.put('l', ""); - colorMap.put('m', ""); - colorMap.put('n', ""); - colorMap.put('o', ""); - } - - StringBuilder result = new StringBuilder(string.length()); - String[] split = string.split(splitWith); - // Skip first part if it does not start with § - boolean skipFirst = !string.startsWith(splitWith); - - int placedSpans = 0; - int hexNumbersLeft = 0; - - for (String part : split) { - if (part.isEmpty()) { - continue; - } - if (skipFirst) { - result.append(part); - skipFirst = false; - continue; - } - - char colorChar = part.charAt(0); - // Deal with hex colors - if (hexNumbersLeft > 1) { - result.append(colorChar); - hexNumbersLeft--; - continue; - } else if (hexNumbersLeft == 1) { - result.append(colorChar).append(";\">").append(part.substring(1)); - hexNumbersLeft--; - continue; - } - - if (colorChar == 'r') { - appendEndTags(result, placedSpans); - placedSpans = 0; // Colors were reset - result.append(part.substring(1)); - continue; - } - - // Deal with hex colors - if (colorChar == 'x') { - result.append("".repeat(Math.max(0, placedSpans))); - } - /** * @param replacements The replacement Strings * @return The HTML String diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/DynamicHtmlTable.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/DynamicHtmlTable.java deleted file mode 100644 index d96f8351d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/DynamicHtmlTable.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.html.structure; - -import com.djrapitops.plan.extension.table.Table; -import org.apache.commons.lang3.math.NumberUtils; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.List; - -/** - * @deprecated Table html generation is to be done in frontend in the future. - */ -@Deprecated(since = "5.5") -public class DynamicHtmlTable implements HtmlTable { - private final Header[] headers; - private final List rows; - - private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM d yyyy, HH:mm"); - - public DynamicHtmlTable(Header[] headers, List rows) { - this.headers = headers; - this.rows = rows; - } - - public DynamicHtmlTable(Table table) { - this(HtmlTable.mapToHeaders(table), table.getRows()); - } - - @Override - public String toHtml() { - return "" + - buildTableHeader() + - buildTableBody() + - "
    "; - } - - private String buildTableHeader() { - StringBuilder builtHeader = new StringBuilder(""); - for (Header header : headers) { - builtHeader.append("") - .append(header.getIcon().toHtml()) - .append(' ') - .append(header.getText()) - .append(""); - } - builtHeader.append(""); - return builtHeader.toString(); - } - - private String buildTableBody() { - StringBuilder builtBody = new StringBuilder(); - builtBody.append(""); - if (rows.isEmpty()) { - appendRow(builtBody, "No Data"); - } - for (Object[] row : rows) { - appendRow(builtBody, row); - } - return builtBody.append("").toString(); - } - - private void appendRow(StringBuilder builtBody, Object... row) { - int headerLength = row.length - 1; - builtBody.append(""); - for (int i = 0; i < headers.length; i++) { - try { - if (i > headerLength) { - builtBody.append("-"); - } else { - appendValue(builtBody, row[i]); - } - builtBody.append(""); - } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { - throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e); - } - } - builtBody.append(""); - } - - private void appendValue(StringBuilder builtBody, Object value) { - String valueString = value != null ? value.toString() : "-"; - try { - long time = dateFormat.parse(valueString).getTime(); - builtBody.append("").append(valueString); - } catch (ParseException e) { - if (NumberUtils.isParsable(valueString)) { - builtBody.append("").append(valueString); - } else { - // Removes non numbers from the value - String numbersInValue = valueString.replaceAll("\\D", ""); - if (!numbersInValue.isEmpty()) { - builtBody.append("").append(valueString); - } else { - builtBody.append("").append(valueString); - } - } - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTable.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTable.java deleted file mode 100644 index f66f2e401..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTable.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.html.structure; - -import com.djrapitops.plan.delivery.domain.datatransfer.extension.TableCellDto; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.rendering.html.Html; -import com.djrapitops.plan.delivery.rendering.html.icon.Color; -import com.djrapitops.plan.delivery.rendering.html.icon.Icon; -import com.djrapitops.plan.extension.table.Table; -import com.djrapitops.plan.extension.table.TableColumnFormat; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * @deprecated Table html generation is to be done in frontend in the future. - */ -@Deprecated(since = "5.5") -public interface HtmlTable { - - static HtmlTable fromExtensionTable(Table table, com.djrapitops.plan.extension.icon.Color tableColor) { - return fromExtensionTable(table, Color.getByName(tableColor.name()).orElse(Color.NONE)); - } - - static HtmlTable fromExtensionTable(Table table, Color tableColor) { - if (table.getRows().size() > 10) { - return new DynamicHtmlTable(table); - } else { - return new HtmlTableWithColoredHeader(table, tableColor); - } - } - - static Header[] mapToHeaders(Table table) { - ArrayList
    headers = new ArrayList<>(); - - com.djrapitops.plan.extension.icon.Icon[] icons = table.getIcons(); - String[] columns = table.getColumns(); - for (int i = 0; i < columns.length; i++) { - String column = columns[i]; - if (column == null) { - break; - } - headers.add(new Header(Icon.fromExtensionIcon(icons[i]), column)); - } - - return headers.toArray(new Header[0]); - } - - static List mapToRows(List rows, TableColumnFormat[] tableColumnFormats) { - return rows.stream() - .map(row -> { - List mapped = new ArrayList<>(row.length); - for (int i = 0; i < row.length; i++) { - Object value = row[i]; - if (value == null) { - mapped.add(null); - } else { - TableColumnFormat format = tableColumnFormats[i]; - mapped.add(new TableCellDto(applyFormat(format, value), value)); - } - } - return mapped.toArray(new TableCellDto[0]); - }) - .collect(Collectors.toList()); - } - - static String applyFormat(TableColumnFormat format, Object value) { - try { - switch (format) { - case TIME_MILLISECONDS: - return Formatters.getInstance().timeAmount().apply(Long.parseLong(value.toString())); - case DATE_YEAR: - return Formatters.getInstance().yearLong().apply(Long.parseLong(value.toString())); - case DATE_SECOND: - return Formatters.getInstance().secondLong().apply(Long.parseLong(value.toString())); - case PLAYER_NAME: - return Html.LINK.create("../player/" + Html.encodeToURL(Html.swapColorCodesToSpan(value.toString()))); - default: - return Html.swapColorCodesToSpan(value.toString()); - } - } catch (Exception e) { - return Objects.toString(value); - } - } - - String toHtml(); - - class Header { - private final Icon icon; - private final String text; - - public Header(Icon icon, String text) { - this.icon = icon; - this.text = text; - } - - public Icon getIcon() { - return icon; - } - - public String getText() { - return text; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTableWithColoredHeader.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTableWithColoredHeader.java deleted file mode 100644 index a812d942b..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/HtmlTableWithColoredHeader.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.html.structure; - -import com.djrapitops.plan.delivery.domain.datatransfer.extension.TableCellDto; -import com.djrapitops.plan.delivery.rendering.html.icon.Color; -import com.djrapitops.plan.extension.table.Table; - -import java.util.List; - -/** - * @deprecated Table html generation is to be done in frontend in the future. - */ -@Deprecated(since = "5.5") -public class HtmlTableWithColoredHeader implements HtmlTable { - private final Header[] headers; - private final Color headerColor; - private final List rows; - - public HtmlTableWithColoredHeader(Header[] headers, Color headerColor, List rows) { - this.headers = headers; - this.headerColor = headerColor; - this.rows = rows; - } - - public HtmlTableWithColoredHeader(Table table, Color headerColor) { - this(HtmlTable.mapToHeaders(table), headerColor, HtmlTable.mapToRows(table.getRows(), table.getTableColumnFormats())); - } - - @Override - public String toHtml() { - return "
    " + - "" + - buildTableHeader() + - buildTableBody() + - "
    " + - "
    "; - } - - private String buildTableHeader() { - StringBuilder builtHeader = new StringBuilder(""); - for (Header header : headers) { - builtHeader.append("") - .append(header.getIcon().toHtml()) - .append(' ') - .append(header.getText()) - .append(""); - } - builtHeader.append(""); - return builtHeader.toString(); - } - - private String buildTableBody() { - StringBuilder builtBody = new StringBuilder(); - builtBody.append(""); - if (rows.isEmpty()) { - appendRow(builtBody, new TableCellDto("No Data")); - } - for (TableCellDto[] row : rows) { - appendRow(builtBody, row); - } - return builtBody.append("").toString(); - } - - private void appendRow(StringBuilder builtBody, TableCellDto... row) { - int headerLength = row.length - 1; - builtBody.append(""); - for (int i = 0; i < headers.length; i++) { - try { - if (i > headerLength) { - builtBody.append("-"); - } else { - builtBody.append(""); - TableCellDto cell = row[i]; - builtBody.append(cell != null ? cell.getValue() : '-'); - } - builtBody.append(""); - } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { - throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e); - } - } - builtBody.append(""); - } - -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java deleted file mode 100644 index a709adffb..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.html.structure; - -import com.djrapitops.plan.delivery.rendering.html.icon.Icon; -import org.apache.commons.lang3.StringUtils; - -/** - * Html utility for creating navigation link html. - * - * @author AuroraLS3 - */ -public class NavLink { - - private final Icon icon; - private final String tabID; - private final String tabName; - private final boolean collapsed; - - private NavLink(Icon icon, String tabID, String tabName, boolean collapsed) { - this.icon = icon; - this.tabID = tabID; - this.tabName = tabName; - this.collapsed = collapsed; - } - - public static NavLink main(Icon icon, String tabName) { - return new NavLink(icon, null, tabName, false); - } - - public static NavLink main(Icon icon, String tabID, String tabName) { - return new NavLink(icon, tabID, tabName, false); - } - - public static NavLink collapsed(Icon icon, String tabName) { - return new NavLink(icon, null, tabName, true); - } - - public static NavLink collapsed(Icon icon, String tabID, String tabName) { - return new NavLink(icon, tabID, tabName, true); - } - - public static String format(String id) { - return StringUtils.replaceChars(StringUtils.lowerCase(id), ' ', '-'); - } - - public String toHtml() { - String usedId = getUsedTabId(); - if (collapsed) { - return "" + - icon.toHtml() + ' ' + - tabName + ""; - } - return "
  • " + - "" + - icon.toHtml() + - "" + tabName + "" + - "
  • "; - } - - private String getUsedTabId() { - return format(tabID != null ? tabID : tabName); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java deleted file mode 100644 index 9fe6186e8..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.html.structure; - -import com.djrapitops.plan.delivery.rendering.html.icon.Icon; -import org.apache.commons.lang3.RegExUtils; - -/** - * Represents a structural HTML element that has Tabs on the top. - * - * @author AuroraLS3 - */ -public class TabsElement { - - private final int pluginId; - private final Tab[] tabs; - - public TabsElement(int pluginId, Tab... tabs) { - this.pluginId = pluginId; - this.tabs = tabs; - } - - public String toHtmlFull() { - String[] navAndContent = toHtml(); - return navAndContent[0] + navAndContent[1]; - } - - public String[] toHtml() { - StringBuilder nav = new StringBuilder(); - StringBuilder content = new StringBuilder(); - - nav.append("
      "); - content.append("
      "); - boolean first = true; - for (Tab tab : tabs) { - String id = tab.getId(pluginId); - String navHtml = tab.getNavHtml(); - String contentHtml = tab.getContentHtml(); - - nav.append("
    • ") - .append(navHtml).append("
    • "); - content.append("
      ") - .append(contentHtml).append("
      "); - first = false; - } - content.append("
      "); - nav.append("
    "); - - return new String[]{nav.toString(), content.toString()}; - } - - public static class Tab { - - private final Icon icon; - private final String title; - private final String contentHtml; - - public Tab(Icon icon, String title, String contentHtml) { - this.icon = icon; - this.title = title; - this.contentHtml = contentHtml; - } - - public String getNavHtml() { - return icon.toHtml() + ' ' + title; - } - - public String getContentHtml() { - return contentHtml; - } - - public String getId(int pluginId) { - return "tab_" + pluginId + "_" + RegExUtils.removeAll(title, "[^a-zA-Z0-9]*").toLowerCase(); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java index 91e8faae7..099c75f9e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java @@ -96,7 +96,7 @@ public class JSONFactory { this.formatters = formatters; } - public Map serverPlayersTableJSON(ServerUUID serverUUID) { + public PlayersTableJSONCreator serverPlayersTableJSON(ServerUUID 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); @@ -108,10 +108,10 @@ public class JSONFactory { database.query(new ExtensionServerTableDataQuery(serverUUID, xMostRecentPlayers)), openPlayerLinksInNewTab, formatters, locale - ).toJSONMap(); + ); } - public Map networkPlayersTableJSON() { + public PlayersTableJSONCreator 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); @@ -147,7 +147,7 @@ public class JSONFactory { openPlayerLinksInNewTab, formatters, locale, true // players page - ).toJSONMap(); + ); } public List playerRetentionAsJSONMap(ServerUUID serverUUID) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONCreator.java index c0dda905b..9f15eb172 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONCreator.java @@ -23,7 +23,6 @@ import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; import com.djrapitops.plan.delivery.domain.mutators.*; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.rendering.html.Html; import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; import com.djrapitops.plan.delivery.rendering.json.graphs.line.PingGraph; import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; @@ -51,7 +50,6 @@ import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; import com.djrapitops.plan.utilities.java.Lists; import com.djrapitops.plan.utilities.java.Maps; -import org.apache.commons.text.StringEscapeUtils; import javax.inject.Inject; import javax.inject.Singleton; @@ -372,7 +370,7 @@ public class PlayerJSONCreator { List mapped = new ArrayList<>(); for (com.djrapitops.plan.delivery.domain.Nickname nickname : nicknames) { mapped.add(new Nickname( - Html.swapColorCodesToSpan(StringEscapeUtils.escapeHtml4(nickname.getName())), + nickname.getName(), serverNames.getOrDefault(nickname.getServerUUID(), nickname.getServerUUID().toString()), dateFormatter.apply(nickname.getDate()) )); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java index 6acf0753c..210ef6a62 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java @@ -17,6 +17,11 @@ package com.djrapitops.plan.delivery.rendering.json; import com.djrapitops.plan.delivery.domain.TablePlayer; +import com.djrapitops.plan.delivery.domain.datatransfer.PlayerListDto; +import com.djrapitops.plan.delivery.domain.datatransfer.TablePlayerDto; +import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionDescriptionDto; +import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionTabDataDto; +import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionValueDataDto; import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; @@ -33,6 +38,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import java.util.*; +import java.util.stream.Collectors; /** * Utility for creating jQuery Datatables JSON for a Players Table. @@ -109,6 +115,7 @@ public class PlayersTableJSONCreator { } } + @Deprecated public Map toJSONMap() { return Maps.builder(String.class, Object.class) .put("columns", createColumnHeaders()) @@ -116,6 +123,41 @@ public class PlayersTableJSONCreator { .build(); } + public PlayerListDto toPlayerList() { + return new PlayerListDto(toPlayers(), getExtensionDescriptors()); + } + + private List toPlayers() { + return players.stream() + .map(player -> TablePlayerDto.builder() + .withUuid(player.getPlayerUUID()) + .withName(player.getName().orElseGet(() -> player.getPlayerUUID().toString())) + .withSessionCount((long) player.getSessionCount().orElse(0)) + .withPlaytimeActive(player.getActivePlaytime().orElse(null)) + .withLastSeen(player.getLastSeen().orElse(null)) + .withRegistered(player.getRegistered().orElse(null)) + .withCountry(player.getGeolocation().orElse(null)) + .withExtensionValues(mapToExtensionValues(extensionData.get(player.getPlayerUUID()))) + .build() + ).collect(Collectors.toList()); + } + + private List getExtensionDescriptors() { + return extensionDescriptions.stream().map(ExtensionDescriptionDto::new).collect(Collectors.toList()); + } + + private Map mapToExtensionValues(ExtensionTabData extensionTabData) { + if (extensionTabData == null) return Collections.emptyMap(); + + Map values = new HashMap<>(); + List descriptions = extensionTabData.getDescriptions(); + for (ExtensionDescription description : descriptions) { + String name = description.getName(); + ExtensionTabDataDto.mapToValue(extensionTabData, name).ifPresent(value -> values.put(name, value)); + } + return values; + } + private List> createData() { List> dataJson = new ArrayList<>(); @@ -196,7 +238,7 @@ public class PlayersTableJSONCreator { } // 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("-"); + String stringValue = tabData.getString(key).map(ExtensionStringData::getValue).orElse("-"); putDataEntry(dataJson, stringValue, stringValue, key); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ErrorMessagePage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ErrorMessagePage.java index d1a2fab71..0f881b74b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ErrorMessagePage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ErrorMessagePage.java @@ -17,7 +17,6 @@ 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.delivery.rendering.html.icon.Icon; import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.utilities.java.UnaryChain; @@ -63,10 +62,7 @@ public class ErrorMessagePage implements Page { placeholders.put("title", icon.toHtml() + " " + errorTitle); placeholders.put("titleText", errorTitle); placeholders.put("paragraph", errorMsg); - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - placeholders.put("contributors", Contributors.generateContributorHtml()); return UnaryChain.of(template) .chain(theme::replaceThemeColors) .chain(placeholders::apply) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/InternalErrorPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/InternalErrorPage.java index efc30e6ba..d0d49348b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/InternalErrorPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/InternalErrorPage.java @@ -17,7 +17,6 @@ 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.delivery.rendering.html.Html; import com.djrapitops.plan.delivery.rendering.html.icon.Icon; import com.djrapitops.plan.exceptions.ExceptionWithContext; @@ -58,10 +57,7 @@ public class InternalErrorPage implements Page { placeholders.put("title", Icon.called("bug") + " 500 Internal Error occurred"); placeholders.put("titleText", "500 Internal Error occurred"); placeholders.put("paragraph", createContent()); - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - placeholders.put("contributors", Contributors.generateContributorHtml()); return placeholders.apply(template); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/LoginPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/LoginPage.java deleted file mode 100644 index f16ebf0e4..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/LoginPage.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; -import com.djrapitops.plan.delivery.web.resource.WebResource; -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.plan.version.VersionChecker; - -/** - * Html String generator for /login and /register page. - * - * @author AuroraLS3 - */ -public class LoginPage implements Page { - - private final WebResource template; - private final ServerInfo serverInfo; - private final Locale locale; - private final Theme theme; - - private final VersionChecker versionChecker; - - LoginPage( - WebResource htmlTemplate, - ServerInfo serverInfo, - Locale locale, - Theme theme, - VersionChecker versionChecker - ) { - this.template = htmlTemplate; - this.serverInfo = serverInfo; - this.locale = locale; - this.theme = theme; - this.versionChecker = versionChecker; - } - - @Override - public long lastModified() { - return template.getLastModified().orElseGet(System::currentTimeMillis); - } - - @Override - public String toHtml() { - PlaceholderReplacer placeholders = new PlaceholderReplacer(); - placeholders.put("command", getCommand()); - placeholders.put("version", versionChecker.getCurrentVersion()); - return UnaryChain.of(template.asString()) - .chain(theme::replaceThemeColors) - .chain(placeholders::apply) - .chain(locale::replaceLanguageInHtml) - .apply(); - } - - private String getCommand() { - if (serverInfo.getServer().isProxy()) return "planproxy"; - return "plan"; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java deleted file mode 100644 index fa94ebb08..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.domain.container.CachingSupplier; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; -import com.djrapitops.plan.delivery.rendering.html.Contributors; -import com.djrapitops.plan.delivery.webserver.cache.DataID; -import com.djrapitops.plan.delivery.webserver.cache.JSONStorage; -import com.djrapitops.plan.extension.implementation.results.ExtensionData; -import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; -import com.djrapitops.plan.identification.ServerInfo; -import com.djrapitops.plan.identification.ServerUUID; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.ProxySettings; -import com.djrapitops.plan.settings.config.paths.WebserverSettings; -import com.djrapitops.plan.settings.locale.Locale; -import com.djrapitops.plan.settings.theme.Theme; -import com.djrapitops.plan.settings.theme.ThemeVal; -import com.djrapitops.plan.storage.database.DBSystem; -import com.djrapitops.plan.utilities.java.UnaryChain; -import com.djrapitops.plan.version.VersionChecker; -import org.apache.commons.lang3.StringUtils; - -import java.util.List; - -/** - * Html String generator for /network page. - * - * @author AuroraLS3 - */ -public class NetworkPage implements Page { - - private final String templateHtml; - private final DBSystem dbSystem; - - private final VersionChecker versionChecker; - private final PlanConfig config; - private final Theme theme; - private final ServerInfo serverInfo; - private final JSONStorage jsonStorage; - private final Formatters formatters; - private final Locale locale; - private final ComponentSvc componentSvc; - - NetworkPage( - String templateHtml, - DBSystem dbSystem, - VersionChecker versionChecker, - PlanConfig config, - Theme theme, - ServerInfo serverInfo, - JSONStorage jsonStorage, - Formatters formatters, - Locale locale, - ComponentSvc componentSvc - ) { - this.templateHtml = templateHtml; - this.dbSystem = dbSystem; - this.versionChecker = versionChecker; - this.config = config; - this.theme = theme; - this.serverInfo = serverInfo; - this.jsonStorage = jsonStorage; - this.formatters = formatters; - this.locale = locale; - this.componentSvc = componentSvc; - } - - @Override - public String toHtml() { - PlaceholderReplacer placeholders = new PlaceholderReplacer(); - - ServerUUID serverUUID = serverInfo.getServerUUID(); - placeholders.put("networkDisplayName", config.get(ProxySettings.NETWORK_NAME)); - placeholders.put("serverName", config.get(ProxySettings.NETWORK_NAME)); - placeholders.put("serverUUID", serverUUID.toString()); - placeholders.put("refreshBarrier", config.get(WebserverSettings.REDUCED_REFRESH_BARRIER)); - - placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); - placeholders.put("playersGraphColor", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); - placeholders.put("worldMapColLow", theme.getValue(ThemeVal.WORLD_MAP_LOW)); - placeholders.put("worldMapColHigh", theme.getValue(ThemeVal.WORLD_MAP_HIGH)); - placeholders.put("maxPingColor", theme.getValue(ThemeVal.GRAPH_MAX_PING)); - placeholders.put("minPingColor", theme.getValue(ThemeVal.GRAPH_MIN_PING)); - placeholders.put("avgPingColor", theme.getValue(ThemeVal.GRAPH_AVG_PING)); - placeholders.put("timeZone", config.getTimeZoneOffsetHours()); - - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); - placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - placeholders.put("contributors", Contributors.generateContributorHtml()); - - CachingSupplier pluginTabs = new CachingSupplier<>(() -> { - List extensionData = dbSystem.getDatabase().query(new ExtensionServerDataQuery(serverUUID)); - return new ServerPluginTabs(extensionData, formatters, componentSvc); - }); - - long after = System.currentTimeMillis() - config.get(WebserverSettings.REDUCED_REFRESH_BARRIER); - String navIdentifier = DataID.EXTENSION_NAV.of(serverUUID); - String tabIdentifier = DataID.EXTENSION_TABS.of(serverUUID); - String nav = jsonStorage.fetchJsonMadeAfter(navIdentifier, after).orElseGet(() -> { - jsonStorage.invalidateOlder(navIdentifier, after); - return jsonStorage.storeJson(navIdentifier, pluginTabs.get().getNav()); - }).json; - String tabs = jsonStorage.fetchJsonMadeAfter(tabIdentifier, after).orElseGet(() -> { - jsonStorage.invalidateOlder(tabIdentifier, after); - return jsonStorage.storeJson(tabIdentifier, pluginTabs.get().getTabs()); - }).json; - - PlaceholderReplacer pluginPlaceholders = new PlaceholderReplacer(); - pluginPlaceholders.put("networkDisplayName", config.get(ProxySettings.NETWORK_NAME)); - pluginPlaceholders.put("serverName", config.get(ProxySettings.NETWORK_NAME)); - pluginPlaceholders.put("serverUUID", serverUUID.toString()); - pluginPlaceholders.put("navPluginsTabs", nav); - pluginPlaceholders.put("tabsPlugins", StringUtils.remove(tabs, "${backButton}")); - - return UnaryChain.of(templateHtml) - .chain(theme::replaceThemeColors) - .chain(placeholders::apply) - .chain(pluginPlaceholders::apply) - .chain(locale::replaceLanguageInHtml) - .apply(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java index 8dd863861..787a42155 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java @@ -16,26 +16,15 @@ */ package com.djrapitops.plan.delivery.rendering.pages; -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.domain.container.PlayerContainer; -import com.djrapitops.plan.delivery.formatting.Formatters; import com.djrapitops.plan.delivery.rendering.html.icon.Icon; import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException; import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.delivery.webserver.Addresses; -import com.djrapitops.plan.delivery.webserver.cache.JSONStorage; -import com.djrapitops.plan.extension.implementation.results.ExtensionData; -import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionPlayerDataQuery; -import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.identification.ServerUUID; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.storage.database.DBSystem; -import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plan.storage.file.PublicHtmlFiles; @@ -47,7 +36,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import java.io.IOException; import java.io.UncheckedIOException; -import java.util.*; /** * Factory for creating different {@link Page} objects. @@ -60,51 +48,31 @@ public class PageFactory { private final Lazy versionChecker; private final Lazy files; private final Lazy publicHtmlFiles; - private final Lazy config; private final Lazy theme; private final Lazy dbSystem; - private final Lazy serverInfo; - private final Lazy jsonStorage; - private final Lazy formatters; - private final Lazy locale; - private final Lazy componentService; private final Lazy addresses; + private static final String ERROR_HTML_FILE = "error.html"; @Inject public PageFactory( Lazy versionChecker, Lazy files, - Lazy publicHtmlFiles, Lazy config, + Lazy publicHtmlFiles, Lazy theme, Lazy dbSystem, Lazy serverInfo, - Lazy jsonStorage, - Lazy formatters, - Lazy locale, - Lazy componentService, Lazy addresses ) { this.versionChecker = versionChecker; this.files = files; this.publicHtmlFiles = publicHtmlFiles; - this.config = config; this.theme = theme; this.dbSystem = dbSystem; - this.serverInfo = serverInfo; - this.jsonStorage = jsonStorage; - this.formatters = formatters; - this.locale = locale; - this.componentService = componentService; this.addresses = addresses; } public Page playersPage() throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - - return new PlayersPage(getResourceAsString("players.html"), versionChecker.get(), - config.get(), theme.get(), serverInfo.get()); + return reactPage(); } public Page reactPage() throws IOException { @@ -134,100 +102,24 @@ public class PageFactory { * @throws IOException If the template files can not be read. */ public Page serverPage(ServerUUID serverUUID) throws IOException { - Server server = dbSystem.get().getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)) - .orElseThrow(() -> new NotFoundException("Server not found in the database")); - - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); + if (dbSystem.get().getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)).isEmpty()) { + throw new NotFoundException("Server not found in the database"); } - - return new ServerPage( - getResourceAsString("server.html"), - server, - config.get(), - theme.get(), - versionChecker.get(), - dbSystem.get(), - serverInfo.get(), - jsonStorage.get(), - formatters.get(), - locale.get(), - componentService.get() - ); + return reactPage(); } - public Page playerPage(PlayerContainer player) throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - - return new PlayerPage( - getResourceAsString("player.html"), player, - versionChecker.get(), - config.get(), - this, - theme.get(), - formatters.get(), - serverInfo.get(), - locale.get() - ); - } - - public PlayerPluginTab inspectPluginTabs(UUID playerUUID) { - Database database = dbSystem.get().getDatabase(); - - Map> extensionPlayerData = database.query(new ExtensionPlayerDataQuery(playerUUID)); - - if (extensionPlayerData.isEmpty()) { - return new PlayerPluginTab("", Collections.emptyList(), formatters.get(), componentService.get()); - } - - List playerPluginTabs = new ArrayList<>(); - for (Map.Entry entry : database.query(ServerQueries.fetchPlanServerInformation()).entrySet()) { - ServerUUID serverUUID = entry.getKey(); - String serverName = entry.getValue().getIdentifiableName(); - - List ofServer = extensionPlayerData.get(serverUUID); - if (ofServer == null) { - continue; - } - - playerPluginTabs.add(new PlayerPluginTab(serverName, ofServer, formatters.get(), componentService.get())); - } - - StringBuilder navs = new StringBuilder(); - StringBuilder tabs = new StringBuilder(); - - playerPluginTabs.stream().sorted().forEach(tab -> { - navs.append(tab.getNav()); - tabs.append(tab.getTab()); - }); - - return new PlayerPluginTab(navs.toString(), tabs.toString(), componentService.get()); + public Page playerPage() throws IOException { + return reactPage(); } public Page networkPage() throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - - return new NetworkPage(getResourceAsString("network.html"), - dbSystem.get(), - versionChecker.get(), - config.get(), - theme.get(), - serverInfo.get(), - jsonStorage.get(), - formatters.get(), - locale.get(), - componentService.get() - ); + return reactPage(); } public Page internalErrorPage(String message, @Untrusted Throwable error) { try { return new InternalErrorPage( - getResourceAsString("error.html"), message, error, + getResourceAsString(ERROR_HTML_FILE), message, error, versionChecker.get()); } catch (IOException noParse) { return () -> "Error occurred: " + error.toString() + @@ -238,13 +130,13 @@ public class PageFactory { public Page errorPage(String title, String error) throws IOException { return new ErrorMessagePage( - getResourceAsString("error.html"), title, error, + getResourceAsString(ERROR_HTML_FILE), title, error, versionChecker.get(), theme.get()); } public Page errorPage(Icon icon, String title, String error) throws IOException { return new ErrorMessagePage( - getResourceAsString("error.html"), icon, title, error, theme.get(), versionChecker.get()); + getResourceAsString(ERROR_HTML_FILE), icon, title, error, theme.get(), versionChecker.get()); } public String getResourceAsString(String name) throws IOException { @@ -268,29 +160,15 @@ public class PageFactory { } public Page loginPage() throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - - return new LoginPage(getResource("login.html"), serverInfo.get(), locale.get(), theme.get(), versionChecker.get()); + return reactPage(); } public Page registerPage() throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - - return new LoginPage(getResource("register.html"), serverInfo.get(), locale.get(), theme.get(), versionChecker.get()); + return reactPage(); } public Page queryPage() throws IOException { - if (config.get().isFalse(PluginSettings.LEGACY_FRONTEND)) { - return reactPage(); - } - return new QueryPage( - getResourceAsString("query.html"), - locale.get(), theme.get(), versionChecker.get() - ); + return reactPage(); } public Page errorsPage() throws IOException { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java deleted file mode 100644 index 87f081a10..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.delivery.domain.container.PlayerContainer; -import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; -import com.djrapitops.plan.delivery.formatting.Formatter; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; -import com.djrapitops.plan.delivery.rendering.html.Contributors; -import com.djrapitops.plan.delivery.rendering.html.Html; -import com.djrapitops.plan.identification.ServerInfo; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.DisplaySettings; -import com.djrapitops.plan.settings.locale.Locale; -import com.djrapitops.plan.settings.theme.Theme; -import com.djrapitops.plan.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.java.UnaryChain; -import com.djrapitops.plan.version.VersionChecker; - -import java.util.UUID; - -/** - * Html String generator for /player page. - * - * @author AuroraLS3 - */ -public class PlayerPage implements Page { - - private final String templateHtml; - private final PlayerContainer player; - - private final VersionChecker versionChecker; - - private final PlanConfig config; - private final PageFactory pageFactory; - private final Theme theme; - private final ServerInfo serverInfo; - - private final Formatter clockLongFormatter; - private final Formatter secondLongFormatter; - private final Locale locale; - - PlayerPage( - String templateHtml, - PlayerContainer player, - VersionChecker versionChecker, - PlanConfig config, - PageFactory pageFactory, - Theme theme, - Formatters formatters, - ServerInfo serverInfo, - Locale locale - ) { - this.templateHtml = templateHtml; - this.player = player; - this.versionChecker = versionChecker; - this.config = config; - this.pageFactory = pageFactory; - this.theme = theme; - this.serverInfo = serverInfo; - - clockLongFormatter = formatters.clockLong(); - secondLongFormatter = formatters.secondLong(); - this.locale = locale; - } - - @Override - public String toHtml() { - if (player.getValue(PlayerKeys.REGISTERED).isEmpty()) { - throw new IllegalStateException("Player is not registered"); - } - return createFor(player); - } - - public String createFor(PlayerContainer player) { - long now = System.currentTimeMillis(); - UUID playerUUID = player.getUnsafe(PlayerKeys.UUID); - - PlaceholderReplacer placeholders = new PlaceholderReplacer(); - - placeholders.put("refresh", clockLongFormatter.apply(now)); - placeholders.put("refreshFull", secondLongFormatter.apply(now)); - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); - placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - - String playerName = player.getValue(PlayerKeys.NAME).orElse(playerUUID.toString()); - placeholders.put("playerName", playerName); - placeholders.put("playerUUID", playerUUID); - placeholders.put("playerHeadUrl", config.get(DisplaySettings.PLAYER_HEAD_IMG_URL)); - - placeholders.put("timeZone", config.getTimeZoneOffsetHours()); - placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); - - placeholders.put("contributors", Contributors.generateContributorHtml()); - - PlaceholderReplacer pluginPlaceholders = new PlaceholderReplacer(); - PlayerPluginTab pluginTabs = pageFactory.inspectPluginTabs(playerUUID); - - pluginPlaceholders.put("playerName", playerName); - pluginPlaceholders.put("backButton", (serverInfo.getServer().isProxy() ? Html.BACK_BUTTON_NETWORK : Html.BACK_BUTTON_SERVER).create()); - pluginPlaceholders.put("navPluginsTabs", pluginTabs.getNav()); - pluginPlaceholders.put("pluginsTabs", pluginTabs.getTab()); - - return UnaryChain.of(templateHtml) - .chain(theme::replaceThemeColors) - .chain(placeholders::apply) - .chain(pluginPlaceholders::apply) - .chain(locale::replaceLanguageInHtml) - .apply(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java deleted file mode 100644 index 4090863c5..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.formatting.Formatter; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.rendering.html.icon.Icon; -import com.djrapitops.plan.delivery.rendering.html.structure.NavLink; -import com.djrapitops.plan.delivery.rendering.html.structure.TabsElement; -import com.djrapitops.plan.extension.ElementOrder; -import com.djrapitops.plan.extension.FormatType; -import com.djrapitops.plan.extension.implementation.TabInformation; -import com.djrapitops.plan.extension.implementation.results.*; - -import java.util.*; - -/** - * Responsible for generating /player page plugin tabs based on DataExtension API data. - * - * @author AuroraLS3 - */ -public class PlayerPluginTab implements Comparable { - - private String serverName; - private List playerData; - - private Map> numberFormatters; - - private Formatter decimalFormatter; - private Formatter percentageFormatter; - - private String nav; - private String tab; - - private boolean hasWideTable; - private final ComponentSvc componentSvc; - - public PlayerPluginTab(String nav, String tab, ComponentSvc componentSvc) { - this.nav = nav; - this.tab = tab; - this.componentSvc = componentSvc; - } - - public PlayerPluginTab( - String serverName, - List playerData, - Formatters formatters, - ComponentSvc componentSvc - ) { - this.serverName = serverName; - this.playerData = playerData; - this.componentSvc = componentSvc; - - numberFormatters = new EnumMap<>(FormatType.class); - numberFormatters.put(FormatType.DATE_SECOND, formatters.secondLong()); - numberFormatters.put(FormatType.DATE_YEAR, formatters.yearLong()); - numberFormatters.put(FormatType.TIME_MILLISECONDS, formatters.timeAmount()); - numberFormatters.put(FormatType.NONE, Object::toString); - - this.decimalFormatter = formatters.decimals(); - this.percentageFormatter = formatters.percentage(); - - hasWideTable = false; - - generate(); - } - - public String getNav() { - return nav; - } - - public String getTab() { - return tab; - } - - private void generate() { - if (playerData.isEmpty()) { - nav = NavLink.collapsed(Icon.called("cubes").build(), "plugins-" + serverName + " (No Data)", serverName + " (No Data)").toHtml(); - tab = wrapInWideTab( - serverName + " (No Data)", - "

    No Extension Data

    " - ); - } else { - nav = NavLink.collapsed(Icon.called("cubes").build(), "plugins-" + serverName, serverName).toHtml(); - tab = generatePageTab(); - } - } - - private String generatePageTab() { - Collections.sort(playerData); - - StringBuilder tabBuilder = new StringBuilder(); - - for (ExtensionData datum : playerData) { - ExtensionInformation extensionInformation = datum.getExtensionInformation(); - - boolean onlyGeneric = datum.hasOnlyGenericTab(); - - String tabsElement; - if (onlyGeneric) { - ExtensionTabData genericTabData = datum.getTabs().get(0); - tabsElement = buildContentHtml(genericTabData); - } else { - tabsElement = new TabsElement( - datum.getPluginID(), - datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) - ).toHtmlFull(); - } - - tabBuilder.append(wrapInContainer(extensionInformation, tabsElement)); - } - - return wrapInCardColumnsTab(serverName, tabBuilder.toString()); - } - - private String wrapInWideTab(String serverName, String content) { - return "
    " + - // Page heading - "
    " + - "

    " + serverName + " · Plugins Overview

    ${backButton}" + - "
    " + - // End Page heading - "
    " + content + "
    "; - } - - private String wrapInCardColumnsTab(String serverName, String content) { - return "
    " + - // Page heading - "
    " + - "

    " + serverName + " · Plugins Overview

    ${backButton}" + - "
    " + - // End Page heading - "
    " + content + "
    "; - } - - private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { - TabInformation tabInformation = tabData.getTabInformation(); - String tabContentHtml = buildContentHtml(tabData); - - String tabName = tabInformation.getTabName(); - return tabName.isEmpty() ? new TabsElement.Tab(Icon.called("info-circle").build(), "General", tabContentHtml) - : new TabsElement.Tab(Icon.fromExtensionIcon(tabInformation.getTabIcon()), tabName, tabContentHtml); - } - - private String buildContentHtml(ExtensionTabData tabData) { - TabInformation tabInformation = tabData.getTabInformation(); - - List order = tabInformation.getTabElementOrder(); - String values = buildValuesHtml(tabData); - String valuesHtml = values.isEmpty() ? "" : "
    " + values + "
    "; - String tablesHtml = buildTablesHtml(tabData); - - StringBuilder builder = new StringBuilder(); - - for (ElementOrder ordering : order) { - switch (ordering) { - case VALUES: - builder.append(valuesHtml); - break; - case TABLE: - builder.append(tablesHtml); - break; - default: - break; - } - } - - return builder.toString(); - } - - private String buildTablesHtml(ExtensionTabData tabData) { - StringBuilder builder = new StringBuilder(); - for (ExtensionTableData tableData : tabData.getTableData()) { - if (tableData.isWideTable()) { - hasWideTable = true; - } - builder.append(tableData.getHtmlTable().toHtml()); - } - return builder.toString(); - } - - private String buildValuesHtml(ExtensionTabData tabData) { - StringBuilder builder = new StringBuilder(); - for (String key : tabData.getValueOrder()) { - tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue())); - tabData.getDouble(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(decimalFormatter))); - tabData.getPercentage(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(percentageFormatter))); - tabData.getNumber(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(numberFormatters.get(data.getFormatType())))); - tabData.getString(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue())); - tabData.getComponent(key).ifPresent(data -> append(builder, data.getDescription(), data.getHtmlValue(componentSvc))); - } - return builder.toString(); - } - - private void append(StringBuilder builder, ExtensionDescription description, String formattedValue) { - Optional textDescription = description.getDescription(); - if (textDescription.isPresent()) { - builder.append("

    "); - } else { - builder.append("

    "); - } - builder.append(Icon.fromExtensionIcon(description.getIcon())) - .append(' ').append(description.getText()).append("").append(formattedValue).append("

    "); - } - - private String wrapInContainer(ExtensionInformation information, String tabsElement) { - String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4"; - // TODO move large tables to their own tabs - return "
    " + - "
    " + - "
    " + - "
    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "
    " + - "
    " + - tabsElement + - "
    " + - "
    "; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PlayerPluginTab)) return false; - PlayerPluginTab that = (PlayerPluginTab) o; - return Objects.equals(serverName, that.serverName) && - Objects.equals(nav, that.nav); - } - - @Override - public int hashCode() { - return Objects.hash(serverName, nav); - } - - @Override - public int compareTo(PlayerPluginTab other) { - return String.CASE_INSENSITIVE_ORDER.compare(this.serverName, other.serverName); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java deleted file mode 100644 index 19edd782c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 . - */ -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.identification.ServerInfo; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; -import com.djrapitops.plan.settings.config.paths.ProxySettings; -import com.djrapitops.plan.settings.config.paths.WebserverSettings; -import com.djrapitops.plan.settings.theme.Theme; -import com.djrapitops.plan.version.VersionChecker; - -/** - * Html String generator for /players page. - * - * @author AuroraLS3 - */ -public class PlayersPage implements Page { - - private final String templateHtml; - private final VersionChecker versionChecker; - private final PlanConfig config; - private final Theme theme; - private final ServerInfo serverInfo; - - PlayersPage( - String templateHtml, - VersionChecker versionChecker, - PlanConfig config, - Theme theme, - ServerInfo serverInfo - ) { - this.templateHtml = templateHtml; - this.versionChecker = versionChecker; - this.config = config; - this.theme = theme; - this.serverInfo = serverInfo; - } - - @Override - public String toHtml() { - PlaceholderReplacer placeholders = new PlaceholderReplacer(); - - placeholders.put("refreshBarrier", config.get(WebserverSettings.REDUCED_REFRESH_BARRIER)); - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); - placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - placeholders.put("contributors", Contributors.generateContributorHtml()); - if (serverInfo.getServer().isProxy()) { - placeholders.put("networkName", config.get(ProxySettings.NETWORK_NAME)); - } else { - placeholders.put("networkName", config.get(PluginSettings.SERVER_NAME)); - } - - return placeholders.apply(theme.replaceThemeColors(templateHtml)); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/QueryPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/QueryPage.java deleted file mode 100644 index 111015108..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/QueryPage.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 . - */ -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 AuroraLS3 - */ -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("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); - placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - placeholders.put("contributors", Contributors.generateContributorHtml()); - return UnaryChain.of(template) - .chain(theme::replaceThemeColors) - .chain(placeholders::apply) - .chain(locale::replaceLanguageInHtml) - .apply(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java deleted file mode 100644 index f26277b42..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.domain.container.CachingSupplier; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; -import com.djrapitops.plan.delivery.rendering.html.Contributors; -import com.djrapitops.plan.delivery.rendering.html.Html; -import com.djrapitops.plan.delivery.webserver.cache.DataID; -import com.djrapitops.plan.delivery.webserver.cache.JSONStorage; -import com.djrapitops.plan.extension.implementation.results.ExtensionData; -import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; -import com.djrapitops.plan.identification.Server; -import com.djrapitops.plan.identification.ServerInfo; -import com.djrapitops.plan.identification.ServerUUID; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.WebserverSettings; -import com.djrapitops.plan.settings.locale.Locale; -import com.djrapitops.plan.settings.theme.Theme; -import com.djrapitops.plan.settings.theme.ThemeVal; -import com.djrapitops.plan.storage.database.DBSystem; -import com.djrapitops.plan.utilities.java.UnaryChain; -import com.djrapitops.plan.version.VersionChecker; - -import java.util.List; - -/** - * Html String generator for /server page. - * - * @author AuroraLS3 - */ -public class ServerPage implements Page { - - private final String templateHtml; - private final Server server; - private final PlanConfig config; - private final Theme theme; - private final VersionChecker versionChecker; - private final DBSystem dbSystem; - private final ServerInfo serverInfo; - private final JSONStorage jsonStorage; - private final Formatters formatters; - private final Locale locale; - private final ComponentSvc componentSvc; - - ServerPage( - String templateHtml, Server server, - PlanConfig config, - Theme theme, - VersionChecker versionChecker, - DBSystem dbSystem, - ServerInfo serverInfo, - JSONStorage jsonStorage, - Formatters formatters, - Locale locale, - ComponentSvc componentSvc - ) { - this.templateHtml = templateHtml; - this.server = server; - this.config = config; - this.theme = theme; - this.versionChecker = versionChecker; - this.dbSystem = dbSystem; - this.serverInfo = serverInfo; - this.jsonStorage = jsonStorage; - this.formatters = formatters; - this.locale = locale; - this.componentSvc = componentSvc; - } - - @Override - public String toHtml() { - PlaceholderReplacer placeholders = new PlaceholderReplacer(); - - ServerUUID serverUUID = server.getUuid(); - placeholders.put("serverUUID", serverUUID.toString()); - placeholders.put("serverName", server.getIdentifiableName()); - placeholders.put("serverDisplayName", server.getName()); - placeholders.put("refreshBarrier", config.get(WebserverSettings.REDUCED_REFRESH_BARRIER)); - - placeholders.put("timeZone", config.getTimeZoneOffsetHours()); - placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); - - placeholders.put("contributors", Contributors.generateContributorHtml()); - placeholders.put("versionButton", versionChecker.getUpdateButton().orElse(versionChecker.getCurrentVersionButton())); - placeholders.put("version", versionChecker.getCurrentVersion()); - placeholders.put("updateModal", versionChecker.getUpdateModal()); - - CachingSupplier pluginTabs = new CachingSupplier<>(() -> { - List extensionData = dbSystem.getDatabase().query(new ExtensionServerDataQuery(serverUUID)); - return new ServerPluginTabs(extensionData, formatters, componentSvc); - }); - - long after = System.currentTimeMillis() - config.get(WebserverSettings.REDUCED_REFRESH_BARRIER); - String navIdentifier = DataID.EXTENSION_NAV.of(serverUUID); - String tabIdentifier = DataID.EXTENSION_TABS.of(serverUUID); - String nav = jsonStorage.fetchJsonMadeAfter(navIdentifier, after).orElseGet(() -> { - jsonStorage.invalidateOlder(navIdentifier, after); - return jsonStorage.storeJson(navIdentifier, pluginTabs.get().getNav()); - }).json; - String tabs = jsonStorage.fetchJsonMadeAfter(tabIdentifier, after).orElseGet(() -> { - jsonStorage.invalidateOlder(tabIdentifier, after); - return jsonStorage.storeJson(tabIdentifier, pluginTabs.get().getTabs()); - }).json; - - PlaceholderReplacer pluginPlaceholders = new PlaceholderReplacer(); - pluginPlaceholders.put("serverUUID", serverUUID.toString()); - pluginPlaceholders.put("serverName", server.getIdentifiableName()); - pluginPlaceholders.put("serverDisplayName", server.getName()); - pluginPlaceholders.put("backButton", serverInfo.getServer().isProxy() ? Html.BACK_BUTTON_NETWORK.create() : ""); - pluginPlaceholders.put("navPluginsTabs", nav); - pluginPlaceholders.put("tabsPlugins", tabs); - - return UnaryChain.of(templateHtml) - .chain(theme::replaceThemeColors) - .chain(placeholders::apply) - .chain(pluginPlaceholders::apply) - .chain(locale::replaceLanguageInHtml) - .apply(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java deleted file mode 100644 index 5f13e25e3..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.rendering.pages; - -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.formatting.Formatter; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.delivery.rendering.html.icon.Icon; -import com.djrapitops.plan.delivery.rendering.html.structure.NavLink; -import com.djrapitops.plan.delivery.rendering.html.structure.TabsElement; -import com.djrapitops.plan.extension.ElementOrder; -import com.djrapitops.plan.extension.FormatType; -import com.djrapitops.plan.extension.implementation.TabInformation; -import com.djrapitops.plan.extension.implementation.results.*; -import com.djrapitops.plan.utilities.java.Lists; - -import java.util.*; - -/** - * Responsible for generating /server page plugin tabs based on DataExtension API data. - *

    - * Currently very similar to {@link PlayerPluginTab}. - * This will become more complex once tables are added, since some big tables will be moved to their own tabs. - * - * @author AuroraLS3 - */ -public class ServerPluginTabs { - - private List serverData; - private List extraTabServerData; - - private Map> numberFormatters; - - private Formatter decimalFormatter; - private Formatter percentageFormatter; - - private StringBuilder nav; - private String tab; - private final ComponentSvc componentSvc; - - public ServerPluginTabs(String nav, String tab, ComponentSvc componentSvc) { - this.nav = new StringBuilder(nav); - this.tab = tab; - this.componentSvc = componentSvc; - } - - public ServerPluginTabs( - List serverData, - Formatters formatters, - ComponentSvc componentSvc - ) { - this.serverData = serverData; - Collections.sort(serverData); - this.extraTabServerData = Lists.filter(serverData, ExtensionData::doesNeedWiderSpace); - this.serverData.removeAll(extraTabServerData); - this.componentSvc = componentSvc; - - numberFormatters = new EnumMap<>(FormatType.class); - numberFormatters.put(FormatType.DATE_SECOND, formatters.secondLong()); - numberFormatters.put(FormatType.DATE_YEAR, formatters.yearLong()); - numberFormatters.put(FormatType.TIME_MILLISECONDS, formatters.timeAmount()); - numberFormatters.put(FormatType.NONE, Object::toString); - - this.decimalFormatter = formatters.decimals(); - this.percentageFormatter = formatters.percentage(); - - generate(); - } - - public String getNav() { - return nav.toString(); - } - - public String getTabs() { - return tab; - } - - private void generate() { - String tabID = "plugins-overview"; - if (serverData.isEmpty()) { - nav = new StringBuilder(NavLink.main(Icon.called("cubes").build(), tabID, "Overview (No Data)").toHtml()); - tab = wrapInWideColumnTab( - "Overview", "

    No Extension Data

    " - ); - } else { - nav = new StringBuilder(NavLink.main(Icon.called("cubes").build(), tabID, "Overview").toHtml()); - tab = generateOverviewTab(); - } - tab += generateExtraTabs(); - } - - private String generateExtraTabs() { - StringBuilder tabBuilder = new StringBuilder(); - - for (ExtensionData datum : extraTabServerData) { - ExtensionInformation extensionInformation = datum.getExtensionInformation(); - - boolean onlyGeneric = datum.hasOnlyGenericTab(); - - String tabsElement; - if (onlyGeneric) { - ExtensionTabData genericTabData = datum.getTabs().get(0); - tabsElement = buildContentHtml(genericTabData); - } else { - tabsElement = new TabsElement( - datum.getPluginID(), - datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) - ).toHtmlFull(); - } - - String tabName = extensionInformation.getPluginName(); - tabBuilder.append(wrapInWideColumnTab(tabName, wrapInWideContainer(extensionInformation, tabsElement))); - nav.append(NavLink.main(Icon.fromExtensionIcon(extensionInformation.getIcon()), "plugins-" + tabName, tabName).toHtml()); - } - return tabBuilder.toString(); - } - - private String generateOverviewTab() { - StringBuilder contentBuilder = new StringBuilder(); - - for (ExtensionData datum : serverData) { - ExtensionInformation extensionInformation = datum.getExtensionInformation(); - - boolean onlyGeneric = datum.hasOnlyGenericTab(); - - String tabsElement; - if (onlyGeneric) { - ExtensionTabData genericTabData = datum.getTabs().get(0); - tabsElement = buildContentHtml(genericTabData); - } else { - tabsElement = new TabsElement( - datum.getPluginID(), - datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) - ).toHtmlFull(); - } - - contentBuilder.append(wrapInContainer(extensionInformation, tabsElement)); - } - - return wrapInOverviewTab(contentBuilder.toString()); - } - - private String wrapInWideColumnTab(String tabName, String content) { - return "
    " + - // Page heading - "
    " + - "

    ${serverName} · " + tabName + "

    ${backButton}" + - "
    " + - // End Page heading - "
    " + content + "
    "; - } - - private String wrapInOverviewTab(String content) { - return "
    " + - // Page heading - "
    " + - "

    ${serverName} · Plugins Overview

    ${backButton}" + - "
    " + - // End Page heading - "
    " + content + "
    "; - } - - private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { - TabInformation tabInformation = tabData.getTabInformation(); - String tabContentHtml = buildContentHtml(tabData); - - String tabName = tabInformation.getTabName(); - return tabName.isEmpty() ? new TabsElement.Tab(Icon.called("info-circle").build(), "General", tabContentHtml) - : new TabsElement.Tab(Icon.fromExtensionIcon(tabInformation.getTabIcon()), tabName, tabContentHtml); - } - - private String buildContentHtml(ExtensionTabData tabData) { - TabInformation tabInformation = tabData.getTabInformation(); - - List order = tabInformation.getTabElementOrder(); - String values = buildValuesHtml(tabData); - String valuesHtml = values.isEmpty() ? "" : "
    " + values + "
    "; - String tablesHtml = buildTablesHtml(tabData); - - StringBuilder builder = new StringBuilder(); - - for (ElementOrder ordering : order) { - switch (ordering) { - case VALUES: - builder.append(valuesHtml); - break; - case TABLE: - builder.append(tablesHtml); - break; - default: - break; - } - } - - return builder.toString(); - } - - private String buildTablesHtml(ExtensionTabData tabData) { - StringBuilder builder = new StringBuilder(); - for (ExtensionTableData tableData : tabData.getTableData()) { - builder.append(tableData.getHtmlTable().toHtml()); - } - return builder.toString(); - } - - private String buildValuesHtml(ExtensionTabData tabData) { - StringBuilder builder = new StringBuilder(); - for (String key : tabData.getValueOrder()) { - tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue())); - tabData.getDouble(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(decimalFormatter))); - tabData.getPercentage(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(percentageFormatter))); - tabData.getNumber(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue(numberFormatters.get(data.getFormatType())))); - tabData.getString(key).ifPresent(data -> append(builder, data.getDescription(), data.getFormattedValue())); - tabData.getComponent(key).ifPresent(data -> append(builder, data.getDescription(), data.getHtmlValue(componentSvc))); - } - return builder.toString(); - } - - private void append(StringBuilder builder, ExtensionDescription description, String formattedValue) { - Optional textDescription = description.getDescription(); - if (textDescription.isPresent()) { - builder.append("

    "); - } else { - builder.append("

    "); - } - builder.append(Icon.fromExtensionIcon(description.getIcon())) - .append(' ').append(description.getText()).append("").append(formattedValue).append("

    "); - } - - private String wrapInContainer(ExtensionInformation information, String tabsElement) { - return "
    " + - "
    " + - "
    " + - "
    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "
    " + - "
    " + - tabsElement + - "
    " + - "
    "; - } - - private String wrapInWideContainer(ExtensionInformation information, String tabsElement) { - return "
    " + - "
    " + - "
    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "
    " + - "
    " + - tabsElement + - "
    "; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/WebAssetVersionCheckTask.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/WebAssetVersionCheckTask.java deleted file mode 100644 index 0de54d1d8..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/WebAssetVersionCheckTask.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.delivery.web; - -import com.djrapitops.plan.TaskSystem; -import com.djrapitops.plan.delivery.formatting.Formatters; -import com.djrapitops.plan.settings.config.ConfigNode; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.storage.file.PlanFiles; -import net.playeranalytics.plugin.scheduling.RunnableFactory; -import net.playeranalytics.plugin.server.PluginLogger; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Task in charge of checking html customized files on enable to see if they are outdated. - * - * @deprecated Html customization system will be overhauled for React version of frontend. - */ -@Singleton -@Deprecated(forRemoval = true, since = "#2260") // TODO Remove after Frontend BETA -public class WebAssetVersionCheckTask extends TaskSystem.Task { - - private final PlanConfig config; - private final PlanFiles files; - private final PluginLogger logger; - private final AssetVersions assetVersions; - private final Formatters formatters; - - @Inject - public WebAssetVersionCheckTask( - PlanConfig config, - PlanFiles files, - PluginLogger logger, - AssetVersions assetVersions, - Formatters formatters - ) { - this.config = config; - this.files = files; - this.logger = logger; - this.assetVersions = assetVersions; - this.formatters = formatters; - } - - @Override - public void register(RunnableFactory runnableFactory) { - runnableFactory.create(this).runTaskLaterAsynchronously(3, TimeUnit.SECONDS); - } - - @Override - public void run() { - try { - runTask(); - } finally { - cancel(); - } - } - - private void runTask() { - Optional planCustomizationNode = getPlanCustomizationNode(); - if (planCustomizationNode.isPresent()) { - try { - assetVersions.prepare(); - } catch (IOException e) { - logger.warn(String.format("Could not read web asset versions, %s", e.toString())); - logger.warn("Web asset version check will be skipped!"); - return; - } - - List outdated = new ArrayList<>(); - - for (ConfigNode child : planCustomizationNode.get().getChildren()) { - if (child.getBoolean()) { - String resource = child.getKey(false).replace(',', '.'); - findOutdatedResource(resource).ifPresent(outdated::add); - } - } - - if (!outdated.isEmpty()) { - logger.warn("You have customized files which are out of date due to recent updates!"); - logger.warn("Plan may not work properly until these files are updated to include the new modifications."); - logger.warn("See https://github.com/plan-player-analytics/Plan/commits/html to compare changes"); - } - for (AssetInfo asset : outdated) { - logger.warn(String.format("- %s was modified %s, but the plugin contains a version from %s", - asset.filename, - formatters.secondLong().apply(asset.modifiedAt), - formatters.secondLong().apply(asset.expectedModifiedAt) - )); - } - } - } - - private Optional findOutdatedResource(String resource) { - Path dir = config.getResourceSettings().getCustomizationDirectory(); - Optional resourceFile = files.attemptToFind(dir, resource); - Optional webAssetVersion = assetVersions.getAssetVersion(resource); - if (resourceFile.isPresent() && webAssetVersion.isPresent() && webAssetVersion.get() > resourceFile.get().lastModified()) { - return Optional.of(new AssetInfo( - resource, - resourceFile.get().lastModified(), - webAssetVersion.get() - )); - } - return Optional.empty(); - } - - private Optional getPlanCustomizationNode() { - return config.getResourceSettings().getCustomizationConfigNode().getNode("Plan"); - } - - private static class AssetInfo { - private final String filename; - private final long modifiedAt; - private final long expectedModifiedAt; - - public AssetInfo(String filename, long modifiedAt, long expectedModifiedAt) { - this.filename = filename; - this.modifiedAt = modifiedAt; - this.expectedModifiedAt = expectedModifiedAt; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseFactory.java index d3a341a4b..9a4c0d4ce 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseFactory.java @@ -24,7 +24,6 @@ import com.djrapitops.plan.delivery.rendering.html.icon.Family; import com.djrapitops.plan.delivery.rendering.html.icon.Icon; import com.djrapitops.plan.delivery.rendering.pages.Page; import com.djrapitops.plan.delivery.rendering.pages.PageFactory; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.web.resolver.MimeType; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.ResponseBuilder; @@ -34,7 +33,6 @@ import com.djrapitops.plan.delivery.web.resource.WebResource; import com.djrapitops.plan.identification.Identifiers; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.ErrorPageLang; import com.djrapitops.plan.settings.theme.Theme; @@ -71,7 +69,6 @@ public class ResponseFactory { private static final String STATIC_BUNDLE_FOLDER = "static"; private final PlanFiles files; - private final PlanConfig config; private final PublicHtmlFiles publicHtmlFiles; private final PageFactory pageFactory; private final Locale locale; @@ -92,7 +89,6 @@ public class ResponseFactory { Lazy addresses ) { this.files = files; - this.config = config; this.publicHtmlFiles = publicHtmlFiles; this.pageFactory = pageFactory; this.locale = locale; @@ -103,14 +99,6 @@ public class ResponseFactory { httpLastModifiedFormatter = formatters.httpLastModifiedLong(); } - /** - * @throws UncheckedIOException If reading the resource fails - */ - public WebResource getResource(@Untrusted String resourceName) { - return ResourceService.getInstance().getResource("Plan", resourceName, - () -> files.getResourceFromJar("web/" + resourceName).asWebResource()); - } - /** * @throws UncheckedIOException If reading the resource fails */ @@ -191,7 +179,7 @@ public class ResponseFactory { } private Response getCachedOrNew(long modified, String fileName, Function newResponseFunction) { - WebResource resource = config.isFalse(PluginSettings.LEGACY_FRONTEND) ? getPublicOrJarResource(fileName) : getResource(fileName); + WebResource resource = getPublicOrJarResource(fileName); Optional lastModified = resource.getLastModified(); if (lastModified.isPresent() && modified == lastModified.get()) { return browserCachedNotChangedResponse(); @@ -240,16 +228,10 @@ public class ResponseFactory { public Response javaScriptResponse(@Untrusted String fileName) { try { - WebResource resource = config.isFalse(PluginSettings.LEGACY_FRONTEND) ? getPublicOrJarResource(fileName) : getResource(fileName); + WebResource resource = getPublicOrJarResource(fileName); String content = UnaryChain.of(resource.asString()) .chain(this::replaceMainAddressPlaceholder) .chain(theme::replaceThemeColors) - .chain(contents -> { - if (fileName.contains(STATIC_BUNDLE_FOLDER) || fileName.startsWith("vendor/") || fileName.startsWith("/vendor/")) { - return contents; - } - return locale.replaceLanguageInJavascript(contents); - }) .chain(contents -> StringUtils.replace(contents, ".p=\"/\"", ".p=\"" + getBasePath() + "/\"")) @@ -290,7 +272,7 @@ public class ResponseFactory { public Response cssResponse(@Untrusted String fileName) { try { - WebResource resource = config.isFalse(PluginSettings.LEGACY_FRONTEND) ? getPublicOrJarResource(fileName) : getResource(fileName); + WebResource resource = getPublicOrJarResource(fileName); String content = UnaryChain.of(resource.asString()) .chain(theme::replaceThemeColors) .chain(contents -> StringUtils.replace(contents, "/static", getBasePath() + "/static")) @@ -320,7 +302,7 @@ public class ResponseFactory { public Response imageResponse(@Untrusted String fileName) { try { - WebResource resource = config.isFalse(PluginSettings.LEGACY_FRONTEND) ? getPublicOrJarResource(fileName) : getResource(fileName); + WebResource resource = getPublicOrJarResource(fileName); ResponseBuilder responseBuilder = Response.builder() .setMimeType(MimeType.IMAGE) .setContent(resource) @@ -356,7 +338,7 @@ public class ResponseFactory { type = MimeType.FONT_BYTESTREAM; } try { - WebResource resource = config.isFalse(PluginSettings.LEGACY_FRONTEND) ? getPublicOrJarResource(fileName) : getResource(fileName); + WebResource resource = getPublicOrJarResource(fileName); ResponseBuilder responseBuilder = Response.builder() .setMimeType(type) .setContent(resource); @@ -422,7 +404,7 @@ public class ResponseFactory { try { return Response.builder() .setMimeType(MimeType.FAVICON) - .setContent(getResource("favicon.ico")) + .setContent(getPublicOrJarResource("favicon.ico")) .build(); } catch (UncheckedIOException e) { return forInternalError(e, "Could not read favicon"); @@ -431,7 +413,7 @@ public class ResponseFactory { public Response robotsResponse() { try { - WebResource resource = getResource("robots.txt"); + WebResource resource = getPublicOrJarResource("robots.txt"); Long lastModified = resource.getLastModified().orElseGet(System::currentTimeMillis); return Response.builder() .setMimeType("text/plain") @@ -523,13 +505,9 @@ public class ResponseFactory { Database db = dbSystem.getDatabase(); PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); if (player.getValue(PlayerKeys.REGISTERED).isPresent()) { - return forPage(request, pageFactory.playerPage(player)); + return forPage(request, pageFactory.playerPage()); } else { - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) { - return playerNotFound404(); - } else { - return forPage(request, pageFactory.reactPage(), 404); - } + return forPage(request, pageFactory.reactPage(), 404); } } catch (IllegalStateException notFoundLegacy) { return playerNotFound404(); @@ -574,7 +552,7 @@ public class ResponseFactory { try { return Response.builder() .setMimeType(MimeType.JSON) - .setContent(getResource(file)) + .setContent(getPublicOrJarResource(file)) .build(); } catch (UncheckedIOException e) { return forInternalError(e, "Could not read " + file); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java index 421342b7a..6d5fe79ae 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java @@ -17,11 +17,8 @@ package com.djrapitops.plan.delivery.webserver; import com.djrapitops.plan.SubSystem; -import com.djrapitops.plan.delivery.web.ResourceService; import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieStore; import com.djrapitops.plan.delivery.webserver.http.WebServer; -import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; import com.djrapitops.plan.storage.file.PublicHtmlFiles; import net.playeranalytics.plugin.server.PluginLogger; @@ -36,7 +33,6 @@ import javax.inject.Singleton; @Singleton public class WebServerSystem implements SubSystem { - private final PlanConfig config; private final Addresses addresses; private final ActiveCookieStore activeCookieStore; private final PublicHtmlFiles publicHtmlFiles; @@ -45,13 +41,11 @@ public class WebServerSystem implements SubSystem { @Inject public WebServerSystem( - PlanConfig config, Addresses addresses, ActiveCookieStore activeCookieStore, PublicHtmlFiles publicHtmlFiles, WebServer webServer, PluginLogger logger) { - this.config = config; this.addresses = addresses; this.activeCookieStore = activeCookieStore; this.publicHtmlFiles = publicHtmlFiles; @@ -63,24 +57,9 @@ public class WebServerSystem implements SubSystem { public void enable() { activeCookieStore.enable(); webServer.enable(); - if (config.isTrue(PluginSettings.LEGACY_FRONTEND)) { - if (!webServer.isAuthRequired()) { - ResourceService.getInstance().addStylesToResource("Plan", "error.html", ResourceService.Position.PRE_CONTENT, "./css/noauth.css"); - ResourceService.getInstance().addStylesToResource("Plan", "server.html", ResourceService.Position.PRE_CONTENT, "../css/noauth.css"); - ResourceService.getInstance().addStylesToResource("Plan", "player.html", ResourceService.Position.PRE_CONTENT, "../css/noauth.css"); - 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"); - ResourceService.getInstance().addStylesToResource("Plan", "query.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"); - } - } else { - if (publicHtmlFiles.findPublicHtmlResource("index.html").isPresent()) { - logger.info("Found index.html in public_html, using a custom React bundle!"); - } + + if (publicHtmlFiles.findPublicHtmlResource("index.html").isPresent()) { + logger.info("Found index.html in public_html, using a custom React bundle!"); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java index c81205105..495511e2c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.identification.ServerUUID; */ public enum DataID { PLAYERS, + PLAYERS_V2, SESSIONS, SERVERS, KILLS, diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java index 2999a76cf..0e34b72b4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java @@ -17,7 +17,6 @@ package com.djrapitops.plan.delivery.webserver.resolver; import com.djrapitops.plan.delivery.domain.auth.WebPermission; -import com.djrapitops.plan.delivery.rendering.html.Html; 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; @@ -26,7 +25,6 @@ import com.djrapitops.plan.delivery.web.resolver.request.WebUser; import com.djrapitops.plan.delivery.webserver.ResponseFactory; import com.djrapitops.plan.identification.UUIDUtility; import com.djrapitops.plan.settings.config.PlanConfig; -import com.djrapitops.plan.settings.config.paths.PluginSettings; import com.djrapitops.plan.utilities.dev.Untrusted; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -44,7 +42,6 @@ import java.util.UUID; @Singleton public class PlayerPageResolver implements Resolver { - private final PlanConfig config; private final ResponseFactory responseFactory; private final UUIDUtility uuidUtility; @@ -54,7 +51,6 @@ public class PlayerPageResolver implements Resolver { ResponseFactory responseFactory, UUIDUtility uuidUtility ) { - this.config = config; this.responseFactory = responseFactory; this.uuidUtility = uuidUtility; } @@ -99,10 +95,6 @@ public class PlayerPageResolver implements Resolver { return responseFactory.rawPlayerPageResponse(playerUUID); } - if (path.getPart(2).isPresent() && config.isTrue(PluginSettings.LEGACY_FRONTEND)) { - // Redirect /player/{uuid/name}/ to /player/{uuid} - return responseFactory.redirectResponse("../" + Html.encodeToURL(playerUUID.toString())); - } return responseFactory.playerPageResponse(request, playerUUID); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersJSONResolver.java new file mode 100644 index 000000000..d6c6626a6 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersJSONResolver.java @@ -0,0 +1,122 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.resolver.json; + +import com.djrapitops.plan.delivery.domain.auth.WebPermission; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.rendering.json.JSONFactory; +import com.djrapitops.plan.delivery.web.resolver.MimeType; +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.delivery.webserver.cache.AsyncJSONResolverService; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONStorage; +import com.djrapitops.plan.identification.Identifiers; +import com.djrapitops.plan.identification.ServerUUID; +import com.djrapitops.plan.utilities.dev.Untrusted; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; + +/** + * Resolves /v1/players JSON requests. + *

    + * Deprecated. + * + * @author AuroraLS3 + */ +@Singleton +@Path("/v1/players") +public class PlayersJSONResolver extends JSONResolver { + + private final Identifiers identifiers; + private final AsyncJSONResolverService jsonResolverService; + private final JSONFactory jsonFactory; + + @Inject + public PlayersJSONResolver( + Identifiers identifiers, + AsyncJSONResolverService jsonResolverService, + JSONFactory jsonFactory + ) { + this.identifiers = identifiers; + this.jsonResolverService = jsonResolverService; + this.jsonFactory = jsonFactory; + } + + @Override + public Formatter getHttpLastModifiedFormatter() {return jsonResolverService.getHttpLastModifiedFormatter();} + + @Override + public boolean canAccess(Request request) { + WebUser user = request.getUser().orElse(new WebUser("")); + if (request.getQuery().get("server").isPresent()) { + return user.hasPermission(WebPermission.PAGE_SERVER_PLAYERS); + } + // Assume players page + return user.hasPermission(WebPermission.ACCESS_PLAYERS) + || user.hasPermission(WebPermission.ACCESS_NETWORK) && user.hasPermission(WebPermission.PAGE_NETWORK_PLAYERS); + } + + @GET + @Operation( + description = "Get player table data for /players page or a server. Deprecated, use /v1/playersTable instead.", + responses = { + @ApiResponse(responseCode = "200", content = @Content(mediaType = MimeType.JSON)), + }, + parameters = @Parameter(in = ParameterIn.QUERY, name = "server", description = "Server identifier to get data for (optional)", examples = { + @ExampleObject("Server 1"), + @ExampleObject("1"), + @ExampleObject("1fb39d2a-eb82-4868-b245-1fad17d823b3"), + }), + requestBody = @RequestBody(content = @Content(examples = @ExampleObject())), + deprecated = true + ) + @Override + public Optional resolve(Request request) { + return Optional.of(getResponse(request)); + } + + private Response getResponse(Request request) { + JSONStorage.StoredJSON storedJSON = getStoredJSON(request); + return getCachedOrNewResponse(request, storedJSON); + } + + private JSONStorage.StoredJSON getStoredJSON(@Untrusted Request request) { + Optional timestamp = Identifiers.getTimestamp(request); + JSONStorage.StoredJSON storedJSON; + if (request.getQuery().get("server").isPresent()) { + ServerUUID serverUUID = identifiers.getServerUUID(request); // Can throw BadRequestException + storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS, serverUUID, uuid -> jsonFactory.serverPlayersTableJSON(uuid).toJSONMap()); + } else { + // Assume players page + storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS, () -> jsonFactory.networkPlayersTableJSON().toJSONMap()); + } + return storedJSON; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersTableJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersTableJSONResolver.java index ef009157f..2b3603595 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersTableJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/PlayersTableJSONResolver.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.delivery.webserver.resolver.json; import com.djrapitops.plan.delivery.domain.auth.WebPermission; +import com.djrapitops.plan.delivery.domain.datatransfer.PlayerListDto; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.rendering.json.JSONFactory; import com.djrapitops.plan.delivery.web.resolver.MimeType; @@ -34,6 +35,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.GET; @@ -44,12 +46,12 @@ import javax.inject.Singleton; import java.util.Optional; /** - * Resolves /v1/players JSON requests. + * Resolves /v1/playersTable JSON requests. * * @author AuroraLS3 */ @Singleton -@Path("/v1/players") +@Path("/v1/playersTable") public class PlayersTableJSONResolver extends JSONResolver { private final Identifiers identifiers; @@ -92,7 +94,7 @@ public class PlayersTableJSONResolver extends JSONResolver { @ExampleObject("1"), @ExampleObject("1fb39d2a-eb82-4868-b245-1fad17d823b3"), }), - requestBody = @RequestBody(content = @Content(examples = @ExampleObject())) + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = PlayerListDto.class))) ) @Override public Optional resolve(Request request) { @@ -109,10 +111,10 @@ public class PlayersTableJSONResolver extends JSONResolver { JSONStorage.StoredJSON storedJSON; if (request.getQuery().get("server").isPresent()) { ServerUUID serverUUID = identifiers.getServerUUID(request); // Can throw BadRequestException - storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS, serverUUID, jsonFactory::serverPlayersTableJSON); + storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS_V2, serverUUID, uuid -> jsonFactory.serverPlayersTableJSON(uuid).toPlayerList()); } else { // Assume players page - storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS, jsonFactory::networkPlayersTableJSON); + storedJSON = jsonResolverService.resolve(timestamp, DataID.PLAYERS_V2, () -> jsonFactory.networkPlayersTableJSON().toPlayerList()); } return storedJSON; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java index 2280d395f..086656a84 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java @@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.domain.DateMap; import com.djrapitops.plan.delivery.domain.auth.WebPermission; import com.djrapitops.plan.delivery.domain.datatransfer.InputFilterDto; import com.djrapitops.plan.delivery.domain.datatransfer.InputQueryDto; +import com.djrapitops.plan.delivery.domain.datatransfer.PlayerListDto; import com.djrapitops.plan.delivery.domain.datatransfer.ViewDto; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; @@ -255,13 +256,13 @@ public class QueryJSONResolver implements Resolver { return graphJSONCreator.createActivityGraphJSON(activityData); } - private Map getPlayersTableData(Set userIds, List serverUUIDs, long after, long before) { + private PlayerListDto getPlayersTableData(Set userIds, List serverUUIDs, long after, long before) { Database database = dbSystem.getDatabase(); return new PlayersTableJSONCreator( database.query(new QueryTablePlayersQuery(userIds, serverUUIDs, after, before, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD))), database.query(new ExtensionQueryResultTableDataQuery(serverInfo.getServerUUID(), userIds)), config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB), formatters, locale - ).toJSONMap(); + ).toPlayerList(); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/RootJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/RootJSONResolver.java index 3c94bc035..17d8b4385 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/RootJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/RootJSONResolver.java @@ -22,6 +22,8 @@ import com.djrapitops.plan.delivery.web.resolver.CompositeResolver; import com.djrapitops.plan.delivery.webserver.cache.AsyncJSONResolverService; import com.djrapitops.plan.delivery.webserver.cache.DataID; import com.djrapitops.plan.delivery.webserver.http.WebServer; +import com.djrapitops.plan.delivery.webserver.resolver.json.metadata.PreferencesJSONResolver; +import com.djrapitops.plan.delivery.webserver.resolver.json.metadata.StorePreferencesJSONResolver; import com.djrapitops.plan.identification.Identifiers; import dagger.Lazy; @@ -46,6 +48,7 @@ public class RootJSONResolver { private final WebGroupDeleteJSONResolver webGroupDeleteJSONResolver; private final CompositeResolver.Builder readOnlyResourcesBuilder; + private final StorePreferencesJSONResolver storePreferencesJSONResolver; private CompositeResolver resolver; @Inject @@ -57,6 +60,7 @@ public class RootJSONResolver { GraphsJSONResolver graphsJSONResolver, SessionsJSONResolver sessionsJSONResolver, + PlayersJSONResolver playersJSONResolver, PlayersTableJSONResolver playersTableJSONResolver, ServerOverviewJSONCreator serverOverviewJSONCreator, OnlineActivityOverviewJSONCreator onlineActivityOverviewJSONCreator, @@ -81,6 +85,8 @@ public class RootJSONResolver { RetentionJSONResolver retentionJSONResolver, PlayerJoinAddressJSONResolver playerJoinAddressJSONResolver, + PreferencesJSONResolver preferencesJSONResolver, + StorePreferencesJSONResolver storePreferencesJSONResolver, WebGroupJSONResolver webGroupJSONResolver, WebGroupPermissionJSONResolver webGroupPermissionJSONResolver, WebPermissionJSONResolver webPermissionJSONResolver, @@ -91,7 +97,8 @@ public class RootJSONResolver { this.asyncJSONResolverService = asyncJSONResolverService; readOnlyResourcesBuilder = CompositeResolver.builder() - .add("players", playersTableJSONResolver) + .add("players", playersJSONResolver) + .add("playersTable", playersTableJSONResolver) .add("sessions", sessionsJSONResolver) .add("kills", playerKillsJSONResolver) .add("graph", graphsJSONResolver) @@ -115,7 +122,8 @@ public class RootJSONResolver { .add("whoami", whoAmIJSONResolver) .add("extensionData", extensionJSONResolver) .add("retention", retentionJSONResolver) - .add("joinAddresses", playerJoinAddressJSONResolver); + .add("joinAddresses", playerJoinAddressJSONResolver) + .add("preferences", preferencesJSONResolver); this.webServer = webServer; // These endpoints require authentication to be enabled. @@ -124,6 +132,7 @@ public class RootJSONResolver { this.webPermissionJSONResolver = webPermissionJSONResolver; this.webGroupSaveJSONResolver = webGroupSaveJSONResolver; this.webGroupDeleteJSONResolver = webGroupDeleteJSONResolver; + this.storePreferencesJSONResolver = storePreferencesJSONResolver; } private ServerTabJSONResolver forJSON(DataID dataID, ServerTabJSONCreator tabJSONCreator, WebPermission permission) { @@ -139,6 +148,7 @@ public class RootJSONResolver { .add("permissions", webPermissionJSONResolver) .add("saveGroupPermissions", webGroupSaveJSONResolver) .add("deleteGroup", webGroupDeleteJSONResolver) + .add("storePreferences", storePreferencesJSONResolver) .build(); } else { resolver = readOnlyResourcesBuilder.build(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/PreferencesJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/PreferencesJSONResolver.java new file mode 100644 index 000000000..fb7415f5e --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/PreferencesJSONResolver.java @@ -0,0 +1,86 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.resolver.json.metadata; + +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.Preferences; +import com.djrapitops.plan.delivery.web.resolver.MimeType; +import com.djrapitops.plan.delivery.web.resolver.NoAuthResolver; +import com.djrapitops.plan.delivery.web.resolver.Response; +import com.djrapitops.plan.delivery.web.resolver.request.Request; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; +import com.djrapitops.plan.utilities.dev.Untrusted; +import com.djrapitops.plan.utilities.java.Maps; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; + +/** + * Represents /v1/preferences endpoint. + * + * @author AuroraLS3 + */ +@Singleton +@Path("/v1/preferences") +public class PreferencesJSONResolver implements NoAuthResolver { + + private final PlanConfig config; + private final DBSystem dbSystem; + + @Inject + public PreferencesJSONResolver(PlanConfig config, DBSystem dbSystem) { + this.config = config; + this.dbSystem = dbSystem; + } + + @GET + @Operation( + description = "Get user preferences (if they exist) and default preferences", + responses = { + @ApiResponse(responseCode = "200", content = @Content(mediaType = MimeType.JSON)) + }, + requestBody = @RequestBody(content = @Content(examples = @ExampleObject())) + ) + @Override + public Optional resolve(@Untrusted Request request) { + Preferences defaultPreferences = config.getDefaultPreferences(); + + return Optional.of(Response.builder() + .setJSONContent(Maps.builder(String.class, Preferences.class) + .put("defaultPreferences", defaultPreferences) + .put("preferences", getUserPreferences(request)) + .build() + ).build()); + } + + private Preferences getUserPreferences(Request request) { + // No user -> no preferences + // No stored preferences -> no preferences + return request.getUser() + .flatMap(user -> dbSystem.getDatabase().query(WebUserQueries.fetchPreferences(user))) + .orElse(null); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/StorePreferencesJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/StorePreferencesJSONResolver.java new file mode 100644 index 000000000..40fafba2d --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/metadata/StorePreferencesJSONResolver.java @@ -0,0 +1,87 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.resolver.json.metadata; + +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.Preferences; +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.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.StoreWebUserPreferencesTransaction; +import com.djrapitops.plan.utilities.dev.Untrusted; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +/** + * @author AuroraLS3 + */ +@Singleton +@Path("/v1/storePreferences") +public class StorePreferencesJSONResolver implements Resolver { + + private final DBSystem dbSystem; + + @Inject + public StorePreferencesJSONResolver(DBSystem dbSystem) {this.dbSystem = dbSystem;} + + @Override + public boolean canAccess(Request request) { + return request.getUser().isPresent(); + } + + @POST + @Operation( + description = "Update user preferences", + responses = { + @ApiResponse(responseCode = "200", description = "Storage was successful"), + @ApiResponse(responseCode = "400", description = "Not logged in (This endpoint only accepts requests if logged in)"), + @ApiResponse(responseCode = "400", description = "Request body does not match json format of preferences"), + }, + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = Preferences.class))) + ) + @Override + public Optional resolve(Request request) { + if (!request.getMethod().equals("POST")) { + throw new BadRequestException("This endpoint only accepts POST requests."); + } + WebUser user = request.getUser() + .orElseThrow(() -> new BadRequestException("This endpoint only accepts requests if logged in.")); + String preferencesBody = new String(request.getRequestBody(), StandardCharsets.UTF_8); + try { + Gson gson = new Gson(); + @Untrusted String syntaxSanitized = gson.toJson(gson.fromJson(preferencesBody, Preferences.class)); + dbSystem.getDatabase().executeTransaction(new StoreWebUserPreferencesTransaction(syntaxSanitized, user)); + } catch (JsonSyntaxException invalidSyntax) { + throw new BadRequestException("Request body does not match json format of preferences"); + } + return Optional.empty(); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionComponentData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionComponentData.java index 60c53f028..e9d419e42 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionComponentData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionComponentData.java @@ -16,11 +16,6 @@ */ package com.djrapitops.plan.extension.implementation.results; -import com.djrapitops.plan.component.Component; -import com.djrapitops.plan.component.ComponentOperation; -import com.djrapitops.plan.component.ComponentSvc; -import com.djrapitops.plan.delivery.rendering.html.Html; - /** * Represents component data returned by a ComponentProvider method. * @@ -44,8 +39,4 @@ public class ExtensionComponentData implements DescribedExtensionData { return value; } - public String getHtmlValue(ComponentSvc service) { - String legacy = service.convert(service.fromJson(value), ComponentOperation.LEGACY, Component.SECTION); - return Html.swapColorCodesToSpan(legacy); - } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java index e8201a1ab..69c2d739c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java @@ -18,6 +18,8 @@ package com.djrapitops.plan.extension.implementation.results; import com.djrapitops.plan.delivery.rendering.html.Html; +import java.util.Map; + /** * Represents double data returned by a DoubleProvider or PercentageProvider method. * @@ -51,9 +53,19 @@ public class ExtensionStringData implements DescribedExtensionData { return playerName; } - public String getFormattedValue() { - String withColors = Html.swapColorCodesToSpan(value); - return !playerName ? withColors : Html.LINK.create("../player/" + Html.encodeToURL(value), withColors); + public String getValue() { + return value; + } + + public Object getFormattedValue() { + if (playerName) { + return Map.of( + "link", "/player/" + Html.encodeToURL(value), + "text", value + ); + } else { + return value; + } } ExtensionStringData concatenate(ExtensionStringData other) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java index 1f296fc69..a9a63b34d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java @@ -16,7 +16,6 @@ */ package com.djrapitops.plan.extension.implementation.results; -import com.djrapitops.plan.delivery.rendering.html.structure.HtmlTable; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.table.Table; @@ -39,10 +38,6 @@ public class ExtensionTableData implements Comparable { this.tableColor = tableColor; } - public HtmlTable getHtmlTable() { - return HtmlTable.fromExtensionTable(table, tableColor); - } - public String getProviderName() { return providerName; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java index 7fed8663f..3a03ac61c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java @@ -16,6 +16,10 @@ */ package com.djrapitops.plan.settings.config; +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.GraphThresholds; +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.Preferences; +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.TimeFormat; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; import com.djrapitops.plan.settings.config.paths.ExportSettings; import com.djrapitops.plan.settings.config.paths.FormatSettings; import com.djrapitops.plan.settings.config.paths.key.Setting; @@ -154,6 +158,36 @@ public class PlanConfig extends Config { return worldAliasSettings; } + public Preferences getDefaultPreferences() { + return Preferences.builder() + .withDateFormatFull(get(FormatSettings.DATE_FULL)) + .withDateFormatNoSeconds(get(FormatSettings.DATE_NO_SECONDS)) + .withDateFormatClock(get(FormatSettings.DATE_CLOCK)) + .withRecentDaysInDateFormat(isTrue(FormatSettings.DATE_RECENT_DAYS)) + .withDecimalFormat(get(FormatSettings.DECIMALS)) + .withFirstDay(1) // 1 is Monday + .withTimeFormat(TimeFormat.builder() + .withYear(get(FormatSettings.YEAR)) + .withYears(get(FormatSettings.YEARS)) + .withMonth(get(FormatSettings.MONTH)) + .withMonths(get(FormatSettings.MONTHS)) + .withDay(get(FormatSettings.DAY)) + .withDays(get(FormatSettings.DAYS)) + .withHours(get(FormatSettings.HOURS)) + .withMinutes(get(FormatSettings.MINUTES)) + .withSeconds(get(FormatSettings.SECONDS)) + .withZero(get(FormatSettings.ZERO_SECONDS)) + .build()) + .withPlayerHeadImageUrl(get(DisplaySettings.PLAYER_HEAD_IMG_URL)) + .withTpsThresholds(new GraphThresholds( + get(DisplaySettings.GRAPH_TPS_THRESHOLD_HIGH), + get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))) + .withDiskThresholds(new GraphThresholds( + get(DisplaySettings.GRAPH_DISK_THRESHOLD_HIGH), + get(DisplaySettings.GRAPH_DISK_THRESHOLD_MED))) + .build(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ResourceSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ResourceSettings.java index 2b2878c10..dfba8e802 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ResourceSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ResourceSettings.java @@ -17,7 +17,6 @@ package com.djrapitops.plan.settings.config; import com.djrapitops.plan.settings.config.paths.CustomizedFileSettings; -import com.djrapitops.plan.settings.config.paths.PluginSettings; import com.djrapitops.plan.settings.config.paths.WebserverSettings; import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plan.utilities.dev.Untrusted; @@ -42,10 +41,6 @@ public class ResourceSettings { } public boolean shouldBeCustomized(String plugin, @Untrusted String fileName) { - if (config.isTrue(CustomizedFileSettings.WEB_DEV_MODE) && config.isTrue(PluginSettings.LEGACY_FRONTEND)) { - return true; - } - ConfigNode fileCustomization = getCustomizationConfigNode(); fileCustomization.setComment(Collections.singletonList("The files are placed in /Plan/web/ if the setting is 'true' when accessed.")); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java index 17a8da535..9ba6e9419 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java @@ -174,6 +174,9 @@ public class ConfigUpdater { new ConfigChange.MovedValue("Time.Thresholds.Activity_index.Playtime_threshold", "Time.Thresholds.Activity_index.Playtime_threshold.Time"), new ConfigChange.Removed("Plugin.Frontend_BETA"), + new ConfigChange.Removed("Plugin.Use_Legacy_Frontend"), + new ConfigChange.Removed("Customized_files.Enable_web_dev_mode"), + new ConfigChange.Removed("Customized_files.Plan"), }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/CustomizedFileSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/CustomizedFileSettings.java index ab32d5572..6ddae3c1e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/CustomizedFileSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/CustomizedFileSettings.java @@ -16,12 +16,10 @@ */ package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; import com.djrapitops.plan.settings.config.paths.key.Setting; import com.djrapitops.plan.settings.config.paths.key.StringSetting; public class CustomizedFileSettings { - public static final Setting WEB_DEV_MODE = new BooleanSetting("Customized_files.Enable_web_dev_mode"); public static final Setting PATH = new StringSetting("Customized_files.Path"); private CustomizedFileSettings() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java index 5d982e551..df05dd134 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java @@ -42,7 +42,6 @@ public class PluginSettings { public static final Setting CHECK_FOR_UPDATES = new BooleanSetting("Plugin.Update_notifications.Check_for_updates"); public static final Setting NOTIFY_ABOUT_DEV_RELEASES = new BooleanSetting("Plugin.Update_notifications.Notify_about_DEV_releases"); public static final Setting PROXY_COPY_CONFIG = new BooleanSetting("Plugin.Configuration.Allow_proxy_to_manage_settings"); - public static final Setting LEGACY_FRONTEND = new BooleanSetting("Plugin.Use_Legacy_Frontend"); private PluginSettings() { /* static variable class */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java index 22f9e8449..f2e33d773 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java @@ -16,8 +16,6 @@ */ package com.djrapitops.plan.settings.locale; -import com.djrapitops.plan.settings.locale.lang.HtmlLang; -import com.djrapitops.plan.settings.locale.lang.JSLang; import com.djrapitops.plan.settings.locale.lang.Lang; import com.djrapitops.plan.storage.file.FileResource; import com.djrapitops.plan.storage.file.PlanFiles; @@ -25,9 +23,9 @@ import com.djrapitops.plan.storage.file.PlanFiles; import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.HashMap; +import java.util.Objects; +import java.util.Optional; /** * Represents loaded language information. @@ -36,8 +34,6 @@ import java.util.regex.Pattern; */ public class Locale extends HashMap { - private static final Pattern FIND_SCRIPT = Pattern.compile("(|||)"); - public static Locale forLangCodeString(PlanFiles files, String code) throws IOException { return forLangCode(LangCode.fromString(code), files); } @@ -108,99 +104,6 @@ public class Locale extends HashMap { this.langCode = locale.langCode; } - public String replaceLanguageInHtml(String from) { - if (isEmpty()) { - return from; - } - - Matcher scriptMatcher = FIND_SCRIPT.matcher(from); - List foundScripts = new ArrayList<>(); - while (scriptMatcher.find()) { - foundScripts.add(scriptMatcher.toMatchResult().group(0)); - } - - TranslatedString translated = new TranslatedString(from); - Arrays.stream(HtmlLang.values()) - // Longest first so that entries that contain each other don't partially replace. - .sorted((one, two) -> Integer.compare( - two.getIdentifier().length(), - one.getIdentifier().length() - )) - .forEach(lang -> getNonDefault(lang).ifPresent(replacement -> - translated.translate(lang.getDefault(), replacement.toString())) - ); - - StringBuilder complete = new StringBuilder(translated.length()); - - String[] parts = FIND_SCRIPT.split(translated.toString()); - for (int i = 0; i < parts.length; i++) { - complete.append(parts[i]); - if (i < parts.length - 1) { - complete.append(replaceLanguageInJavascript(foundScripts.get(i))); - } - } - - return complete.toString(); - } - - public String replaceLanguageInJavascript(String from) { - if (isEmpty()) { - return from; - } - - TranslatedString translated = new TranslatedString(from); - Arrays.stream(JSLang.values()) - // Longest first so that entries that contain each other don't partially replace. - .sorted((one, two) -> Integer.compare( - two.getIdentifier().length(), - one.getIdentifier().length() - )) - .forEach(lang -> getNonDefault(lang).ifPresent(replacement -> - translated.translate(lang.getDefault(), replacement.toString())) - ); - - for (Lang extra : new Lang[]{ - HtmlLang.UNIT_NO_DATA, - HtmlLang.TITLE_WORLD_PLAYTIME, -// HtmlLang.LABEL_OPERATOR, -// HtmlLang.LABEL_BANNED, - HtmlLang.SIDE_SESSIONS, - HtmlLang.LABEL_PLAYTIME, - HtmlLang.LABEL_AFK_TIME, - HtmlLang.LABEL_LONGEST_SESSION, - HtmlLang.LABEL_SESSION_MEDIAN, - HtmlLang.LABEL_PLAYER_KILLS, - HtmlLang.LABEL_MOB_KILLS, - HtmlLang.LABEL_DEATHS, - HtmlLang.LABEL_PLAYERS_ONLINE, - HtmlLang.LABEL_REGISTERED, - HtmlLang.TITLE_SERVER, - HtmlLang.TITLE_LENGTH, - HtmlLang.TITLE_AVG_PING, - HtmlLang.TITLE_BEST_PING, - HtmlLang.TITLE_WORST_PING, - 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, - HtmlLang.TITLE_JOIN_ADDRESSES - - }) { - getNonDefault(extra).ifPresent(replacement -> - translated.translate(extra.getDefault(), replacement.toString())); - } - - return translated.toString(); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java deleted file mode 100644 index 3dc25243c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.settings.locale; - -import org.apache.commons.lang3.StringUtils; - -import java.util.LinkedList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Utility for translating String. - *

    - * Improves performance by avoiding a double for-each loop since this class can be considered final in the lambda - * expression in {@link Locale#replaceLanguageInHtml(String)}. - * - * @author AuroraLS3 - */ -class TranslatedString { - private static final Pattern LINK_MATCHER = Pattern.compile("http(s|)://[\\w.\\-_%/?$#@!()&=]+"); - - private final List translating = new LinkedList<>(); - - TranslatedString(String translating) { - final Matcher matcher = LINK_MATCHER.matcher(translating); - int start = 0; - while (matcher.find()) { - String link = translating.substring(matcher.start(), matcher.end()); - String prev = translating.substring(start, matcher.start()); - if (!prev.isEmpty()) { - this.translating.add(new Translatable(prev)); - } - start = matcher.end(); - this.translating.add(new LockedString(link)); - } - String remaining = translating.substring(start); - if (!remaining.isEmpty()) { - this.translating.add(new Translatable(remaining)); - } - } - - TranslatedString() { - } - - public void translate(String replace, String with) { - for (TranslatedString sub : translating) { - sub.translate(replace, with); - } - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - toString(builder); - return builder.toString(); - } - - public void toString(StringBuilder builder) { - for (TranslatedString sub : translating) { - sub.toString(builder); - } - } - - public int length() { - int length = 0; - for (TranslatedString sub : translating) { - length += sub.length(); - } - return length; - } - - static class Translatable extends TranslatedString { - - private String translating; - - Translatable(String translating) { - this.translating = translating; - } - - @Override - public void translate(String replace, String with) { - translating = StringUtils.replace(translating, replace, with); - } - - @Override - public void toString(StringBuilder builder) { - builder.append(translating); - } - - @Override - public int length() { - return translating.length(); - } - } - - static class LockedString extends TranslatedString { - final String text; - - LockedString(String text) { - this.text = text; - } - - @Override - public void translate(String replace, String with) { - /* Some elements should not be translated, like URLs */ - } - - @Override - public void toString(StringBuilder builder) { - builder.append(text); - } - - @Override - public int length() { - return text.length(); - } - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java index ec826a2e2..0e8263c2c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java @@ -269,6 +269,10 @@ public enum HtmlLang implements Lang { LABEL_SERVER_SELECTOR("html.label.serverSelector", "Server selector"), LABEL_APPLY("html.label.apply", "Apply"), + LABEL_TABLE_VISIBLE_COLUMNS("html.label.table.visibleColumns", "Visible columns"), + LABEL_TABLE_SHOW_N_OF_M("html.label.table.showNofM", "Showing {{n}} of {{mn}} entries"), + LABEL_TABLE_SHOW_PER_PAGE("html.label.table.showPerPage", "Show per page"), + LOGIN_LOGIN("html.login.login", "Login"), LOGIN_LOGOUT("html.login.logout", "Logout"), LOGIN_USERNAME("html.login.username", "Username"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java index 146ef5400..a5a9c89a2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java @@ -483,4 +483,21 @@ public class LargeStoreQueries { } }; } + + public static Executable storeAllPreferences(Map preferencesByUsername) { + if (preferencesByUsername.isEmpty()) return Executable.empty(); + + return new ExecBatchStatement(WebUserPreferencesTable.INSERT_STATEMENT) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + for (var entry : preferencesByUsername.entrySet()) { + String username = entry.getKey(); + String preferences = entry.getValue(); + statement.setString(1, preferences); + statement.setString(2, username); + statement.addBatch(); + } + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java index 95f0af4e8..4fc51d3b2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java @@ -17,12 +17,16 @@ package com.djrapitops.plan.storage.database.queries.objects; import com.djrapitops.plan.delivery.domain.auth.User; +import com.djrapitops.plan.delivery.domain.datatransfer.preferences.Preferences; +import com.djrapitops.plan.delivery.web.resolver.request.WebUser; import com.djrapitops.plan.storage.database.queries.Query; import com.djrapitops.plan.storage.database.queries.QueryAllStatement; import com.djrapitops.plan.storage.database.sql.building.Sql; import com.djrapitops.plan.storage.database.sql.tables.*; import com.djrapitops.plan.utilities.dev.Untrusted; import com.djrapitops.plan.utilities.java.Lists; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import org.intellij.lang.annotations.Language; import java.sql.ResultSet; @@ -239,4 +243,25 @@ public class WebUserQueries { } }; } + + public static Query> fetchPreferences(@Untrusted WebUser user) { + return db -> db.queryOptional(WebUserPreferencesTable.SELECT_BY_WEB_USERNAME, WebUserQueries::extractPreferences, user.getUsername()); + } + + private static Preferences extractPreferences(ResultSet set) throws SQLException { + try { + String preferences = set.getString(WebUserPreferencesTable.PREFERENCES); + return new Gson().fromJson(preferences, Preferences.class); + } catch (JsonSyntaxException jsonChangedIncompatibly) { + return null; + } + } + + public static Query> fetchAllPreferences() { + String sql = SELECT + WebUserPreferencesTable.PREFERENCES + "," + SecurityTable.USERNAME + + FROM + WebUserPreferencesTable.TABLE_NAME + " p" + + INNER_JOIN + SecurityTable.TABLE_NAME + " s ON s." + SecurityTable.ID + "=p." + WebUserPreferencesTable.WEB_USER_ID; + return db -> db.queryMap(sql, (results, to) -> + to.put(results.getString(SecurityTable.USERNAME), results.getString(WebUserPreferencesTable.PREFERENCES))); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java index f7db04d18..adb731308 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java @@ -18,13 +18,16 @@ package com.djrapitops.plan.storage.database.sql.tables; import com.djrapitops.plan.storage.database.DBType; import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder; -import com.djrapitops.plan.storage.database.sql.building.Sql; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; /** * Table information about 'plan_security' * * @author AuroraLS3 + * @see com.djrapitops.plan.storage.database.transactions.patches.LinkedToSecurityTablePatch * @see com.djrapitops.plan.storage.database.transactions.patches.SecurityTableGroupPatch + * @see com.djrapitops.plan.storage.database.transactions.patches.SecurityTableIdPatch */ public class SecurityTable { @@ -36,6 +39,7 @@ public class SecurityTable { public static final String SALT_PASSWORD_HASH = "salted_pass_hash"; public static final String GROUP_ID = "group_id"; + public static final String SELECT_ID_BY_USERNAME = SELECT + ID + FROM + TABLE_NAME + WHERE + USERNAME + "=?"; public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + USERNAME + ',' + LINKED_TO + ',' + @@ -48,11 +52,11 @@ public class SecurityTable { public static String createTableSQL(DBType dbType) { return CreateTableBuilder.create(TABLE_NAME, dbType) - .column(ID, Sql.INT).primaryKey() - .column(USERNAME, Sql.varchar(100)).notNull().unique() - .column(LINKED_TO, Sql.varchar(36)).defaultValue("''") - .column(SALT_PASSWORD_HASH, Sql.varchar(100)).notNull().unique() - .column(GROUP_ID, Sql.INT).notNull() + .column(ID, INT).primaryKey() + .column(USERNAME, varchar(100)).notNull().unique() + .column(LINKED_TO, varchar(36)).defaultValue("''") + .column(SALT_PASSWORD_HASH, varchar(100)).notNull().unique() + .column(GROUP_ID, INT).notNull() .foreignKey(GROUP_ID, WebGroupTable.TABLE_NAME, WebGroupTable.ID) .toString(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WebUserPreferencesTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WebUserPreferencesTable.java new file mode 100644 index 000000000..06389ff35 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WebUserPreferencesTable.java @@ -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 . + */ +package com.djrapitops.plan.storage.database.sql.tables; + +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder; +import com.djrapitops.plan.storage.database.sql.building.Sql; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +/** + * Represents plan_web_user_preferences. + * + * @author AuroraLS3 + */ +public class WebUserPreferencesTable { + + public static final String TABLE_NAME = "plan_web_user_preferences"; + + public static final String ID = "id"; + public static final String WEB_USER_ID = "web_user_id"; + public static final String PREFERENCES = "preferences"; + + public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + PREFERENCES + ',' + WEB_USER_ID + + ") VALUES (?, (" + SecurityTable.SELECT_ID_BY_USERNAME + "))"; + public static final String SELECT_BY_WEB_USERNAME = SELECT + PREFERENCES + FROM + TABLE_NAME + + WHERE + WEB_USER_ID + "=(" + SecurityTable.SELECT_ID_BY_USERNAME + ")"; + public static final String DELETE_BY_WEB_USERNAME = DELETE_FROM + TABLE_NAME + + WHERE + WEB_USER_ID + "=(" + SecurityTable.SELECT_ID_BY_USERNAME + ")"; + + private WebUserPreferencesTable() { + /* Static information class */ + } + + public static String createTableSQL(DBType dbType) { + return CreateTableBuilder.create(TABLE_NAME, dbType) + .column(ID, Sql.INT).primaryKey() + .column(PREFERENCES, "TEXT").notNull() + .column(WEB_USER_ID, Sql.INT) + .foreignKey(WEB_USER_ID, SecurityTable.TABLE_NAME, SecurityTable.ID) + .toString(); + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java index 6b5523858..b6172d17f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java @@ -88,6 +88,7 @@ public class BackupCopyTransaction extends RemoveEverythingTransaction { private void copyPlanWebUsers() { copy(LargeStoreQueries::storeAllPlanWebUsers, WebUserQueries.fetchAllUsers()); + copy(LargeStoreQueries::storeAllPreferences, WebUserQueries.fetchAllPreferences()); } private void copyPlanServerInformation() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreWebUserPreferencesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreWebUserPreferencesTransaction.java new file mode 100644 index 000000000..1468dcd72 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreWebUserPreferencesTransaction.java @@ -0,0 +1,60 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions; + +import com.djrapitops.plan.delivery.web.resolver.request.WebUser; +import com.djrapitops.plan.storage.database.sql.tables.WebUserPreferencesTable; +import com.djrapitops.plan.utilities.dev.Untrusted; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Stores user preferences as text in database. + * + * @author AuroraLS3 + */ +public class StoreWebUserPreferencesTransaction extends Transaction { + + @Untrusted + private final String preferences; + @Untrusted + private final WebUser user; + + public StoreWebUserPreferencesTransaction(String preferences, WebUser user) { + this.preferences = preferences; + this.user = user; + } + + @Override + protected void performOperations() { + execute(new ExecStatement(WebUserPreferencesTable.DELETE_BY_WEB_USERNAME) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, user.getUsername()); + } + }); + commitMidTransaction(); + execute(new ExecStatement(WebUserPreferencesTable.INSERT_STATEMENT) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, preferences); + statement.setString(2, user.getUsername()); + } + }); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java index 9ce63cf21..63097eee2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java @@ -49,6 +49,7 @@ public class RemoveEverythingTransaction extends Patch { clearTable(WebGroupToPermissionTable.TABLE_NAME); clearTable(WebPermissionTable.TABLE_NAME); clearTable(WebGroupTable.TABLE_NAME); + clearTable(WebUserPreferencesTable.TABLE_NAME); clearTable(SecurityTable.TABLE_NAME); clearTable(ServerTable.TABLE_NAME); clearTable(CookieTable.TABLE_NAME); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java index 52f9c7118..1b84c9b7a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.transactions.init; import com.djrapitops.plan.storage.database.sql.tables.*; import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction; +import com.djrapitops.plan.storage.database.transactions.patches.SecurityTableIdPatch; /** * Transaction that creates the table schema of Plan database. @@ -52,6 +53,9 @@ public class CreateTablesTransaction extends OperationCriticalTransaction { execute(WebPermissionTable.createTableSQL(dbType)); execute(WebGroupToPermissionTable.createTableSQL(dbType)); execute(SecurityTable.createTableSQL(dbType)); + // Ensure plan_security has id column + executeOther(new SecurityTableIdPatch()); + execute(WebUserPreferencesTable.createTableSQL(dbType)); // DataExtension tables execute(ExtensionIconTable.createTableSQL(dbType)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableGroupPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableGroupPatch.java index 466a2c740..f0567e808 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableGroupPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableGroupPatch.java @@ -48,16 +48,26 @@ public class SecurityTableGroupPatch extends Patch { @Override protected void applyPatch() { try { + boolean hasIdColumn = hasColumn(tableName, SecurityTable.ID) + // Either temp table doesn't exist or it has id column so one can be selected. + && !hasTable(tempTableName) || hasColumn(tempTableName, SecurityTable.ID); + if (hasIdColumn) { + dropForeignKeys(tableName); + ensureNoForeignKeyConstraints(tableName); + } + tempOldTable(); dropTable(tableName); execute(SecurityTable.createTableSQL(dbType)); execute("INSERT INTO " + tableName + " (" + + (hasIdColumn ? SecurityTable.ID + ',' : "") + SecurityTable.USERNAME + ',' + SecurityTable.LINKED_TO + ',' + SecurityTable.SALT_PASSWORD_HASH + ',' + SecurityTable.GROUP_ID + ") " + SELECT + + (hasIdColumn ? SecurityTable.ID + ',' : "") + SecurityTable.USERNAME + ',' + SecurityTable.LINKED_TO + ',' + SecurityTable.SALT_PASSWORD_HASH + ',' + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableIdPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableIdPatch.java new file mode 100644 index 000000000..95d354270 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SecurityTableIdPatch.java @@ -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 . + */ +package com.djrapitops.plan.storage.database.transactions.patches; + +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder; +import com.djrapitops.plan.storage.database.sql.tables.SecurityTable; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +/** + * Adds id to plan_security if it is not yet there. + * + * @author AuroraLS3 + */ +public class SecurityTableIdPatch extends Patch { + + private static final String TEMP_TABLE_NAME = "temp_security_id_patch"; + private static final String TABLE_NAME = SecurityTable.TABLE_NAME; + private static final String PERMISSION_LEVEL = "permission_level"; + + @Override + public boolean hasBeenApplied() { + return (hasColumn(TABLE_NAME, SecurityTable.ID)) + && !hasTable(TEMP_TABLE_NAME); + } + + @Override + protected void applyPatch() { + boolean hasUuidLinks = hasColumn(TABLE_NAME, SecurityTable.LINKED_TO); + if (hasUuidLinks) { + patchWithLinkedUuids(); + } else { + patchSimpleTable(); + } + } + + private void patchSimpleTable() { + try { + tempOldTable(); + dropTable(TABLE_NAME); + execute(CreateTableBuilder.create(TABLE_NAME, dbType) + .column(ID, INT).primaryKey() + .column(SecurityTable.USERNAME, varchar(100)).notNull().unique() + .column(SecurityTable.SALT_PASSWORD_HASH, varchar(100)).notNull().unique() + .column(PERMISSION_LEVEL, INT) + .toString()); + + execute("INSERT INTO " + TABLE_NAME + " (" + + SecurityTable.USERNAME + ',' + + SecurityTable.SALT_PASSWORD_HASH + ',' + + PERMISSION_LEVEL + + ") " + SELECT + + SecurityTable.USERNAME + ',' + + SecurityTable.SALT_PASSWORD_HASH + ',' + + PERMISSION_LEVEL + + FROM + TEMP_TABLE_NAME + ); + + dropTable(TEMP_TABLE_NAME); + } catch (Exception e) { + throw new DBOpException(SecurityTableGroupPatch.class.getSimpleName() + " failed.", e); + } + } + + private void patchWithLinkedUuids() { + try { + tempOldTable(); + dropTable(TABLE_NAME); + execute(CreateTableBuilder.create(TABLE_NAME, dbType) + .column(ID, INT).primaryKey() + .column(SecurityTable.USERNAME, varchar(100)).notNull().unique() + .column(SecurityTable.SALT_PASSWORD_HASH, varchar(100)).notNull().unique() + .column(SecurityTable.LINKED_TO, varchar(36)).defaultValue("''") + .column(PERMISSION_LEVEL, INT) + .toString()); + + execute("INSERT INTO " + TABLE_NAME + " (" + + SecurityTable.USERNAME + ',' + + SecurityTable.SALT_PASSWORD_HASH + ',' + + SecurityTable.LINKED_TO + ',' + + PERMISSION_LEVEL + + ") " + SELECT + + SecurityTable.USERNAME + ',' + + SecurityTable.SALT_PASSWORD_HASH + ',' + + SecurityTable.LINKED_TO + ',' + + PERMISSION_LEVEL + + FROM + TEMP_TABLE_NAME + ); + + dropTable(TEMP_TABLE_NAME); + } catch (Exception e) { + throw new DBOpException(SecurityTableGroupPatch.class.getSimpleName() + " failed.", e); + } + } + + private void tempOldTable() { + if (!hasTable(TEMP_TABLE_NAME)) { + renameTable(TABLE_NAME, TEMP_TABLE_NAME); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/Maps.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/Maps.java index cac71d1ee..3fd50b835 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/Maps.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/Maps.java @@ -60,7 +60,9 @@ public class Maps { } public Builder put(K key, V value) { - map.put(key, value); + if (value != null) { + map.put(key, value); + } return this; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/version/VersionChecker.java b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionChecker.java index 3a51d6e59..2fa6872b9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/version/VersionChecker.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionChecker.java @@ -49,8 +49,6 @@ public class VersionChecker implements SubSystem { protected final RunnableFactory runnableFactory; protected final ErrorLogger errorLogger; - private static final String DOWNLOAD_ICON_HTML = " "; - protected VersionInfo newVersionAvailable; @Inject @@ -126,49 +124,6 @@ public class VersionChecker implements SubSystem { return Optional.ofNullable(newVersionAvailable); } - public Optional getUpdateButton() { - return getNewVersionAvailable().map(v -> { - String reduceFontSize = v.getVersion().compareTo(new VersionNumber("5.2 build 999")) > 0 ? - "font-size: 0.95rem;" : ""; - return ""; - } - ); - } - - public String getCurrentVersionButton() { - return ""; - } - - public String getUpdateModal() { - return getNewVersionAvailable() - .map(v -> "

    " + - "
    " + - DOWNLOAD_ICON_HTML + locale.getString(PluginLang.VERSION_UPDATE_AVAILABLE, v.getVersion().asString()) + - "
    " + - "
    " + // Close modal-header - "
    " + - "

    " + locale.getString(PluginLang.VERSION_CURRENT, getCurrentVersion()) + ". " + locale.getString(PluginLang.VERSION_UPDATE_INFO) + - (v.isRelease() ? "" : "
    " + locale.getString(PluginLang.VERSION_UPDATE_DEV)) + "

    " + - "" + - " " + locale.getString(PluginLang.VERSION_CHANGE_LOG) + "" + - "" + - DOWNLOAD_ICON_HTML + locale.getString(PluginLang.VERSION_DOWNLOAD, v.getVersion().asString()) + "" + - "
    ") // Close modal-body - .orElse("
    " + - "
    " + - " " + locale.getString(PluginLang.VERSION_CURRENT, getCurrentVersion()) + - "
    " + - "
    " + // Close modal-header - "
    " + - "

    " + locale.getString(PluginLang.VERSION_NEWEST) + "

    " + - "
    "); // Close modal-body - } - public String getCurrentVersion() { return currentVersion.asString(); } diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.yml index 5844d4df7..cad585090 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.yml @@ -567,6 +567,10 @@ html: sessions: "会话" sortBy: "排序方式" stacked: "堆叠" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "主题选择" thirdDeadliestWeapon: "第三致命的 PVP 武器" thirtyDays: "30 天" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_CS.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_CS.yml index d26162b85..70d7c272c 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_CS.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_CS.yml @@ -567,6 +567,10 @@ html: sessions: "Relace" sortBy: "Seřadit podle" stacked: "Zaplnění" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Zvolené téma" thirdDeadliestWeapon: "3. PvP Zbraň" thirtyDays: "30 dní" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.yml index b46cb7e13..640aefa53 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.yml @@ -567,6 +567,10 @@ html: sessions: "Sitzungen" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Thema ausgewählt" thirdDeadliestWeapon: "3. PvP Waffe" thirtyDays: "30 Tage" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.yml index 9c67aaa2c..49e70b223 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.yml @@ -567,6 +567,10 @@ html: sessions: "Sessions" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Theme Select" thirdDeadliestWeapon: "3rd PvP Weapon" thirtyDays: "30 days" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_ES.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_ES.yml index 57479798f..a4202aa41 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_ES.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_ES.yml @@ -567,6 +567,10 @@ html: sessions: "Sesiones" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Selección de tema" thirdDeadliestWeapon: "3ª arma PvP" thirtyDays: "30 días" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.yml index 51061e42b..6b3a7f143 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.yml @@ -567,6 +567,10 @@ html: sessions: "Istunnot" sortBy: "Järjestä" stacked: "Päällekäin" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Teemavalikko" thirdDeadliestWeapon: "3. PvP Ase" thirtyDays: "30 päivää" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.yml index 1dfde5b91..65c2ec266 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.yml @@ -567,6 +567,10 @@ html: sessions: "Sessions" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Sélection du Thème" thirdDeadliestWeapon: "3ᵉ Arme de Combat" thirtyDays: "30 jours" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_IT.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_IT.yml index 24b4ea792..70891f75b 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_IT.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_IT.yml @@ -567,6 +567,10 @@ html: sessions: "Sessioni" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Selezione Tema" thirdDeadliestWeapon: "3° Arma PvP Preferita" thirtyDays: "30 giorni" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_JA.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.yml index d5c2e120d..2e095995d 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_JA.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.yml @@ -567,6 +567,10 @@ html: sessions: "接続履歴" sortBy: "並べ替え" stacked: "スタックビュー" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "テーマ選択" thirdDeadliestWeapon: "3番目にPvPで使用されている武器" thirtyDays: "1ヶ月" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_KO.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_KO.yml index 14aaeb5a4..2edf811fa 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_KO.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_KO.yml @@ -567,6 +567,10 @@ html: sessions: "세션 목록" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "테마 선택" thirdDeadliestWeapon: "3rd PvP 무기" thirtyDays: "30일" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_NL.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_NL.yml index 19101b3bf..ed19cf440 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_NL.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_NL.yml @@ -567,6 +567,10 @@ html: sessions: "Sessies" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Thema selecteren" thirdDeadliestWeapon: "3e PvP-wapen" thirtyDays: "30 dagen" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.yml index 2e9f64df0..b64755be4 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.yml @@ -567,6 +567,10 @@ html: sessions: "Sessões" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Theme Select" thirdDeadliestWeapon: "3rd PvP Weapon" thirtyDays: "30 days" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_RU.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_RU.yml index 8f4e037cb..9f9e29bf0 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_RU.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_RU.yml @@ -567,6 +567,10 @@ html: sessions: "Сессии" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Выбор темы" thirdDeadliestWeapon: "3-е PvP оружие" thirtyDays: "30 дней" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_TR.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_TR.yml index 7f6e8f809..d95915ef9 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_TR.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_TR.yml @@ -567,6 +567,10 @@ html: sessions: "Oturumlar" sortBy: "Sort By" stacked: "Stacked" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "Tema Seçimi" thirdDeadliestWeapon: "3. PvP Silahı" thirtyDays: "30 gün" diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_ZH_TW.yml b/Plan/common/src/main/resources/assets/plan/locale/locale_ZH_TW.yml index 3631416d0..124c7ce27 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_ZH_TW.yml +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_ZH_TW.yml @@ -567,6 +567,10 @@ html: sessions: "會話" sortBy: "排序方式" stacked: "堆疊的" + table: + showNofM: "Showing {{n}} of {{mn}} entries" + showPerPage: "Show per page" + visibleColumns: "Visible columns" themeSelect: "主題色選擇" thirdDeadliestWeapon: "第三致命的 PvP 武器" thirtyDays: "30 天" diff --git a/Plan/common/src/main/resources/assets/plan/web/css/noauth.css b/Plan/common/src/main/resources/assets/plan/web/css/noauth.css deleted file mode 100644 index 2ea8ef56d..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/noauth.css +++ /dev/null @@ -1,3 +0,0 @@ -#logout-button { - display: none; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/querybutton.css b/Plan/common/src/main/resources/assets/plan/web/css/querybutton.css deleted file mode 100644 index 902a714ba..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/querybutton.css +++ /dev/null @@ -1,3 +0,0 @@ -.query-buttons { - display: inline-block !important; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css b/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css deleted file mode 100644 index b58acd7cd..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css +++ /dev/null @@ -1,3291 +0,0 @@ -/*! - * SB Admin 2 custom changes - * Compiled from a diff between SB Admin 2 - * and Plan's version of SB Admin 2 - */ -img { - height: auto; -} - -.table { - margin-bottom: unset; - color: unset; - background-color: transparent; -} - -.table th, -.table td { - border-top: 1px solid #dddfeb; -} - -.table thead th { - border-bottom: 2px solid #dddfeb !important; -} - -.table tbody + tbody { - border-top: 2px solid #dddfeb; -} - -.table-bordered { - border: 1px solid #dddfeb; -} - -.table-bordered th, -.table-bordered td { - border: 1px solid #dddfeb; -} - -/* SB Admin 2 table changes start */ - -.table-hover tbody tr:hover { - color: #858796; -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #cdd8f6; -} - -.table-primary th, -.table-primary td, -.table-primary thead th, -.table-primary tbody + tbody { - border-color: #a3b6ee; -} - -.table-hover .table-primary:hover { - background-color: #b7c7f2; -} - -.table-hover .table-primary:hover > td, -.table-hover .table-primary:hover > th { - background-color: #b7c7f2; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #dddde2; -} - -.table-secondary th, -.table-secondary td, -.table-secondary thead th, -.table-secondary tbody + tbody { - border-color: #c0c1c8; -} - -.table-hover .table-secondary:hover { - background-color: #cfcfd6; -} - -.table-hover .table-secondary:hover > td, -.table-hover .table-secondary:hover > th { - background-color: #cfcfd6; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #bff0de; -} - -.table-success th, -.table-success td, -.table-success thead th, -.table-success tbody + tbody { - border-color: #89e2c2; -} - -.table-hover .table-success:hover { - background-color: #aaebd3; -} - -.table-hover .table-success:hover > td, -.table-hover .table-success:hover > th { - background-color: #aaebd3; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #c7ebf1; -} - -.table-info th, -.table-info td, -.table-info thead th, -.table-info tbody + tbody { - border-color: #96dbe4; -} - -.table-hover .table-info:hover { - background-color: #b3e4ec; -} - -.table-hover .table-info:hover > td, -.table-hover .table-info:hover > th { - background-color: #b3e4ec; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #fceec9; -} - -.table-warning th, -.table-warning td, -.table-warning thead th, -.table-warning tbody + tbody { - border-color: #fadf9b; -} - -.table-hover .table-warning:hover { - background-color: #fbe6b1; -} - -.table-hover .table-warning:hover > td, -.table-hover .table-warning:hover > th { - background-color: #fbe6b1; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #f8ccc8; -} - -.table-danger th, -.table-danger td, -.table-danger thead th, -.table-danger tbody + tbody { - border-color: #f3a199; -} - -.table-hover .table-danger:hover { - background-color: #f5b7b1; -} - -.table-hover .table-danger:hover > td, -.table-hover .table-danger:hover > th { - background-color: #f5b7b1; -} - -.table-light th, -.table-light td, -.table-light thead th, -.table-light tbody + tbody { - border-color: #fbfcfd; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #d1d1d5; -} - -.table-dark th, -.table-dark td, -.table-dark thead th, -.table-dark tbody + tbody { - border-color: #a9aab1; -} - -.table-hover .table-dark:hover { - background-color: #c4c4c9; -} - -.table-hover .table-dark:hover > td, -.table-hover .table-dark:hover > th { - background-color: #c4c4c9; -} - -/* SB Admin 2 table changes end */ - -.table .thead-dark th { - background-color: #3a3b45; - border-color: #4b4d5a; -} - -.table .thead-light th { - color: #6e707e; - background-color: #eaecf4; - border-color: #dddfeb; -} - -.table-dark { - color: #fff; - background-color: #3a3b45; -} - -.table-dark th, -.table-dark td, -.table-dark thead th { - border-color: #4b4d5a !important; -} - -.table-dark.table-hover tbody tr:hover { - color: unset; -} - -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - background-color: unset; -} - -.page-item.active .page-link { - background-color: #368F17; - border-color: #368F17; -} - -.modal-footer { - border-top: 1px solid #eaecf4; -} - -.bg-white { - background-color: unset; -} - -@media print { - .table-dark th, - .table-dark td, - .table-dark thead th, - .table-dark tbody + tbody { - border-color: #dddfeb; - } - .table .thead-dark th { - border-color: #dddfeb; - } -} - -.chart-area { - position: relative; - height: 22rem; - width: 100%; - padding: 0.5rem; - text-align: center; -} - -.chart-bar { - position: relative; - height: 20rem; - width: 100%; -} - -.chart-pie { - position: relative; - height: calc(20rem - 43px); - width: 100%; -} - -/*! - * Start Bootstrap - SB Admin 2 v4.1.3 (https://startbootstrap.com/theme/sb-admin-2) - * Copyright 2013-2020 Start Bootstrap - * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin-2/blob/master/LICENSE) - * Changes compiled from SB Admin/Bootstrap diff - */ - -:root { - --bs-blue: #4e73df; - --bs-red: #e74a3b; - --bs-yellow: #f6c23e; - --bs-green: #1cc88a; - --bs-teal: #20c9a6; - --bs-cyan: #36b9cc; - --bs-gray: #858796; - --bs-gray-dark: #5a5c69; - --bs-primary: #4e73df; - --bs-secondary: #858796; - --bs-success: #1cc88a; - --bs-info: #36b9cc; - --bs-warning: #f6c23e; - --bs-danger: #e74a3b; - --bs-light: #f8f9fc; - --bs-dark: #5a5c69; - --bs-font-sans-serif: "Nunito", system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; -} - -html { - position: relative; - min-height: 100%; -} - -body { - font-family: "Nunito", system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; - height: 100%; - color: #858796; -} - -a { - color: #4e73df; -} - -a:hover { - color: #224abe; -} - -a:focus { - outline: none; -} - -caption { - color: #858796; -} - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - font-weight: 400; -} - -.blockquote-footer { - color: #858796; -} - -.img-thumbnail { - border: 1px solid #dddfeb; - border-radius: 0.35rem; -} - -.figure-caption { - color: #858796; -} - -kbd { - background-color: #3a3b45; -} - -pre { - color: #3a3b45; -} - -.form-control { - color: #6e707e; - border: 1px solid #d1d3e2; - border-radius: 0.35rem; -} - -.form-control:-moz-focusring { - text-shadow: 0 0 0 #6e707e; -} - -.form-control:focus { - color: #6e707e; - border-color: #bac8f3; - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.form-control::-webkit-input-placeholder { - color: #858796; -} - -.form-control::-moz-placeholder { - color: #858796; -} - -.form-control:-ms-input-placeholder { - color: #858796; -} - -.form-control::-ms-input-placeholder { - color: #858796; -} - -.form-control::placeholder { - color: #858796; -} - -.form-control:disabled, .form-control[readonly] { - background-color: #eaecf4; -} - -select.form-control:focus::-ms-value { - color: #6e707e; -} - -.form-control-plaintext { - color: #858796; -} - -.form-check-input[disabled] ~ .form-check-label, -.form-check-input:disabled ~ .form-check-label { - color: #858796; -} - -.valid-feedback { - color: #1cc88a; -} - -.valid-tooltip { - background-color: rgba(28, 200, 138, 0.9); - border-radius: 0.35rem; -} - -.was-validated .form-control:valid, .form-control.is-valid { - border-color: #1cc88a; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); -} - -.was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #1cc88a; - box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); -} - -.was-validated .form-select:valid, .form-select.is-valid { - border-color: #1cc88a; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-select:valid:focus, .form-select.is-valid:focus { - border-color: #1cc88a; - box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); -} - -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #1cc88a; -} - -.invalid-feedback { - color: #e74a3b; -} - -.invalid-tooltip { - background-color: rgba(231, 74, 59, 0.9); - border-radius: 0.35rem; -} - -.was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #e74a3b; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23e74a3b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23e74a3b' stroke='none'/%3e%3c/svg%3e"); -} - -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #e74a3b; - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); -} - -.was-validated .form-select:invalid, .form-select.is-invalid { - border-color: #e74a3b; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23e74a3b' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23e74a3b' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { - border-color: #e74a3b; - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); -} - -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #e74a3b; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { - color: #e74a3b; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { - border-color: #e74a3b; -} - -.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { - border-color: #ed7468; - background-color: #ed7468; -} - -.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); -} - -.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #e74a3b; -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { - border-color: #e74a3b; -} - -.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { - border-color: #e74a3b; - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); -} - -.btn { - color: #858796; - border-radius: 0.35rem; -} - -.btn:hover { - color: #858796; -} - -.btn:focus, .btn.focus { - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.btn-primary { - background-color: #4e73df; - border-color: #4e73df; -} - -.btn-primary:hover { - background-color: #2e59d9; - border-color: #2653d4; -} - -.btn-primary:focus, .btn-primary.focus { - background-color: #2e59d9; - border-color: #2653d4; - box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); -} - -.btn-primary.disabled, .btn-primary:disabled { - background-color: #4e73df; - border-color: #4e73df; -} - -.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, -.show > .btn-primary.dropdown-toggle { - background-color: #2653d4; - border-color: #244ec9; -} - -.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); -} - -.btn-secondary { - background-color: #858796; - border-color: #858796; -} - -.btn-secondary:hover { - background-color: #717384; - border-color: #6b6d7d; -} - -.btn-secondary:focus, .btn-secondary.focus { - background-color: #717384; - border-color: #6b6d7d; - box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); -} - -.btn-secondary.disabled, .btn-secondary:disabled { - background-color: #858796; - border-color: #858796; -} - -.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, -.show > .btn-secondary.dropdown-toggle { - background-color: #6b6d7d; - border-color: #656776; -} - -.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); -} - -.btn-success { - background-color: #1cc88a; - border-color: #1cc88a; -} - -.btn-success:hover { - background-color: #17a673; - border-color: #169b6b; -} - -.btn-success:focus, .btn-success.focus { - background-color: #17a673; - border-color: #169b6b; - box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); -} - -.btn-success.disabled, .btn-success:disabled { - background-color: #1cc88a; - border-color: #1cc88a; -} - -.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, -.show > .btn-success.dropdown-toggle { - background-color: #169b6b; - border-color: #149063; -} - -.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); -} - -.btn-info { - background-color: #36b9cc; - border-color: #36b9cc; -} - -.btn-info:hover { - background-color: #2c9faf; - border-color: #2a96a5; -} - -.btn-info:focus, .btn-info.focus { - background-color: #2c9faf; - border-color: #2a96a5; - box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); -} - -.btn-info.disabled, .btn-info:disabled { - background-color: #36b9cc; - border-color: #36b9cc; -} - -.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, -.show > .btn-info.dropdown-toggle { - background-color: #2a96a5; - border-color: #278c9b; -} - -.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); -} - -.btn-warning { - color: #fff; - background-color: #f6c23e; - border-color: #f6c23e; -} - -.btn-warning:hover { - color: #fff; - background-color: #f4b619; - border-color: #f4b30d; -} - -.btn-warning:focus, .btn-warning.focus { - color: #fff; - background-color: #f4b619; - border-color: #f4b30d; - box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); -} - -.btn-warning.disabled, .btn-warning:disabled { - color: #fff; - background-color: #f6c23e; - border-color: #f6c23e; -} - -.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, -.show > .btn-warning.dropdown-toggle { - color: #fff; - background-color: #f4b30d; - border-color: #e9aa0b; -} - -.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); -} - -.btn-danger { - background-color: #e74a3b; - border-color: #e74a3b; -} - -.btn-danger:hover { - background-color: #e02d1b; - border-color: #d52a1a; -} - -.btn-danger:focus, .btn-danger.focus { - background-color: #e02d1b; - border-color: #d52a1a; - box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); -} - -.btn-danger.disabled, .btn-danger:disabled { - background-color: #e74a3b; - border-color: #e74a3b; -} - -.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, -.show > .btn-danger.dropdown-toggle { - background-color: #d52a1a; - border-color: #ca2819; -} - -.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); -} - -.btn-light { - color: #3a3b45; - background-color: #f8f9fc; - border-color: #f8f9fc; -} - -.btn-light:hover { - color: #3a3b45; - background-color: #dde2f1; - border-color: #d4daed; -} - -.btn-light:focus, .btn-light.focus { - color: #3a3b45; - background-color: #dde2f1; - border-color: #d4daed; - box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); -} - -.btn-light.disabled, .btn-light:disabled { - color: #3a3b45; - background-color: #f8f9fc; - border-color: #f8f9fc; -} - -.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, -.show > .btn-light.dropdown-toggle { - color: #3a3b45; - background-color: #d4daed; - border-color: #cbd3e9; -} - -.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); -} - -.btn-dark { - background-color: #5a5c69; - border-color: #5a5c69; -} - -.btn-dark:hover { - background-color: #484a54; - border-color: #42444e; -} - -.btn-dark:focus, .btn-dark.focus { - background-color: #484a54; - border-color: #42444e; - box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); -} - -.btn-dark.disabled, .btn-dark:disabled { - background-color: #5a5c69; - border-color: #5a5c69; -} - -.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, -.show > .btn-dark.dropdown-toggle { - background-color: #42444e; - border-color: #3d3e47; -} - -.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); -} - -.btn-outline-primary { - color: #4e73df; - border-color: #4e73df; -} - -.btn-outline-primary:hover { - background-color: #4e73df; - border-color: #4e73df; -} - -.btn-outline-primary:focus, .btn-outline-primary.focus { - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); -} - -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #4e73df; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, -.show > .btn-outline-primary.dropdown-toggle { - background-color: #4e73df; - border-color: #4e73df; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); -} - -.btn-outline-secondary { - color: #858796; - border-color: #858796; -} - -.btn-outline-secondary:hover { - background-color: #858796; - border-color: #858796; -} - -.btn-outline-secondary:focus, .btn-outline-secondary.focus { - box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); -} - -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #858796; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, -.show > .btn-outline-secondary.dropdown-toggle { - background-color: #858796; - border-color: #858796; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); -} - -.btn-outline-success { - color: #1cc88a; - border-color: #1cc88a; -} - -.btn-outline-success:hover { - background-color: #1cc88a; - border-color: #1cc88a; -} - -.btn-outline-success:focus, .btn-outline-success.focus { - box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); -} - -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #1cc88a; -} - -.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, -.show > .btn-outline-success.dropdown-toggle { - background-color: #1cc88a; - border-color: #1cc88a; -} - -.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); -} - -.btn-outline-info { - color: #36b9cc; - border-color: #36b9cc; -} - -.btn-outline-info:hover { - background-color: #36b9cc; - border-color: #36b9cc; -} - -.btn-outline-info:focus, .btn-outline-info.focus { - box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); -} - -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #36b9cc; -} - -.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, -.show > .btn-outline-info.dropdown-toggle { - background-color: #36b9cc; - border-color: #36b9cc; -} - -.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); -} - -.btn-outline-warning { - color: #f6c23e; - border-color: #f6c23e; -} - -.btn-outline-warning:hover { - color: #fff; - background-color: #f6c23e; - border-color: #f6c23e; -} - -.btn-outline-warning:focus, .btn-outline-warning.focus { - box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); -} - -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #f6c23e; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, -.show > .btn-outline-warning.dropdown-toggle { - color: #fff; - background-color: #f6c23e; - border-color: #f6c23e; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); -} - -.btn-outline-danger { - color: #e74a3b; - border-color: #e74a3b; -} - -.btn-outline-danger:hover { - background-color: #e74a3b; - border-color: #e74a3b; -} - -.btn-outline-danger:focus, .btn-outline-danger.focus { - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); -} - -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #e74a3b; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, -.show > .btn-outline-danger.dropdown-toggle { - background-color: #e74a3b; - border-color: #e74a3b; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); -} - -.btn-outline-light { - color: #f8f9fc; - border-color: #f8f9fc; -} - -.btn-outline-light:hover { - color: #3a3b45; - background-color: #f8f9fc; - border-color: #f8f9fc; -} - -.btn-outline-light:focus, .btn-outline-light.focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); -} - -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #f8f9fc; -} - -.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, -.show > .btn-outline-light.dropdown-toggle { - color: #3a3b45; - background-color: #f8f9fc; - border-color: #f8f9fc; -} - -.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); -} - -.btn-outline-dark { - color: #5a5c69; - border-color: #5a5c69; -} - -.btn-outline-dark:hover { - background-color: #5a5c69; - border-color: #5a5c69; -} - -.btn-outline-dark:focus, .btn-outline-dark.focus { - box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); -} - -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #5a5c69; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, -.show > .btn-outline-dark.dropdown-toggle { - background-color: #5a5c69; - border-color: #5a5c69; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); -} - -.btn-link { - color: #4e73df; -} - -.btn-link:hover { - color: #224abe; -} - -.btn-link:disabled, .btn-link.disabled { - color: #858796; -} - -.collapsing { - transition: height 0.15s ease; -} - -.dropdown-menu { - font-size: 0.85rem; - color: #858796; - border: 1px solid #e3e6f0; - border-radius: 0.35rem; -} - -.dropdown-divider { - border-top: 1px solid #eaecf4; -} - -.dropdown-item { - color: #3a3b45; -} - -.dropdown-item:hover, .dropdown-item:focus { - color: #2e2f37; - background-color: #f8f9fc; -} - -.dropdown-item.active, .dropdown-item:active { - background-color: #4e73df; -} - -.dropdown-item.disabled, .dropdown-item:disabled { - color: #858796; -} - -.dropdown-header { - color: #858796; -} - -.dropdown-item-text { - color: #3a3b45; -} - -.input-group-text { - color: #6e707e; - background-color: #eaecf4; - border: 1px solid #d1d3e2; - border-radius: 0.35rem; -} - -.form-select { - color: #6e707e; - background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; - border: 1px solid #d1d3e2; - border-radius: 0.35rem; -} - -.form-select:focus { - border-color: #bac8f3; - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.form-select:focus::-ms-value { - color: #6e707e; -} - -.form-select:disabled { - color: #858796; - background-color: #eaecf4; -} - -.form-select:-moz-focusring { - text-shadow: 0 0 0 #6e707e; -} - -.custom-file-input:focus ~ .custom-file-label { - border-color: #bac8f3; - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.custom-file-input[disabled] ~ .custom-file-label, -.custom-file-input:disabled ~ .custom-file-label { - background-color: #eaecf4; -} - -.custom-file-label { - color: #6e707e; - border: 1px solid #d1d3e2; - border-radius: 0.35rem; -} - -.custom-file-label::after { - color: #6e707e; - background-color: #eaecf4; - border-radius: 0 0.35rem 0.35rem 0; -} - -.custom-range:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.custom-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.custom-range:focus::-ms-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.custom-range::-webkit-slider-thumb { - background-color: #4e73df; -} - -.custom-range::-webkit-slider-thumb:active { - background-color: #e5ebfa; -} - -.custom-range::-webkit-slider-runnable-track { - background-color: #dddfeb; -} - -.custom-range::-moz-range-thumb { - background-color: #4e73df; -} - -.custom-range::-moz-range-thumb:active { - background-color: #e5ebfa; -} - -.custom-range::-moz-range-track { - background-color: #dddfeb; -} - -.custom-range::-ms-thumb { - background-color: #4e73df; -} - -.custom-range::-ms-thumb:active { - background-color: #e5ebfa; -} - -.custom-range::-ms-fill-lower { - background-color: #dddfeb; -} - -.custom-range::-ms-fill-upper { - background-color: #dddfeb; -} - -.custom-range:disabled::-webkit-slider-thumb { - background-color: #b7b9cc; -} - -.custom-range:disabled::-moz-range-thumb { - background-color: #b7b9cc; -} - -.custom-range:disabled::-ms-thumb { - background-color: #b7b9cc; -} - -.nav-link.disabled { - color: #858796; -} - -.nav-tabs { - border-bottom: 1px solid #dddfeb; -} - -.nav-tabs .nav-link { - border-top-left-radius: 0.35rem; - border-top-right-radius: 0.35rem; -} - -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #eaecf4 #eaecf4 #dddfeb; -} - -.nav-tabs .nav-link.disabled { - color: #858796; -} - -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #6e707e; - border-color: #dddfeb #dddfeb #fff; -} - -.nav-pills .nav-link { - border-radius: 0.35rem; -} - -.navbar-toggler { - border-radius: 0.35rem; -} - -.card { - border: 1px solid #e3e6f0; - border-radius: 0.35rem; -} - -.card > .list-group:first-child { - border-top-left-radius: calc(0.35rem - 1px); - border-top-right-radius: calc(0.35rem - 1px); -} - -.card > .list-group:last-child { - border-bottom-right-radius: calc(0.35rem - 1px); - border-bottom-left-radius: calc(0.35rem - 1px); -} - -.card-header { - background-color: #f8f9fc; - border-bottom: 1px solid #e3e6f0; -} - -.card-header:first-child { - border-radius: calc(0.35rem - 1px) calc(0.35rem - 1px) 0 0; -} - -.card-footer { - background-color: #f8f9fc; - border-top: 1px solid #e3e6f0; -} - -.card-footer:last-child { - border-radius: 0 0 calc(0.35rem - 1px) calc(0.35rem - 1px); -} - -.card-img-overlay { - border-radius: calc(0.35rem - 1px); -} - -.card-img, -.card-img-top { - border-top-left-radius: calc(0.35rem - 1px); - border-top-right-radius: calc(0.35rem - 1px); -} - -.card-img, -.card-img-bottom { - border-bottom-right-radius: calc(0.35rem - 1px); - border-bottom-left-radius: calc(0.35rem - 1px); -} - -.card-group > .card { - margin-bottom: 0.75rem; -} - -.breadcrumb-item + .breadcrumb-item::before { - display: inline-block; - padding-right: 0.5rem; - color: #858796; - content: "/"; -} - -.breadcrumb-item.active { - color: #858796; -} - -.pagination { - border-radius: 0.35rem; -} - -.page-link { - color: #4e73df; - border: 1px solid #dddfeb; -} - -.page-link:hover { - color: #224abe; - background-color: #eaecf4; - border-color: #dddfeb; -} - -.page-link:focus { - box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); -} - -.page-item:first-child .page-link { - border-top-left-radius: 0.35rem; - border-bottom-left-radius: 0.35rem; -} - -.page-item:last-child .page-link { - border-top-right-radius: 0.35rem; - border-bottom-right-radius: 0.35rem; -} - -.page-item.disabled .page-link { - color: #858796; - border-color: #dddfeb; -} - -.badge { - border-radius: 0.35rem; -} - -.alert { - border-radius: 0.35rem; -} - -.alert-primary { - color: #293c74; - background-color: #dce3f9; - border-color: #cdd8f6; -} - -.alert-primary hr { - border-top-color: #b7c7f2; -} - -.alert-primary .alert-link { - color: #1c294e; -} - -.alert-secondary { - color: #45464e; - background-color: #e7e7ea; - border-color: #dddde2; -} - -.alert-secondary hr { - border-top-color: #cfcfd6; -} - -.alert-secondary .alert-link { - color: #2d2e33; -} - -.alert-success { - color: #0f6848; - background-color: #d2f4e8; - border-color: #bff0de; -} - -.alert-success hr { - border-top-color: #aaebd3; -} - -.alert-success .alert-link { - color: #093b29; -} - -.alert-info { - color: #1c606a; - background-color: #d7f1f5; - border-color: #c7ebf1; -} - -.alert-info hr { - border-top-color: #b3e4ec; -} - -.alert-info .alert-link { - color: #113b42; -} - -.alert-warning { - color: #806520; - background-color: #fdf3d8; - border-color: #fceec9; -} - -.alert-warning hr { - border-top-color: #fbe6b1; -} - -.alert-warning .alert-link { - color: #574516; -} - -.alert-danger { - color: #78261f; - background-color: #fadbd8; - border-color: #f8ccc8; -} - -.alert-danger hr { - border-top-color: #f5b7b1; -} - -.alert-danger .alert-link { - color: #4f1915; -} - -.alert-light { - color: #818183; -} - -.alert-light .alert-link { - color: #686869; -} - -.alert-dark { - color: #2f3037; - background-color: #dedee1; - border-color: #d1d1d5; -} - -.alert-dark hr { - border-top-color: #c4c4c9; -} - -.alert-dark .alert-link { - color: #18181c; -} - -.progress { - background-color: #eaecf4; - border-radius: 0.35rem; -} - -.progress-bar { - background-color: #4e73df; -} - -.list-group { - border-radius: 0.35rem; -} - -.list-group-item-action { - color: #6e707e; -} - -.list-group-item-action:hover, .list-group-item-action:focus { - color: #6e707e; - background-color: #f8f9fc; -} - -.list-group-item-action:active { - color: #858796; - background-color: #eaecf4; -} - -.list-group-item.disabled, .list-group-item:disabled { - color: #858796; -} - -.list-group-item.active { - background-color: #4e73df; - border-color: #4e73df; -} - -.list-group-horizontal > .list-group-item:first-child { - border-bottom-left-radius: 0.35rem; -} - -.list-group-horizontal > .list-group-item:last-child { - border-top-right-radius: 0.35rem; -} - -@media (min-width: 576px) { - .list-group-horizontal-sm > .list-group-item:first-child { - border-bottom-left-radius: 0.35rem; - } - - .list-group-horizontal-sm > .list-group-item:last-child { - border-top-right-radius: 0.35rem; - } -} - -@media (min-width: 768px) { - .list-group-horizontal-md > .list-group-item:first-child { - border-bottom-left-radius: 0.35rem; - } - - .list-group-horizontal-md > .list-group-item:last-child { - border-top-right-radius: 0.35rem; - } -} - -@media (min-width: 992px) { - .list-group-horizontal-lg > .list-group-item:first-child { - border-bottom-left-radius: 0.35rem; - } - - .list-group-horizontal-lg > .list-group-item:last-child { - border-top-right-radius: 0.35rem; - } -} - -@media (min-width: 1200px) { - .list-group-horizontal-xl > .list-group-item:first-child { - border-bottom-left-radius: 0.35rem; - } - - .list-group-horizontal-xl > .list-group-item:last-child { - border-top-right-radius: 0.35rem; - } -} - -.list-group-item-primary { - color: #293c74; - background-color: #cdd8f6; -} - -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #293c74; - background-color: #b7c7f2; -} - -.list-group-item-primary.list-group-item-action.active { - background-color: #293c74; - border-color: #293c74; -} - -.list-group-item-secondary { - color: #45464e; - background-color: #dddde2; -} - -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #45464e; - background-color: #cfcfd6; -} - -.list-group-item-secondary.list-group-item-action.active { - background-color: #45464e; - border-color: #45464e; -} - -.list-group-item-success { - color: #0f6848; - background-color: #bff0de; -} - -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #0f6848; - background-color: #aaebd3; -} - -.list-group-item-success.list-group-item-action.active { - background-color: #0f6848; - border-color: #0f6848; -} - -.list-group-item-info { - color: #1c606a; - background-color: #c7ebf1; -} - -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #1c606a; - background-color: #b3e4ec; -} - -.list-group-item-info.list-group-item-action.active { - background-color: #1c606a; - border-color: #1c606a; -} - -.list-group-item-warning { - color: #806520; - background-color: #fceec9; -} - -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #806520; - background-color: #fbe6b1; -} - -.list-group-item-warning.list-group-item-action.active { - background-color: #806520; - border-color: #806520; -} - -.list-group-item-danger { - color: #78261f; - background-color: #f8ccc8; -} - -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #78261f; - background-color: #f5b7b1; -} - -.list-group-item-danger.list-group-item-action.active { - background-color: #78261f; - border-color: #78261f; -} - -.list-group-item-light { - color: #818183; - background-color: #fdfdfe; -} - -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #818183; - background-color: #ececf6; -} - -.list-group-item-light.list-group-item-action.active { - background-color: #818183; - border-color: #818183; -} - -.list-group-item-dark { - color: #2f3037; - background-color: #d1d1d5; -} - -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #2f3037; - background-color: #c4c4c9; -} - -.list-group-item-dark.list-group-item-action.active { - background-color: #2f3037; - border-color: #2f3037; -} - -.toast-header { - color: #858796; -} - -.modal-header { - border-bottom: 1px solid #e3e6f0; -} - -.tooltip-inner { - border-radius: 0.35rem; -} - -.popover-body { - color: #858796; -} - -.bg-primary { - background-color: #4e73df !important; -} - -.bg-secondary { - background-color: #858796 !important; -} - -.bg-success { - background-color: #1cc88a !important; -} - -.bg-info { - background-color: #36b9cc !important; -} - -.bg-warning { - background-color: #f6c23e !important; -} - -.bg-danger { - background-color: #e74a3b !important; -} - -.bg-light { - background-color: #f8f9fc !important; -} - -.bg-dark { - background-color: #5a5c69 !important; -} - -.border { - border: 1px solid #e3e6f0 !important; -} - -.border-top { - border-top: 1px solid #e3e6f0 !important; -} - -.border-end { - border-right: 1px solid #e3e6f0 !important; -} - -.border-bottom { - border-bottom: 1px solid #e3e6f0 !important; -} - -.border-start { - border-left: 1px solid #e3e6f0 !important; -} - -.border-primary { - border-color: #4e73df !important; -} - -.border-secondary { - border-color: #858796 !important; -} - -.border-success { - border-color: #1cc88a !important; -} - -.border-info { - border-color: #36b9cc !important; -} - -.border-warning { - border-color: #f6c23e !important; -} - -.border-danger { - border-color: #e74a3b !important; -} - -.border-light { - border-color: #f8f9fc !important; -} - -.border-dark { - border-color: #5a5c69 !important; -} - -.rounded { - border-radius: 0.35rem !important; -} - -.rounded-top { - border-top-left-radius: 0.35rem !important; - border-top-right-radius: 0.35rem !important; -} - -.rounded-end { - border-top-right-radius: 0.35rem !important; - border-bottom-right-radius: 0.35rem !important; -} - -.rounded-bottom { - border-bottom-right-radius: 0.35rem !important; - border-bottom-left-radius: 0.35rem !important; -} - -.rounded-start { - border-top-left-radius: 0.35rem !important; - border-bottom-left-radius: 0.35rem !important; -} - -.shadow-sm { - box-shadow: 0 0.125rem 0.25rem 0 rgba(58, 59, 69, 0.2) !important; -} - -.shadow { - box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important; -} - -.text-uppercase, .dropdown .dropdown-menu .dropdown-header, .sidebar .sidebar-heading { - text-transform: uppercase !important; -} - -.text-primary { - color: #4e73df !important; -} - -a.text-primary:hover, a.text-primary:focus { - color: #224abe !important; -} - -.text-secondary { - color: #858796 !important; -} - -a.text-secondary:hover, a.text-secondary:focus { - color: #60616f !important; -} - -.text-success { - color: #1cc88a !important; -} - -a.text-success:hover, a.text-success:focus { - color: #13855c !important; -} - -.text-info { - color: #36b9cc !important; -} - -a.text-info:hover, a.text-info:focus { - color: #258391 !important; -} - -.text-warning { - color: #f6c23e !important; -} - -a.text-warning:hover, a.text-warning:focus { - color: #dda20a !important; -} - -.text-danger { - color: #e74a3b !important; -} - -a.text-danger:hover, a.text-danger:focus { - color: #be2617 !important; -} - -.text-light { - color: #f8f9fc !important; -} - -a.text-light:hover, a.text-light:focus { - color: #c2cbe5 !important; -} - -.text-dark { - color: #5a5c69 !important; -} - -a.text-dark:hover, a.text-dark:focus { - color: #373840 !important; -} - -.text-body { - color: #858796 !important; -} - -.text-muted { - color: #858796 !important; -} - -@media print { - pre, - blockquote { - border: 1px solid #b7b9cc; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #dddfeb !important; - } -} - -#wrapper { - display: flex; - min-height: 100vh; -} - -#wrapper #content-wrapper { - background-color: #f8f9fc; - width: 100%; - overflow-x: hidden; -} - -#wrapper #content-wrapper #content { - flex: 1 0 auto; -} - -.scroll-to-top { - position: fixed; - right: 1rem; - bottom: 1rem; - display: none; - width: 2.75rem; - height: 2.75rem; - text-align: center; - color: #fff; - background: rgba(90, 92, 105, 0.5); - line-height: 46px; -} - -.scroll-to-top:focus, .scroll-to-top:hover { - color: white; -} - -.scroll-to-top:hover { - background: #5a5c69; -} - -.scroll-to-top i { - font-weight: 800; -} - -@-webkit-keyframes growIn { - 0% { - transform: scale(0.9); - opacity: 0; - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -@keyframes growIn { - 0% { - transform: scale(0.9); - opacity: 0; - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -.animated--grow-in, .sidebar .nav-item .collapse { - -webkit-animation-name: growIn; - animation-name: growIn; - -webkit-animation-duration: 200ms; - animation-duration: 200ms; - -webkit-animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); - animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); -} - -@-webkit-keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -@keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.animated--fade-in { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; - -webkit-animation-duration: 200ms; - animation-duration: 200ms; - -webkit-animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1); - animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1); -} - -.bg-gradient-primary { - background-color: #4e73df; - background-image: linear-gradient(180deg, #4e73df 10%, #224abe 100%); - background-size: cover; -} - -.bg-gradient-secondary { - background-color: #858796; - background-image: linear-gradient(180deg, #858796 10%, #60616f 100%); - background-size: cover; -} - -.bg-gradient-success { - background-color: #1cc88a; - background-image: linear-gradient(180deg, #1cc88a 10%, #13855c 100%); - background-size: cover; -} - -.bg-gradient-info { - background-color: #36b9cc; - background-image: linear-gradient(180deg, #36b9cc 10%, #258391 100%); - background-size: cover; -} - -.bg-gradient-warning { - background-color: #f6c23e; - background-image: linear-gradient(180deg, #f6c23e 10%, #dda20a 100%); - background-size: cover; -} - -.bg-gradient-danger { - background-color: #e74a3b; - background-image: linear-gradient(180deg, #e74a3b 10%, #be2617 100%); - background-size: cover; -} - -.bg-gradient-light { - background-color: #f8f9fc; - background-image: linear-gradient(180deg, #f8f9fc 10%, #c2cbe5 100%); - background-size: cover; -} - -.bg-gradient-dark { - background-color: #5a5c69; - background-image: linear-gradient(180deg, #5a5c69 10%, #373840 100%); - background-size: cover; -} - -.bg-gray-100 { - background-color: #f8f9fc !important; -} - -.bg-gray-200 { - background-color: #eaecf4 !important; -} - -.bg-gray-300 { - background-color: #dddfeb !important; -} - -.bg-gray-400 { - background-color: #d1d3e2 !important; -} - -.bg-gray-500 { - background-color: #b7b9cc !important; -} - -.bg-gray-600 { - background-color: #858796 !important; -} - -.bg-gray-700 { - background-color: #6e707e !important; -} - -.bg-gray-800 { - background-color: #5a5c69 !important; -} - -.bg-gray-900 { - background-color: #3a3b45 !important; -} - -.o-hidden { - overflow: hidden !important; -} - -.text-xs { - font-size: .7rem; -} - -.text-lg { - font-size: 1.2rem; -} - -.text-gray-100 { - color: #f8f9fc !important; -} - -.text-gray-200 { - color: #eaecf4 !important; -} - -.text-gray-300 { - color: #dddfeb !important; -} - -.text-gray-400 { - color: #d1d3e2 !important; -} - -.text-gray-500 { - color: #b7b9cc !important; -} - -.text-gray-600 { - color: #858796 !important; -} - -.text-gray-700 { - color: #6e707e !important; -} - -.text-gray-800 { - color: #5a5c69 !important; -} - -.text-gray-900 { - color: #3a3b45 !important; -} - -.icon-circle { - height: 2.5rem; - width: 2.5rem; - border-radius: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.border-left-primary { - border-left: 0.25rem solid #4e73df !important; -} - -.border-bottom-primary { - border-bottom: 0.25rem solid #4e73df !important; -} - -.border-left-secondary { - border-left: 0.25rem solid #858796 !important; -} - -.border-bottom-secondary { - border-bottom: 0.25rem solid #858796 !important; -} - -.border-left-success { - border-left: 0.25rem solid #1cc88a !important; -} - -.border-bottom-success { - border-bottom: 0.25rem solid #1cc88a !important; -} - -.border-left-info { - border-left: 0.25rem solid #36b9cc !important; -} - -.border-bottom-info { - border-bottom: 0.25rem solid #36b9cc !important; -} - -.border-left-warning { - border-left: 0.25rem solid #f6c23e !important; -} - -.border-bottom-warning { - border-bottom: 0.25rem solid #f6c23e !important; -} - -.border-left-danger { - border-left: 0.25rem solid #e74a3b !important; -} - -.border-bottom-danger { - border-bottom: 0.25rem solid #e74a3b !important; -} - -.border-left-light { - border-left: 0.25rem solid #f8f9fc !important; -} - -.border-bottom-light { - border-bottom: 0.25rem solid #f8f9fc !important; -} - -.border-left-dark { - border-left: 0.25rem solid #5a5c69 !important; -} - -.border-bottom-dark { - border-bottom: 0.25rem solid #5a5c69 !important; -} - -.progress-sm { - height: .5rem; -} - -.rotate-15 { - transform: rotate(15deg); -} - -.rotate-n-15 { - transform: rotate(-15deg); -} - -.dropdown .dropdown-menu { - font-size: 0.85rem; -} - -.dropdown .dropdown-menu .dropdown-header { - font-weight: 800; - font-size: 0.65rem; - color: #b7b9cc; -} - -.dropdown.no-arrow .dropdown-toggle::after { - display: none; -} - -.sidebar .nav-item.dropdown .dropdown-toggle::after, -.topbar .nav-item.dropdown .dropdown-toggle::after { - width: 1rem; - text-align: center; - float: right; - vertical-align: 0; - border: 0; - font-weight: 900; - content: '\f105'; - font-family: 'Font Awesome 5 Free'; -} - -.sidebar .nav-item.dropdown.show .dropdown-toggle::after, -.topbar .nav-item.dropdown.show .dropdown-toggle::after { - content: '\f107'; -} - -.sidebar .nav-item .nav-link, -.topbar .nav-item .nav-link { - position: relative; -} - -.sidebar .nav-item .nav-link .badge-counter, -.topbar .nav-item .nav-link .badge-counter { - position: absolute; - transform: scale(0.7); - transform-origin: top right; - right: .25rem; - margin-top: -.25rem; -} - -.sidebar .nav-item .nav-link .img-profile, -.topbar .nav-item .nav-link .img-profile { - height: 2rem; - width: 2rem; -} - -.topbar { - height: 4.375rem; -} - -.topbar #sidebarToggleTop { - height: 2.5rem; - width: 2.5rem; -} - -.topbar #sidebarToggleTop:hover { - background-color: #eaecf4; -} - -.topbar #sidebarToggleTop:active { - background-color: #dddfeb; -} - -.topbar .navbar-search { - width: 25rem; -} - -.topbar .navbar-search input { - font-size: 0.85rem; - height: auto; -} - -.topbar .topbar-divider { - width: 0; - border-right: 1px solid #e3e6f0; - height: calc(4.375rem - 2rem); - margin: auto 1rem; -} - -.topbar .nav-item .nav-link { - height: 4.375rem; - display: flex; - align-items: center; - padding: 0 0.75rem; -} - -.topbar .nav-item .nav-link:focus { - outline: none; -} - -.topbar .nav-item:focus { - outline: none; -} - -.topbar .dropdown { - position: static; -} - -.topbar .dropdown .dropdown-menu { - width: calc(100% - 1.5rem); - right: 0.75rem; -} - -.topbar .dropdown-list { - padding: 0; - border: none; - overflow: hidden; -} - -.topbar .dropdown-list .dropdown-header { - background-color: #4e73df; - border: 1px solid #4e73df; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #fff; -} - -.topbar .dropdown-list .dropdown-item { - white-space: normal; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - border-left: 1px solid #e3e6f0; - border-right: 1px solid #e3e6f0; - border-bottom: 1px solid #e3e6f0; - line-height: 1.3rem; -} - -.topbar .dropdown-list .dropdown-item .dropdown-list-image { - position: relative; - height: 2.5rem; - width: 2.5rem; -} - -.topbar .dropdown-list .dropdown-item .dropdown-list-image img { - height: 2.5rem; - width: 2.5rem; -} - -.topbar .dropdown-list .dropdown-item .dropdown-list-image .status-indicator { - background-color: #eaecf4; - height: 0.75rem; - width: 0.75rem; - border-radius: 100%; - position: absolute; - bottom: 0; - right: 0; - border: 0.125rem solid #fff; -} - -.topbar .dropdown-list .dropdown-item .text-truncate { - max-width: 10rem; -} - -.topbar .dropdown-list .dropdown-item:active { - background-color: #eaecf4; - color: #3a3b45; -} - -@media (min-width: 576px) { - .topbar .dropdown { - position: relative; - } - .topbar .dropdown .dropdown-menu { - width: auto; - right: 0; - } - .topbar .dropdown-list { - width: 20rem !important; - } - .topbar .dropdown-list .dropdown-item .text-truncate { - max-width: 13.375rem; - } -} - -.topbar.navbar-dark .navbar-nav .nav-item .nav-link { - color: rgba(255, 255, 255, 0.8); -} - -.topbar.navbar-dark .navbar-nav .nav-item .nav-link:hover { - color: #fff; -} - -.topbar.navbar-dark .navbar-nav .nav-item .nav-link:active { - color: #fff; -} - -.topbar.navbar-light .navbar-nav .nav-item .nav-link { - color: #d1d3e2; -} - -.topbar.navbar-light .navbar-nav .nav-item .nav-link:hover { - color: #b7b9cc; -} - -.topbar.navbar-light .navbar-nav .nav-item .nav-link:active { - color: #858796; -} - -.sidebar { - width: 6.5rem; - min-height: 100vh; -} - -.sidebar .nav-item { - position: relative; -} - -.sidebar .nav-item:last-child { - margin-bottom: 1rem; -} - -.sidebar .nav-item .nav-link { - text-align: center; - padding: 0.75rem 1rem; - width: 6.5rem; -} - -.sidebar .nav-item .nav-link span { - font-size: 0.65rem; - display: block; -} - -.sidebar .nav-item.active .nav-link { - font-weight: 700; -} - -.sidebar .nav-item .collapse { - position: absolute; - left: calc(6.5rem + 1.5rem / 2); - z-index: 1; - top: 2px; -} - -.sidebar .nav-item .collapse .collapse-inner { - border-radius: 0.35rem; - box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15); -} - -.sidebar .nav-item .collapsing { - display: none; - transition: none; -} - -.sidebar .nav-item .collapse .collapse-inner, -.sidebar .nav-item .collapsing .collapse-inner { - padding: .5rem 0; - min-width: 10rem; - font-size: 0.85rem; - margin: 0 0 1rem 0; -} - -.sidebar .nav-item .collapse .collapse-inner .collapse-header, -.sidebar .nav-item .collapsing .collapse-inner .collapse-header { - margin: 0; - white-space: nowrap; - padding: .5rem 1.5rem; - text-transform: uppercase; - font-weight: 800; - font-size: 0.65rem; - color: #b7b9cc; -} - -.sidebar .nav-item .collapse .collapse-inner .collapse-item, -.sidebar .nav-item .collapsing .collapse-inner .collapse-item { - padding: 0.5rem 1rem; - margin: 0 0.5rem; - display: block; - color: #3a3b45; - text-decoration: none; - border-radius: 0.35rem; - white-space: nowrap; -} - -.sidebar .nav-item .collapse .collapse-inner .collapse-item:hover, -.sidebar .nav-item .collapsing .collapse-inner .collapse-item:hover { - background-color: #eaecf4; -} - -.sidebar .nav-item .collapse .collapse-inner .collapse-item:active, -.sidebar .nav-item .collapsing .collapse-inner .collapse-item:active { - background-color: #dddfeb; -} - -.sidebar .nav-item .collapse .collapse-inner .collapse-item.active, -.sidebar .nav-item .collapsing .collapse-inner .collapse-item.active { - font-weight: 700; -} - -.sidebar #sidebarToggle { - width: 2.5rem; - height: 2.5rem; - text-align: center; - margin-bottom: 1rem; - cursor: pointer; -} - -.sidebar #sidebarToggle::after { - font-weight: 900; - content: '\f104'; - font-family: 'Font Awesome 5 Free'; - margin-right: 0.1rem; -} - -.sidebar #sidebarToggle:hover { - text-decoration: none; -} - -.sidebar #sidebarToggle:focus { - outline: none; -} - -.sidebar.toggled { - width: 0 !important; - overflow: hidden; -} - -.sidebar.toggled #sidebarToggle::after { - content: '\f105'; - font-family: 'Font Awesome 5 Free'; - margin-left: 0.25rem; -} - -.sidebar.toggled .sidebar-card { - display: none; -} - -.sidebar .sidebar-brand { - height: 4.375rem; - text-decoration: none; - font-size: 1rem; - font-weight: 800; - padding: 1.5rem 1rem; - text-align: center; - text-transform: uppercase; - letter-spacing: 0.05rem; - z-index: 1; -} - -.sidebar .sidebar-brand .sidebar-brand-icon i { - font-size: 2rem; -} - -.sidebar .sidebar-brand .sidebar-brand-text { - display: none; -} - -.sidebar .sidebar-divider { - margin: 0 1rem 1rem; - border-top: 1px solid rgba(255, 255, 255, 0.2); -} - -.sidebar .sidebar-heading { - text-align: center; - padding: 0 1rem; - font-weight: 800; - font-size: 0.65rem; -} - -.sidebar .sidebar-card { - display: flex; - flex-direction: column; - align-items: center; - font-size: 0.875rem; - border-radius: 0.35rem; - color: rgba(255, 255, 255, 0.8); - margin-left: 1rem; - margin-right: 1rem; - margin-bottom: 1rem; - padding: 1rem; - background-color: rgba(0, 0, 0, 0.1); -} - -.sidebar .sidebar-card .sidebar-card-illustration { - height: 3rem; - display: block; -} - -.sidebar .sidebar-card .sidebar-card-title { - font-weight: bold; -} - -.sidebar .sidebar-card p { - font-size: 0.75rem; - color: rgba(255, 255, 255, 0.5); -} - -@media (min-width: 768px) { - .sidebar { - width: 14rem !important; - } - .sidebar .nav-item .collapse { - position: relative; - left: 0; - z-index: 1; - top: 0; - -webkit-animation: none; - animation: none; - } - .sidebar .nav-item .collapse .collapse-inner { - border-radius: 0; - box-shadow: none; - } - .sidebar .nav-item .collapsing { - display: block; - transition: height 0.15s ease; - } - .sidebar .nav-item .collapse, - .sidebar .nav-item .collapsing { - margin: 0 1rem; - } - - .sidebar .nav-item .nav-link { - display: block; - width: 14rem; - text-align: left; - padding: 1rem; - } - .sidebar .nav-item .nav-link i { - font-size: 0.85rem; - margin-right: 0.25rem; - } - .sidebar .nav-item .nav-link span { - font-size: 0.85rem; - display: inline; - } - .sidebar .nav-item .nav-link[data-bs-toggle="collapse"]::after { - width: 1rem; - text-align: center; - float: right; - vertical-align: 0; - border: 0; - font-weight: 900; - content: '\f107'; - font-family: 'Font Awesome 5 Free'; - } - .sidebar .nav-item .nav-link[data-bs-toggle="collapse"].collapsed::after { - content: '\f105'; - } - .sidebar .sidebar-brand .sidebar-brand-icon i { - font-size: 2rem; - } - .sidebar .sidebar-brand .sidebar-brand-text { - display: inline; - } - .sidebar .sidebar-heading { - text-align: left; - } - .sidebar.toggled { - overflow: visible; - width: 6.5rem !important; - } - .sidebar.toggled .nav-item .collapse { - position: absolute; - left: calc(6.5rem + 1.5rem / 2); - z-index: 1; - top: 2px; - -webkit-animation-name: growIn; - animation-name: growIn; - -webkit-animation-duration: 200ms; - animation-duration: 200ms; - -webkit-animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); - animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); - } - .sidebar.toggled .nav-item .collapse .collapse-inner { - box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15); - border-radius: 0.35rem; - } - .sidebar.toggled .nav-item .collapsing { - display: none; - transition: none; - } - .sidebar.toggled .nav-item .collapse, - .sidebar.toggled .nav-item .collapsing { - margin: 0; - } - .sidebar.toggled .nav-item:last-child { - margin-bottom: 1rem; - } - .sidebar.toggled .nav-item .nav-link { - text-align: center; - padding: 0.75rem 1rem; - width: 6.5rem; - } - .sidebar.toggled .nav-item .nav-link span { - font-size: 0.65rem; - display: block; - } - .sidebar.toggled .nav-item .nav-link i { - margin-right: 0; - } - .sidebar.toggled .nav-item .nav-link[data-bs-toggle="collapse"]::after { - display: none; - } - .sidebar.toggled .sidebar-brand .sidebar-brand-icon i { - font-size: 2rem; - } - .sidebar.toggled .sidebar-brand .sidebar-brand-text { - display: none; - } - .sidebar.toggled .sidebar-heading { - text-align: center; - } -} - -.sidebar-light .sidebar-brand { - color: #6e707e; -} - -.sidebar-light hr.sidebar-divider { - border-top: 1px solid #eaecf4; -} - -.sidebar-light .sidebar-heading { - color: #b7b9cc; -} - -.sidebar-light .nav-item .nav-link { - color: #858796; -} - -.sidebar-light .nav-item .nav-link i { - color: #d1d3e2; -} - -.sidebar-light .nav-item .nav-link:active, .sidebar-light .nav-item .nav-link:focus, .sidebar-light .nav-item .nav-link:hover { - color: #6e707e; -} - -.sidebar-light .nav-item .nav-link:active i, .sidebar-light .nav-item .nav-link:focus i, .sidebar-light .nav-item .nav-link:hover i { - color: #6e707e; -} - -.sidebar-light .nav-item .nav-link[data-bs-toggle="collapse"]::after { - color: #b7b9cc; -} - -.sidebar-light .nav-item.active .nav-link { - color: #6e707e; -} - -.sidebar-light .nav-item.active .nav-link i { - color: #6e707e; -} - -.sidebar-light #sidebarToggle { - background-color: #eaecf4; -} - -.sidebar-light #sidebarToggle::after { - color: #b7b9cc; -} - -.sidebar-light #sidebarToggle:hover { - background-color: #dddfeb; -} - -.sidebar-dark .sidebar-brand { - color: #fff; -} - -.sidebar-dark hr.sidebar-divider { - border-top: 1px solid rgba(255, 255, 255, 0.15); -} - -.sidebar-dark .sidebar-heading { - color: rgba(255, 255, 255, 0.4); -} - -.sidebar-dark .nav-item .nav-link { - color: rgba(255, 255, 255, 0.8); -} - -.sidebar-dark .nav-item .nav-link i { - color: rgba(255, 255, 255, 0.3); -} - -.sidebar-dark .nav-item .nav-link:active, .sidebar-dark .nav-item .nav-link:focus, .sidebar-dark .nav-item .nav-link:hover { - color: #fff; -} - -.sidebar-dark .nav-item .nav-link:active i, .sidebar-dark .nav-item .nav-link:focus i, .sidebar-dark .nav-item .nav-link:hover i { - color: #fff; -} - -.sidebar-dark .nav-item .nav-link[data-bs-toggle="collapse"]::after { - color: rgba(255, 255, 255, 0.5); -} - -.sidebar-dark .nav-item.active .nav-link { - color: #fff; -} - -.sidebar-dark .nav-item.active .nav-link i { - color: #fff; -} - -.sidebar-dark #sidebarToggle { - background-color: rgba(255, 255, 255, 0.2); -} - -.sidebar-dark #sidebarToggle::after { - color: rgba(255, 255, 255, 0.5); -} - -.sidebar-dark #sidebarToggle:hover { - background-color: rgba(255, 255, 255, 0.25); -} - -.sidebar-dark.toggled #sidebarToggle::after { - color: rgba(255, 255, 255, 0.5); -} - -.btn-circle { - border-radius: 100%; - height: 2.5rem; - width: 2.5rem; - font-size: 1rem; - display: inline-flex; - align-items: center; - justify-content: center; -} - -.btn-circle.btn-sm, .btn-group-sm > .btn-circle.btn { - height: 1.8rem; - width: 1.8rem; - font-size: 0.75rem; -} - -.btn-circle.btn-lg, .btn-group-lg > .btn-circle.btn { - height: 3.5rem; - width: 3.5rem; - font-size: 1.35rem; -} - -.btn-icon-split { - padding: 0; - overflow: hidden; - display: inline-flex; - align-items: stretch; - justify-content: center; -} - -.btn-icon-split .icon { - background: rgba(0, 0, 0, 0.15); - display: inline-block; - padding: 0.375rem 0.75rem; -} - -.btn-icon-split .text { - display: inline-block; - padding: 0.375rem 0.75rem; -} - -.btn-icon-split.btn-sm .icon, .btn-group-sm > .btn-icon-split.btn .icon { - padding: 0.25rem 0.5rem; -} - -.btn-icon-split.btn-sm .text, .btn-group-sm > .btn-icon-split.btn .text { - padding: 0.25rem 0.5rem; -} - -.btn-icon-split.btn-lg .icon, .btn-group-lg > .btn-icon-split.btn .icon { - padding: 0.5rem 1rem; -} - -.btn-icon-split.btn-lg .text, .btn-group-lg > .btn-icon-split.btn .text { - padding: 0.5rem 1rem; -} - -.card .card-header .dropdown { - line-height: 1; -} - -.card .card-header .dropdown .dropdown-menu { - line-height: 1.5; -} - -.card .card-header[data-bs-toggle="collapse"] { - text-decoration: none; - position: relative; - padding: 0.75rem 3.25rem 0.75rem 1.25rem; -} - -.card .card-header[data-bs-toggle="collapse"]::after { - position: absolute; - right: 0; - top: 0; - padding-right: 1.725rem; - line-height: 51px; - font-weight: 900; - content: '\f107'; - font-family: 'Font Awesome 5 Free'; - color: #d1d3e2; -} - -.card .card-header[data-bs-toggle="collapse"].collapsed { - border-radius: 0.35rem; -} - -.card .card-header[data-bs-toggle="collapse"].collapsed::after { - content: '\f105'; -} - -form.user .custom-checkbox.small label { - line-height: 1.5rem; -} - -form.user .form-control-user { - font-size: 0.8rem; - border-radius: 10rem; - padding: 1.5rem 1rem; - height: calc(1.5em + 0.75rem + 2px); -} - -form.user .btn-user { - font-size: 0.8rem; - border-radius: 10rem; - padding: 0.75rem 1rem; -} - -.btn-google { - color: #fff; - background-color: #ea4335; - border-color: #fff; -} - -.btn-google:hover { - color: #fff; - background-color: #e12717; - border-color: #e6e6e6; -} - -.btn-google:focus, .btn-google.focus { - color: #fff; - background-color: #e12717; - border-color: #e6e6e6; - box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); -} - -.btn-google.disabled, .btn-google:disabled { - color: #fff; - background-color: #ea4335; - border-color: #fff; -} - -.btn-google:not(:disabled):not(.disabled):active, .btn-google:not(:disabled):not(.disabled).active, -.show > .btn-google.dropdown-toggle { - color: #fff; - background-color: #d62516; - border-color: #dfdfdf; -} - -.btn-google:not(:disabled):not(.disabled):active:focus, .btn-google:not(:disabled):not(.disabled).active:focus, -.show > .btn-google.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); -} - -.btn-facebook { - color: #fff; - background-color: #3b5998; - border-color: #fff; -} - -.btn-facebook:hover { - color: #fff; - background-color: #30497c; - border-color: #e6e6e6; -} - -.btn-facebook:focus, .btn-facebook.focus { - color: #fff; - background-color: #30497c; - border-color: #e6e6e6; - box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); -} - -.btn-facebook.disabled, .btn-facebook:disabled { - color: #fff; - background-color: #3b5998; - border-color: #fff; -} - -.btn-facebook:not(:disabled):not(.disabled):active, .btn-facebook:not(:disabled):not(.disabled).active, -.show > .btn-facebook.dropdown-toggle { - color: #fff; - background-color: #2d4373; - border-color: #dfdfdf; -} - -.btn-facebook:not(:disabled):not(.disabled):active:focus, .btn-facebook:not(:disabled):not(.disabled).active:focus, -.show > .btn-facebook.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); -} - -.error { - color: #5a5c69; - font-size: 7rem; - position: relative; - line-height: 1; - width: 12.5rem; -} - -@-webkit-keyframes noise-anim { - 0% { - clip: rect(10px, 9999px, 83px, 0); - } - 5% { - clip: rect(2px, 9999px, 65px, 0); - } - 10% { - clip: rect(8px, 9999px, 54px, 0); - } - 15% { - clip: rect(24px, 9999px, 22px, 0); - } - 20% { - clip: rect(19px, 9999px, 84px, 0); - } - 25% { - clip: rect(19px, 9999px, 10px, 0); - } - 30% { - clip: rect(27px, 9999px, 77px, 0); - } - 35% { - clip: rect(74px, 9999px, 34px, 0); - } - 40% { - clip: rect(34px, 9999px, 64px, 0); - } - 45% { - clip: rect(83px, 9999px, 47px, 0); - } - 50% { - clip: rect(23px, 9999px, 70px, 0); - } - 55% { - clip: rect(55px, 9999px, 49px, 0); - } - 60% { - clip: rect(90px, 9999px, 21px, 0); - } - 65% { - clip: rect(58px, 9999px, 63px, 0); - } - 70% { - clip: rect(77px, 9999px, 11px, 0); - } - 75% { - clip: rect(6px, 9999px, 72px, 0); - } - 80% { - clip: rect(27px, 9999px, 57px, 0); - } - 85% { - clip: rect(89px, 9999px, 54px, 0); - } - 90% { - clip: rect(55px, 9999px, 4px, 0); - } - 95% { - clip: rect(36px, 9999px, 91px, 0); - } - 100% { - clip: rect(19px, 9999px, 71px, 0); - } -} - -@keyframes noise-anim { - 0% { - clip: rect(10px, 9999px, 83px, 0); - } - 5% { - clip: rect(2px, 9999px, 65px, 0); - } - 10% { - clip: rect(8px, 9999px, 54px, 0); - } - 15% { - clip: rect(24px, 9999px, 22px, 0); - } - 20% { - clip: rect(19px, 9999px, 84px, 0); - } - 25% { - clip: rect(19px, 9999px, 10px, 0); - } - 30% { - clip: rect(27px, 9999px, 77px, 0); - } - 35% { - clip: rect(74px, 9999px, 34px, 0); - } - 40% { - clip: rect(34px, 9999px, 64px, 0); - } - 45% { - clip: rect(83px, 9999px, 47px, 0); - } - 50% { - clip: rect(23px, 9999px, 70px, 0); - } - 55% { - clip: rect(55px, 9999px, 49px, 0); - } - 60% { - clip: rect(90px, 9999px, 21px, 0); - } - 65% { - clip: rect(58px, 9999px, 63px, 0); - } - 70% { - clip: rect(77px, 9999px, 11px, 0); - } - 75% { - clip: rect(6px, 9999px, 72px, 0); - } - 80% { - clip: rect(27px, 9999px, 57px, 0); - } - 85% { - clip: rect(89px, 9999px, 54px, 0); - } - 90% { - clip: rect(55px, 9999px, 4px, 0); - } - 95% { - clip: rect(36px, 9999px, 91px, 0); - } - 100% { - clip: rect(19px, 9999px, 71px, 0); - } -} - -.error:after { - content: attr(data-text); - position: absolute; - left: 2px; - text-shadow: -1px 0 #e74a3b; - top: 0; - color: #5a5c69; - background: #f8f9fc; - overflow: hidden; - clip: rect(0, 900px, 0, 0); - animation: noise-anim 2s infinite linear alternate-reverse; -} - -@-webkit-keyframes noise-anim-2 { - 0% { - clip: rect(37px, 9999px, 52px, 0); - } - 5% { - clip: rect(77px, 9999px, 64px, 0); - } - 10% { - clip: rect(22px, 9999px, 93px, 0); - } - 15% { - clip: rect(38px, 9999px, 90px, 0); - } - 20% { - clip: rect(17px, 9999px, 20px, 0); - } - 25% { - clip: rect(20px, 9999px, 39px, 0); - } - 30% { - clip: rect(55px, 9999px, 43px, 0); - } - 35% { - clip: rect(72px, 9999px, 8px, 0); - } - 40% { - clip: rect(17px, 9999px, 94px, 0); - } - 45% { - clip: rect(87px, 9999px, 64px, 0); - } - 50% { - clip: rect(54px, 9999px, 26px, 0); - } - 55% { - clip: rect(89px, 9999px, 22px, 0); - } - 60% { - clip: rect(3px, 9999px, 3px, 0); - } - 65% { - clip: rect(60px, 9999px, 57px, 0); - } - 70% { - clip: rect(68px, 9999px, 88px, 0); - } - 75% { - clip: rect(39px, 9999px, 4px, 0); - } - 80% { - clip: rect(40px, 9999px, 89px, 0); - } - 85% { - clip: rect(1px, 9999px, 91px, 0); - } - 90% { - clip: rect(31px, 9999px, 43px, 0); - } - 95% { - clip: rect(51px, 9999px, 93px, 0); - } - 100% { - clip: rect(21px, 9999px, 67px, 0); - } -} - -@keyframes noise-anim-2 { - 0% { - clip: rect(37px, 9999px, 52px, 0); - } - 5% { - clip: rect(77px, 9999px, 64px, 0); - } - 10% { - clip: rect(22px, 9999px, 93px, 0); - } - 15% { - clip: rect(38px, 9999px, 90px, 0); - } - 20% { - clip: rect(17px, 9999px, 20px, 0); - } - 25% { - clip: rect(20px, 9999px, 39px, 0); - } - 30% { - clip: rect(55px, 9999px, 43px, 0); - } - 35% { - clip: rect(72px, 9999px, 8px, 0); - } - 40% { - clip: rect(17px, 9999px, 94px, 0); - } - 45% { - clip: rect(87px, 9999px, 64px, 0); - } - 50% { - clip: rect(54px, 9999px, 26px, 0); - } - 55% { - clip: rect(89px, 9999px, 22px, 0); - } - 60% { - clip: rect(3px, 9999px, 3px, 0); - } - 65% { - clip: rect(60px, 9999px, 57px, 0); - } - 70% { - clip: rect(68px, 9999px, 88px, 0); - } - 75% { - clip: rect(39px, 9999px, 4px, 0); - } - 80% { - clip: rect(40px, 9999px, 89px, 0); - } - 85% { - clip: rect(1px, 9999px, 91px, 0); - } - 90% { - clip: rect(31px, 9999px, 43px, 0); - } - 95% { - clip: rect(51px, 9999px, 93px, 0); - } - 100% { - clip: rect(21px, 9999px, 67px, 0); - } -} - -.error:before { - content: attr(data-text); - position: absolute; - left: -2px; - text-shadow: 1px 0 #4e73df; - top: 0; - color: #5a5c69; - background: #f8f9fc; - overflow: hidden; - clip: rect(0, 900px, 0, 0); - animation: noise-anim-2 3s infinite linear alternate-reverse; -} - -footer.sticky-footer { - padding: 2rem 0; - flex-shrink: 0; -} - -footer.sticky-footer .copyright { - line-height: 1; - font-size: 0.8rem; -} - -body.sidebar-toggled footer.sticky-footer { - width: 100%; -} diff --git a/Plan/common/src/main/resources/assets/plan/web/css/style.css b/Plan/common/src/main/resources/assets/plan/web/css/style.css deleted file mode 100644 index e6605818d..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/style.css +++ /dev/null @@ -1,1287 +0,0 @@ -a { - text-decoration: none; -} - -hr { - background-color: transparent; - border-top: 1px solid rgba(0, 0, 0, 0.1); - opacity: 1; -} - -small, .small { - font-weight: 400; -} - - -@media (max-width: 1520px) { - html { - font-size: 95%; - } -} - -.fc-title, .fc-time { - color: #eee; -} - -.accordion-striped tbody tr:nth-of-type(4n+1):not(.bg-teal), -.accordion-striped tbody tr:nth-of-type(4n+2):not(.bg-teal) { - background-color: rgba(0, 0, 0, 0.05); -} - -.table-dark.accordion-striped tbody tr:nth-of-type(4n+1):not(.bg-teal), -.table-dark.accordion-striped tbody tr:nth-of-type(4n+2):not(.bg-teal) { - background-color: rgba(255, 255, 255, 0.05); -} - -.table { - --bs-table-striped-color: none; -} - -.table-dark { - --bs-table-striped-bg: rgba(255, 255, 255, 0.05); - --bs-table-bg: none; -} - -.table-dark.table-bordered { - border: 0; -} - -.table th, -.table td { - padding: 0.75rem; -} - -.card-body p + p { - margin-top: 1rem; -} - -.card-body p { - margin: 0; -} - -.w-15 { - width: 15% !important; - height: 50%; -} - -.w-22 { - width: 22% !important; -} - -p.collapsing { - display: none; -} - -.page-link:not(:disabled):not(.disabled) { - cursor: pointer; -} - -.nav-link.sidebar-toggler { - padding-top: 0 !important; -} - -.sidebar-toggler { - padding: 0.6rem; - display: inline; - cursor: pointer; -} - -.sidebar-close-modal { - position: absolute; - opacity: 0.7; - background-color: #000; - width: 100%; - height: 100%; - top: 0; - z-index: 1; - cursor: pointer; -} - -.hidden { - width: 0 !important; - display: none; -} - -.hidden .sidebar .sidebar-brand img { - display: none; -} - -@media (min-width: 1400px) { - .sidebar-toggler { - display: none !important; - } -} - -.page-loader { - position: fixed; - background: #ddd; - width: 100%; - z-index: 50; - height: 100%; - text-align: center; -} - -.loader-container { - position: relative; - top: 50%; - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} - -.loader { - display: inline-block; - width: 2rem; - height: 2rem; - border: 4px solid #368F17; - border-radius: 5px; - background-color: #368F17; - animation: loader 2s infinite ease; -} - -.card span.loader { - margin-top: 4rem; -} - -.loader-text { - margin-top: 0.5rem; -} - -@keyframes loader { - 0% { - transform: rotate(0deg); - } - - 25% { - transform: rotate(180deg); - } - - 50% { - transform: rotate(180deg); - } - - 75% { - transform: rotate(360deg); - } - - 100% { - transform: rotate(360deg); - } -} - -div.scrollbar { - overflow: hidden; - overflow-y: auto; - max-height: 60vh; -} - -.clickable { - cursor: pointer; -} - -.color-chooser { - margin-bottom: 0.3rem; -} - -/* Navbar ====================================== */ - -.col-red .navbar .nav > li > a:hover, -.col-red .navbar .nav > li > a:focus, -.col-red .navbar .nav .open > a, -.col-red .navbar .nav .open > a:hover, -.col-red .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-red .navbar .nav > li > a { - color: #fff; -} - -.col-red .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-red .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-pink .navbar .nav > li > a:hover, -.col-pink .navbar .nav > li > a:focus, -.col-pink .navbar .nav .open > a, -.col-pink .navbar .nav .open > a:hover, -.col-pink .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-pink .navbar .nav > li > a { - color: #fff; -} - -.col-pink .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-pink .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-purple .navbar .nav > li > a:hover, -.col-purple .navbar .nav > li > a:focus, -.col-purple .navbar .nav .open > a, -.col-purple .navbar .nav .open > a:hover, -.col-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-purple .navbar .nav > li > a { - color: #fff; -} - -.col-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-deep-purple .navbar .nav > li > a:hover, -.col-deep-purple .navbar .nav > li > a:focus, -.col-deep-purple .navbar .nav .open > a, -.col-deep-purple .navbar .nav .open > a:hover, -.col-deep-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-deep-purple .navbar .nav > li > a { - color: #fff; -} - -.col-deep-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-deep-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-indigo .navbar .nav > li > a:hover, -.col-indigo .navbar .nav > li > a:focus, -.col-indigo .navbar .nav .open > a, -.col-indigo .navbar .nav .open > a:hover, -.col-indigo .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-indigo .navbar .nav > li > a { - color: #fff; -} - -.col-indigo .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-indigo .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-blue .navbar .nav > li > a:hover, -.col-blue .navbar .nav > li > a:focus, -.col-blue .navbar .nav .open > a, -.col-blue .navbar .nav .open > a:hover, -.col-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-blue .navbar .nav > li > a { - color: #fff; -} - -.col-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-light-blue .navbar .nav > li > a:hover, -.col-light-blue .navbar .nav > li > a:focus, -.col-light-blue .navbar .nav .open > a, -.col-light-blue .navbar .nav .open > a:hover, -.col-light-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-light-blue .navbar .nav > li > a { - color: #fff; -} - -.col-light-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-light-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-cyan .navbar .nav > li > a:hover, -.col-cyan .navbar .nav > li > a:focus, -.col-cyan .navbar .nav .open > a, -.col-cyan .navbar .nav .open > a:hover, -.col-cyan .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-cyan .navbar .nav > li > a { - color: #fff; -} - -.col-cyan .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-cyan .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-teal .navbar .nav > li > a:hover, -.col-teal .navbar .nav > li > a:focus, -.col-teal .navbar .nav .open > a, -.col-teal .navbar .nav .open > a:hover, -.col-teal .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-teal .navbar .nav > li > a { - color: #fff; -} - -.col-teal .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-teal .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-green .navbar .nav > li > a:hover, -.col-green .navbar .nav > li > a:focus, -.col-green .navbar .nav .open > a, -.col-green .navbar .nav .open > a:hover, -.col-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-green .navbar .nav > li > a { - color: #fff; -} - -.col-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-light-green .navbar .nav > li > a:hover, -.col-light-green .navbar .nav > li > a:focus, -.col-light-green .navbar .nav .open > a, -.col-light-green .navbar .nav .open > a:hover, -.col-light-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-light-green .navbar .nav > li > a { - color: #fff; -} - -.col-light-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-light-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-lime .navbar .nav > li > a:hover, -.col-lime .navbar .nav > li > a:focus, -.col-lime .navbar .nav .open > a, -.col-lime .navbar .nav .open > a:hover, -.col-lime .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-lime .navbar .nav > li > a { - color: #fff; -} - -.col-lime .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-lime .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-yellow .navbar .nav > li > a:hover, -.col-yellow .navbar .nav > li > a:focus, -.col-yellow .navbar .nav .open > a, -.col-yellow .navbar .nav .open > a:hover, -.col-yellow .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-yellow .navbar .nav > li > a { - color: #fff; -} - -.col-yellow .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-yellow .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-amber .navbar .nav > li > a:hover, -.col-amber .navbar .nav > li > a:focus, -.col-amber .navbar .nav .open > a, -.col-amber .navbar .nav .open > a:hover, -.col-amber .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-amber .navbar .nav > li > a { - color: #fff; -} - -.col-amber .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-amber .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-orange .navbar .nav > li > a:hover, -.col-orange .navbar .nav > li > a:focus, -.col-orange .navbar .nav .open > a, -.col-orange .navbar .nav .open > a:hover, -.col-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-orange .navbar .nav > li > a { - color: #fff; -} - -.col-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-deep-orange .navbar .nav > li > a:hover, -.col-deep-orange .navbar .nav > li > a:focus, -.col-deep-orange .navbar .nav .open > a, -.col-deep-orange .navbar .nav .open > a:hover, -.col-deep-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-deep-orange .navbar .nav > li > a { - color: #fff; -} - -.col-deep-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-deep-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-brown .navbar .nav > li > a:hover, -.col-brown .navbar .nav > li > a:focus, -.col-brown .navbar .nav .open > a, -.col-brown .navbar .nav .open > a:hover, -.col-brown .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-brown .navbar .nav > li > a { - color: #fff; -} - -.col-brown .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-brown .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-grey .navbar .nav > li > a:hover, -.col-grey .navbar .nav > li > a:focus, -.col-grey .navbar .nav .open > a, -.col-grey .navbar .nav .open > a:hover, -.col-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-grey .navbar .nav > li > a { - color: #fff; -} - -.col-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-blue-grey .navbar .nav > li > a:hover, -.col-blue-grey .navbar .nav > li > a:focus, -.col-blue-grey .navbar .nav .open > a, -.col-blue-grey .navbar .nav .open > a:hover, -.col-blue-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-blue-grey .navbar .nav > li > a { - color: #fff; -} - -.col-blue-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-blue-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-black .navbar .nav > li > a:hover, -.col-black .navbar .nav > li > a:focus, -.col-black .navbar .nav .open > a, -.col-black .navbar .nav .open > a:hover, -.col-black .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-black .navbar .nav > li > a { - color: #fff; -} - -.col-black .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-black .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -.col-white .navbar .nav > li > a:hover, -.col-white .navbar .nav > li > a:focus, -.col-white .navbar .nav .open > a, -.col-white .navbar .nav .open > a:hover, -.col-white .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); -} - -.col-white .navbar .nav > li > a { - color: #fff; -} - -.col-white .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; -} - -.col-white .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); -} - -div#navSrvContainer { - overflow-y: auto; - overflow-x: hidden; - text-overflow: ellipsis; - max-height: 180px; -} - -div#navSrvContainer > a { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} - -div#navSrvContainer::-webkit-scrollbar { - width: 12px; -} - -div#navSrvContainer::-webkit-scrollbar-thumb { - border: 4px solid rgba(0, 0, 0, 0); - background-clip: padding-box; - -webkit-border-radius: 7px; - background-color: rgba(0, 0, 0, 0.25); - -webkit-box-shadow: inset -1px -1px 0px rgba(0, 0, 0, 0.05), inset 1px 1px 0px rgba(0, 0, 0, 0.05); -} - -.plan-bg-gradient { - background: rgba(0, 0, 0, 0) linear-gradient(180deg, rgba(0, 0, 0, 0) 20%, rgba(0, 0, 0, 0.2) 100%); -} - -.bg-red, body.theme-red .fc-toolbar-chunk .btn.btn-primary { - background-color: #F44336; - color: #fff; -} - -.bg-pink, body.theme-pink .fc-toolbar-chunk .btn.btn-primary { - background-color: #E91E63; - color: #fff; -} - -.bg-purple, body.theme-purple .fc-toolbar-chunk .btn.btn-primary { - background-color: #9C27B0; - color: #fff; -} - -.bg-deep-purple, body.theme-deep-purple .fc-toolbar-chunk .btn.btn-primary { - background-color: #673AB7; - color: #fff; -} - -.bg-indigo, body.theme-indigo .fc-toolbar-chunk .btn.btn-primary { - background-color: #3F51B5; - color: #fff; -} - -.bg-blue, body.theme-blue .fc-toolbar-chunk .btn.btn-primary { - background-color: #2196F3; - color: #fff; -} - -.bg-light-blue, body.theme-light-blue .fc-toolbar-chunk .btn.btn-primary { - background-color: #03A9F4; - color: #fff; -} - -.bg-cyan, body.theme-cyan .fc-toolbar-chunk .btn.btn-primary { - background-color: #00BCD4; - color: #fff; -} - -.bg-teal, body.theme-teal .fc-toolbar-chunk .btn.btn-primary { - background-color: #009688; - color: #fff; -} - -.bg-green, body.theme-green .fc-toolbar-chunk .btn.btn-primary { - background-color: #4CAF50; - color: #fff; -} - -.bg-light-green, body.theme-light-green .fc-toolbar-chunk .btn.btn-primary { - background-color: #8BC34A; - color: #fff; -} - -.bg-lime, body.theme-lime .fc-toolbar-chunk .btn.btn-primary { - background-color: #CDDC39; - color: #fff; -} - -.bg-yellow, body.theme-yellow .fc-toolbar-chunk .btn.btn-primary { - background-color: #ffe821; - color: #fff; -} - -.bg-amber, body.theme-amber .fc-toolbar-chunk .btn.btn-primary { - background-color: #FFC107; - color: #fff; -} - -.bg-orange, body.theme-orange .fc-toolbar-chunk .btn.btn-primary { - background-color: #FF9800; - color: #fff; -} - -.bg-deep-orange, body.theme-deep-orange .fc-toolbar-chunk .btn.btn-primary { - background-color: #FF5722; - color: #fff; -} - -.bg-brown, body.theme-brown .fc-toolbar-chunk .btn.btn-primary { - background-color: #795548; - color: #fff; -} - -.bg-grey, body.theme-grey .fc-toolbar-chunk .btn.btn-primary { - background-color: #9E9E9E; - color: #fff; -} - -.bg-blue-grey, body.theme-blue-grey .fc-toolbar-chunk .btn.btn-primary { - background-color: #607D8B; - color: #fff; -} - -.bg-black, body.theme-black .fc-toolbar-chunk .btn.btn-primary { - background-color: #555555; - color: #fff; -} - -.bg-white, body.theme-white .fc-toolbar-chunk .btn.btn-primary { - background-color: #ffffff; - color: #333; -} - -.bg-plan, body.theme-plan .fc-toolbar-chunk .btn.btn-primary { - background-color: #368F17; - color: #fff; -} - -.btn.bg-plan:hover, -.btn.bg-pink:hover, -.btn.bg-red:hover, -.btn.bg-purple:hover, -.btn.bg-deep-purple:hover, -.btn.bg-indigo:hover, -.btn.bg-light-blue:hover, -.btn.bg-black:hover, -.btn.bg-blue:hover, -.btn.bg-cyan:hover, -.btn.bg-teal:hover, -.btn.bg-green:hover, -.btn.bg-light-green:hover, -.btn.bg-lime:hover, -.btn.bg-yellow:hover, -.btn.bg-amber:hover, -.btn.bg-orange:hover, -.btn.bg-deep-orange:hover, -.btn.bg-brown:hover, -.btn.bg-grey:hover, -.btn.bg-blue-grey:hover { - color: #ccc; -} - -.bg-night, body.theme-night .fc-toolbar-chunk .btn.btn-primary { - background-color: #44475a; - color: #eee8d5; -} - -.fc-toolbar-chunk .btn.btn-primary { - outline: none !important; - border: none !important; -} - -.bg-red-outline { - border-color: #F44336; - border-style: solid; - outline: #F44336 solid 1px; -} - -.bg-pink-outline { - border-color: #E91E63; - border-style: solid; - outline: #E91E63 solid 1px; -} - -.bg-purple-outline { - border-color: #9C27B0; - border-style: solid; - outline: #9C27B0 solid 1px; -} - -.bg-deep-purple-outline { - border-color: #673AB7; - border-style: solid; - outline: #673AB7 solid 1px; -} - -.bg-indigo-outline { - border-color: #3F51B5; - border-style: solid; - outline: #3F51B5 solid 1px; -} - -.bg-blue-outline { - border-color: #2196F3; - border-style: solid; - outline: #2196F3 solid 1px; -} - -.bg-light-blue-outline { - border-color: #03A9F4; - border-style: solid; - outline: #03A9F4 solid 1px; -} - -.bg-cyan-outline { - border-color: #00BCD4; - border-style: solid; - outline: #00BCD4 solid 1px; -} - -.bg-teal-outline { - border-color: #009688; - border-style: solid; - border-width: 3px; - outline: #009688 solid 1px; -} - -.bg-green-outline { - border-color: #4CAF50; - border-style: solid; - outline: #4CAF50 solid 1px; -} - -.bg-light-green-outline { - border-color: #8BC34A; - border-style: solid; - outline: #8BC34A solid 1px; -} - -.bg-lime-outline { - border-color: #CDDC39; - border-style: solid; - outline: #CDDC39 solid 1px; -} - -.bg-yellow-outline { - border-color: #ffe821; - border-style: solid; - outline: #ffe821 solid 1px; -} - -.bg-amber-outline { - border-color: #FFC107; - border-style: solid; - outline: #FFC107 solid 1px; -} - -.bg-orange-outline { - border-color: #FF9800; - border-style: solid; - outline: #FF9800 solid 1px; -} - -.bg-deep-orange-outline { - border-color: #FF5722; - border-style: solid; - outline: #FF5722 solid 1px; -} - -.bg-brown-outline { - border-color: #795548; - border-style: solid; - outline: #795548 solid 1px; -} - -.bg-grey-outline { - border-color: #9E9E9E; - border-style: solid; - outline: #9E9E9E solid 1px; -} - -.bg-blue-grey-outline { - border-color: #607D8B; - border-style: solid; - outline: #607D8B solid 1px; -} - -.bg-black-outline { - border-color: #555555; - border-style: solid; - outline: #555555 solid 1px; -} - -.bg-plan-outline { - border-color: #368F17; - border-style: solid; - outline: #368F17 solid 1px; -} - -.col-red { - color: #F44336; -} - -.col-pink { - color: #E91E63; -} - -.col-purple { - color: #9C27B0; -} - -.col-deep-purple { - color: #673AB7; -} - -.col-indigo { - color: #3F51B5; -} - -.col-blue { - color: #2196F3; -} - -.col-light-blue { - color: #03A9F4; -} - -.col-cyan { - color: #00BCD4; -} - -.col-teal { - color: #009688; -} - -.col-green { - color: #4CAF50; -} - -.col-light-green { - color: #8BC34A; -} - -.col-lime { - color: #CDDC39; -} - -.col-yellow { - color: #ffe821; -} - -.col-amber { - color: #FFC107; -} - -.col-orange { - color: #FF9800; -} - -.col-deep-orange { - color: #FF5722; -} - -.col-brown { - color: #795548; -} - -.col-grey { - color: #9E9E9E; -} - -.col-blue-grey { - color: #607D8B; -} - -.col-black { - color: #555555; -} - -.col-white { - color: #ffffff; -} - -.col-plan { - color: #368F17; -} - -/* Minecraft color codes */ -.black { - color: #000000; -} - -.darkblue { - color: #0000AA; -} - -.darkgreen { - color: #00AA00; -} - -.darkaqua { - color: #00AAAA; -} - -.darkred { - color: #AA0000; -} - -.darkpurple { - color: #AA00AA; -} - -.gold { - color: #FFAA00; -} - -.gray { - color: #AAAAAA; -} - -.darkgray { - color: #555555; -} - -.blue { - color: #5555FF; -} - -.green { - color: #55FF55; -} - -.aqua { - color: #55FFFF; -} - -.red { - color: #FF5555; -} - -.pink { - color: #FF55FF; -} - -.yellow { - color: #FFFF55; - text-shadow: 0 0 6px #000; -} - -.white { - color: #FFFFFF; - text-shadow: 0 0 8px #000; -} - -/* -Adding a translation, even though it doesn't visually -impact the page, causes child elements with a -position of "fixed" to be based on this element -instead of any other parent elements. For example, -if a child element to this element had `top: 0` -and `left: 0`, then it would align with the top -left of this element instead of another parent or the document body. -See this StackOverflow answer: https://stackoverflow.com/a/20830413 - -We use this to properly align the popover when there are more -than 3 events in one day on the sessions calendar. - */ -#sessions-overview { - transform: translateX(0); -} - -/* -When the sidebar is visible, it offsets the popovers by its width. - The rules below fix that by applying a negative margin. - */ -body .fc-popover { - margin-left: -14rem; -} - -body.sidebar-hidden .fc-popover { - margin-left: 0 !important; -} - -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; -} - -#filter-dropdown { - max-height: 18rem; - overflow-y: scroll; -} - -.refresh-element { - font-size: 1rem; - margin-left: 1rem; -} - -.refresh-element > i { - cursor: pointer; -} - -.sorting, .sorting_desc, .sorting_asc { - cursor: pointer; -} - -.dataTables_length { - padding-top: 0.5rem; - padding-left: 0.5rem; -} - -.dataTables_filter { - padding-top: 0.5rem; - padding-right: 0.5rem; -} - -.dataTables_info { - padding-bottom: 0.5rem; - padding-left: 0.5rem; -} - -.dataTables_paginate { - padding-bottom: 0.5rem; - padding-right: 0.5rem; -} - -.sidebar .nav-item .collapse { - z-index: 2; -} - -.container, .container-fluid, .container-sm, .container-md, .container-lg, .container-xl { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.topbar-divider { - width: 0; - border-right: 1px solid #e3e6f0; - height: calc(4.375rem - 2rem); - margin: auto 1rem; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/error.html b/Plan/common/src/main/resources/assets/plan/web/error.html index cfd78f6ee..4f9a7b58c 100644 --- a/Plan/common/src/main/resources/assets/plan/web/error.html +++ b/Plan/common/src/main/resources/assets/plan/web/error.html @@ -51,16 +51,10 @@ - Logout -
    - ${versionButton} -
    @@ -153,77 +147,6 @@ - - - - - - - diff --git a/Plan/common/src/main/resources/assets/plan/web/favicon.ico b/Plan/common/src/main/resources/assets/plan/web/favicon.ico deleted file mode 100644 index cea02dd44..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/favicon.ico and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png b/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png deleted file mode 100644 index 89c9f5ea3..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js b/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js deleted file mode 100644 index 837f62890..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js +++ /dev/null @@ -1,498 +0,0 @@ -(function ($) { - const bgElements = ['.sidebar', '.btn', 'body']; - const textElements = ['a', 'button']; - - const colors = ['plan', - 'red', 'pink', 'purple', 'deep-purple', - 'indigo', 'blue', 'light-blue', 'cyan', - 'teal', 'green', 'light-green', 'lime', - 'yellow', 'amber', 'orange', 'deep-orange', - 'brown', 'grey', 'blue-grey']; - - const selectedColor = window.localStorage.getItem('themeColor'); - const themeDefaultColor = '${defaultTheme}'; - let currentColor = 'plan'; - - // Function for changing color - function setColor(nextColor) { - if (selectedColor === null) { - window.localStorage.setItem('themeColor', currentColor); - } - const bodyElement = document.querySelector('body'); - bodyElement.classList.remove(`theme-${currentColor}`); - bodyElement.classList.add(`theme-${nextColor}`); - - if (!nextColor || nextColor == currentColor) { - return; - } - - bgElements.map(element => element + '.bg-' + currentColor + ":not(.color-chooser)") - .forEach(selector => { - document.querySelectorAll(selector).forEach(element => { - element.classList.remove(`bg-${currentColor}`); - element.classList.add(`bg-${nextColor}`); - }); - }); - textElements.map(element => element + '.col-' + currentColor) - .forEach(selector => { - document.querySelectorAll(selector).forEach(element => { - element.classList.remove(`col-${currentColor}`); - element.classList.add(`col-${nextColor}`); - }); - }); - if (nextColor != 'night') { - window.localStorage.setItem('themeColor', nextColor); - } - currentColor = nextColor; - } - - // Set the color changing function for all color change buttons - function enableColorSetters() { - for (const color of colors) { - const selector = document.getElementById(`choose-${color}`); - selector.removeAttribute('disabled'); - selector.classList.remove('disabled'); - selector.classList.add(`bg-${color}`); - selector.addEventListener('click', () => setColor(color)); - } - } - - enableColorSetters(); - - function disableColorSetters() { - for (const color of colors) { - const selector = document.getElementById(`choose-${color}`); - selector.classList.add('disabled'); - selector.setAttribute('disabled', 'true'); - } - } - - // Change the color of the theme - setColor(selectedColor ? selectedColor : themeDefaultColor); - - let nightMode = window.localStorage.getItem('nightMode') == 'true' || '${defaultTheme}' == 'night'; - - const saturationReduction = 0.70; - - // From https://stackoverflow.com/a/3732187 - function withReducedSaturation(colorHex) { - // To RGB - let r = parseInt(colorHex.substr(1, 2), 16); // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10). - let g = parseInt(colorHex.substr(3, 2), 16); - let b = parseInt(colorHex.substr(5, 2), 16); - - // To HSL - r /= 255; - g /= 255; - b /= 255; - const max = Math.max(r, g, b), min = Math.min(r, g, b); - let h, s; - const l = (max + min) / 2; - - if (max === min) { - h = s = 0; // achromatic - } else { - const d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch (max) { - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break; - case g: - h = (b - r) / d + 2; - break; - case b: - h = (r - g) / d + 4; - break; - } - h /= 6; - } - - // To css property - return 'hsl(' + h * 360 + ',' + s * 100 * saturationReduction + '%,' + l * 95 + '%)'; - } - - const red = withReducedSaturation('#F44336'); - const pink = withReducedSaturation('#E91E63'); - const purple = withReducedSaturation('#9C27B0'); - const deepPurple = withReducedSaturation('#673AB7'); - const indigo = withReducedSaturation('#3F51B5'); - const blue = withReducedSaturation('#2196F3'); - const lightBlue = withReducedSaturation('#03A9F4'); - const cyan = withReducedSaturation('#00BCD4'); - const teal = withReducedSaturation('#009688'); - const green = withReducedSaturation('#4CAF50'); - const lightGreen = withReducedSaturation('#8BC34A'); - const lime = withReducedSaturation('#CDDC39'); - const yellow = withReducedSaturation('#ffe821'); - const amber = withReducedSaturation('#FFC107'); - const warningColor = withReducedSaturation('#f6c23e'); - const orange = withReducedSaturation('#FF9800'); - const deepOrange = withReducedSaturation('#FF5722'); - const dangerColor = withReducedSaturation('#e74a3b'); - const brown = withReducedSaturation('#795548'); - const grey = withReducedSaturation('#9E9E9E'); - const blueGrey = withReducedSaturation('#607D8B'); - const black = withReducedSaturation('#555555'); - const planColor = withReducedSaturation('#368F17'); - const successColor = withReducedSaturation('#1cc88a'); - const nightModeColors = `.bg-red {background-color: ${red};color: #eee8d5;}` + - `.bg-pink {background-color: ${pink};color: #eee8d5;}` + - `.bg-purple {background-color: ${purple};color: #eee8d5;}` + - `.bg-deep-purple {background-color: ${deepPurple};color: #eee8d5;}` + - `.bg-indigo {background-color: ${indigo};color: #eee8d5;}` + - `.bg-blue {background-color: ${blue};color: #eee8d5;}` + - `.bg-light-blue {background-color: ${lightBlue};color: #eee8d5;}` + - `.bg-cyan {background-color: ${cyan};color: #eee8d5;}` + - `.bg-teal {background-color: ${teal};color: #eee8d5;}` + - `.bg-green {background-color: ${green};color: #eee8d5;}` + - `.bg-light-green {background-color: ${lightGreen};color: #eee8d5;}` + - `.bg-lime {background-color: ${lime};color: #eee8d5;}` + - `.bg-yellow {background-color: ${yellow};color: #eee8d5;}` + - `.bg-amber {background-color: ${amber};color: #eee8d5;}` + - `.bg-warning {background-color: ${warningColor};color: #eee8d5;}` + - `.bg-orange {background-color: ${orange};color: #eee8d5;}` + - `.bg-deep-orange {background-color: ${deepOrange};color: #eee8d5;}` + - `.bg-danger {background-color: ${dangerColor};color: #eee8d5;}` + - `.bg-brown {background-color: ${brown};color: #eee8d5;}` + - `.bg-grey {background-color: ${grey};color: #eee8d5;}` + - `.bg-blue-grey {background-color: ${blueGrey};color: #eee8d5;}` + - `.bg-black {background-color: ${black};color: #eee8d5;}` + - `.bg-plan,.page-item.active .page-link {background-color: ${planColor};color: #eee8d5;}` + - `.bg-success {background-color: ${successColor};color: #eee8d5;}` + - `.bg-night {background-color: #44475a;color: #eee8d5;}` + - `.bg-red-outline {outline-color: ${red};border-color: ${red};}` + - `.bg-pink-outline {outline-color: ${pink};border-color: ${pink};}` + - `.bg-purple-outline {outline-color: ${purple};border-color: ${purple};}` + - `.bg-deep-purple-outline {outline-color: ${deepPurple};border-color: ${deepPurple};}` + - `.bg-indigo-outline {outline-color: ${indigo};border-color: ${indigo};}` + - `.bg-blue-outline {outline-color: ${blue};border-color: ${blue};}` + - `.bg-light-blue-outline {outline-color: ${lightBlue};border-color: ${lightBlue};}` + - `.bg-cyan-outline {outline-color: ${cyan};border-color: ${cyan};}` + - `.bg-teal-outline {outline-color: ${teal};border-color: ${teal};}` + - `.bg-green-outline {outline-color: ${green};border-color: ${green};}` + - `.bg-light-green-outline {outline-color: ${lightGreen};border-color: ${lightGreen};}` + - `.bg-lime-outline {outline-color: ${lime};border-color: ${lime};}` + - `.bg-yellow-outline {outline-color: ${yellow};border-color: ${yellow};}` + - `.bg-amber-outline {outline-color: ${amber};border-color: ${amber};}` + - `.bg-orange-outline {outline-color: ${orange};border-color: ${orange};}` + - `.bg-deep-orange-outline {outline-color: ${deepOrange};border-color: ${deepOrange};}` + - `.bg-brown-outline {outline-color: ${brown};border-color: ${brown};}` + - `.bg-grey-outline {outline-color: ${grey};border-color: ${grey};}` + - `.bg-blue-grey-outline {outline-color: ${blueGrey};border-color: ${blueGrey};}` + - `.bg-black-outline {outline-color: ${black};border-color: ${black};}` + - `.bg-plan-outline {outline-color: ${planColor};border-color: ${planColor};}` + - `.col-red {color: ${red};}` + - `.col-pink {color: ${pink};}` + - `.col-purple {color: ${purple};}` + - `.col-deep-purple {color: ${deepPurple};}` + - `.col-indigo {color: ${indigo};}` + - `.col-blue {color: ${blue};}` + - `.col-light-blue {color: ${lightBlue};}` + - `.col-cyan {color: ${cyan};}` + - `.col-teal {color: ${teal};}` + - `.col-green {color: ${green};}` + - `.col-light-green {color: ${lightGreen};}` + - `.col-lime {color: ${lime};}` + - `.col-yellow {color: ${yellow};}` + - `.col-amber {color: ${amber};}` + - `.text-warning {color: ${warningColor};}` + - `.col-orange {color: ${orange};}` + - `.col-deep-orange {color: ${deepOrange};}` + - `.text-danger {color: ${dangerColor};}` + - `.col-brown {color: ${brown};}` + - `.col-grey {color: ${grey};}` + - `.col-blue-grey {color: ${blueGrey};}` + - `.col-plan {color: ${planColor};}` + - `.text-success {color: ${successColor};}`; - - function changeNightModeCSS() { - if (nightMode) { - // Background colors from dracula theme - $('head').append(''); - // Turn bright tables to dark - $('.table').addClass('table-dark'); - // Turn modal close buttons light - document.querySelectorAll('button.btn-close').forEach(element => { element.classList.add('btn-close-white'); }) - // Sidebar is grey when in night mode - disableColorSetters(); - setColor('night'); - } else { - // Remove night mode style sheet - $('#nightmode').remove(); - // Turn dark tables bright again - $('.table').removeClass('table-dark'); - // Turn modal close buttons dark - document.querySelectorAll('button.btn-close').forEach(element => { element.classList.remove('btn-close-white'); }) - // Sidebar is colorful - enableColorSetters(); - setColor(window.localStorage.getItem('themeColor')); - } - } - - function changeHighChartsNightMode() { - try { - Highcharts.theme = nightMode ? { - chart: { - backgroundColor: null, - plotBorderColor: '#606063' - }, - title: { - style: {color: '#eee8d5'} - }, - subtitle: { - style: {color: '#eee8d5'} - }, - xAxis: { - gridLineColor: '#707073', - labels: { - style: {color: '#eee8d5'} - }, - lineColor: '#707073', - minorGridLineColor: '#505053', - tickColor: '#707073', - title: { - style: {color: '#eee8d5'} - } - }, - yAxis: { - gridLineColor: '#707073', - labels: { - style: {color: '#eee8d5'} - }, - lineColor: '#707073', - minorGridLineColor: '#505053', - tickColor: '#707073', - tickWidth: 1, - title: { - style: {color: '#eee8d5'} - } - }, - tooltip: { - backgroundColor: '#44475a', - style: {color: '#eee8d5'} - }, - plotOptions: { - series: { - dataLabels: {color: '#B0B0B3'}, - marker: {lineColor: '#333'} - } - }, - legend: { - itemStyle: {color: '#eee8d5'}, - itemHoverStyle: {color: '#eee8d5'}, - itemHiddenStyle: {color: '#606063'} - }, - labels: { - style: {color: '#eee8d5'} - }, - drilldown: { - activeAxisLabelStyle: {color: '#eee8d5'}, - activeDataLabelStyle: {color: '#eee8d5'} - }, - navigation: { - buttonOptions: { - symbolStroke: '#eee8d5', - theme: {fill: '#44475a'} - } - }, - // scroll charts - rangeSelector: { - buttonTheme: { - fill: '#505053', - stroke: '#646e8c', - style: {color: '#CCC'}, - states: { - hover: { - fill: '#646e9d', - stroke: '#646e8c', - style: {color: 'white'} - }, - select: { - fill: '#646e9d', - stroke: '#646e8c', - style: {color: 'white'} - } - } - }, - inputBoxBorderColor: '#505053', - inputStyle: { - backgroundColor: '#333', - color: 'silver' - }, - labelStyle: {color: 'silver'} - }, - - navigator: { - handles: { - backgroundColor: '#666', - borderColor: '#AAA' - }, - outlineColor: '#CCC', - maskFill: 'rgba(255,255,255,0.1)', - series: {lineColor: '#A6C7ED'}, - xAxis: {gridLineColor: '#505053'} - }, - - scrollbar: { - barBackgroundColor: '#808083', - barBorderColor: '#808083', - buttonArrowColor: '#CCC', - buttonBackgroundColor: '#606063', - buttonBorderColor: '#606063', - rifleColor: '#FFF', - trackBackgroundColor: '#404043', - trackBorderColor: '#404043' - } - } : { // Defaults - chart: { - backgroundColor: null, - plotBorderColor: '#cccccc' - }, - title: { - style: {color: '#3E576F'} - }, - subtitle: { - style: {color: '#3E576F'} - }, - xAxis: { - gridLineColor: '#E6E6E6', - labels: { - style: {color: '#333333'} - }, - lineColor: '#E6E6E6', - minorGridLineColor: '#505053', - tickColor: '#E6E6E6', - title: { - style: {color: '#333333'} - } - }, - yAxis: { - gridLineColor: '#E6E6E6', - labels: { - style: {color: '#333333'} - }, - lineColor: '#E6E6E6', - minorGridLineColor: '#505053', - tickColor: '#E6E6E6', - tickWidth: 1, - title: { - style: {color: '#333333'} - } - }, - tooltip: { - backgroundColor: 'rgba(247,247,247,0.85)', - style: {color: '#333333'} - }, - plotOptions: { - series: { - dataLabels: {color: undefined}, - marker: {lineColor: undefined} - } - }, - legend: { - itemStyle: {color: '#333333'}, - itemHoverStyle: {color: '#000000'}, - itemHiddenStyle: {color: '#cccccc'} - }, - labels: { - style: {color: '#333333'} - }, - drilldown: { - activeAxisLabelStyle: {color: '#333333'}, - activeDataLabelStyle: {color: '#333333'} - }, - navigation: { - buttonOptions: { - symbolStroke: '#333333', - theme: {fill: '#CCCCCC'} - } - }, - // scroll charts - rangeSelector: { - buttonTheme: { - fill: '#F7F7F7', - stroke: '#333', - style: {color: '#4B336A'}, - states: { - hover: { - fill: '#E6EBF5', - stroke: '#333', - style: {color: 'black'} - }, - select: { - fill: '#E6EBF5', - stroke: '#333', - style: {color: 'black'} - } - } - }, - inputBoxBorderColor: '#CCCCCC', - inputStyle: { - backgroundColor: '#333', - color: '#666666' - }, - labelStyle: {color: "#666666"} - }, - - navigator: { - handles: { - backgroundColor: '#f2f2f2', - borderColor: '#999999' - }, - outlineColor: '#cccccc', - maskFill: 'rgba(102,133,194,0.3)', - series: {lineColor: "#3FA0FF"}, - xAxis: {gridLineColor: '#e6e6e6'} - }, - - scrollbar: { - barBackgroundColor: '#cccccc', - barBorderColor: '#cccccc', - buttonArrowColor: '#333333', - buttonBackgroundColor: '#e6e6e6', - buttonBorderColor: '#cccccc', - rifleColor: '#333333', - trackBackgroundColor: '#f2f2f2', - trackBorderColor: '#f2f2f2' - } - }; - Highcharts.setOptions(Highcharts.theme); - updateGraphs(); - } catch (e) { - if ("Highcharts is not defined" === e.message || "updateGraphs is not defined" === e.message) { - // Highcharts isn't loaded, can be ignored - } else { - console.error(e); - } - } - } - - changeNightModeCSS(); - changeHighChartsNightMode(); - - function toggleNightMode() { - nightMode = !nightMode; - setTimeout(function () { - window.localStorage.setItem('nightMode', nightMode); - changeNightModeCSS(); - changeHighChartsNightMode(); - }, 0); - } - - $('#night-mode-toggle').on('click', toggleNightMode); - -})(jQuery); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/domUtils.js b/Plan/common/src/main/resources/assets/plan/web/js/domUtils.js deleted file mode 100644 index 258dab53d..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/domUtils.js +++ /dev/null @@ -1,38 +0,0 @@ -function insertElementBefore(elementSelector, createElementFunction) { - const placeBefore = document.querySelector(elementSelector); - insertElementBeforeElement(placeBefore, createElementFunction); -} - -function insertElementBeforeElement(placeBefore, createElementFunction) { - const element = createElementFunction(); - placeBefore.insertAdjacentElement('beforebegin', element); -} - -function insertElementAfter(elementSelector, createElementFunction) { - const placeBefore = document.querySelector(elementSelector); - insertElementAfterElement(placeBefore, createElementFunction) -} - -function insertElementAfterElement(placeBefore, createElementFunction) { - const element = createElementFunction(); - placeBefore.insertAdjacentElement('afterend', element); -} - -/** - * Shows an error using the page loader. - * @param {string} [message] The message to be shown. - */ -function loaderError(message) { - let loader = document.querySelector(".page-loader"); - let loaderText = document.querySelector('.loader-text'); - - if (loader.style.display === "none") { - loader.style.display = "block"; - } - - if (message !== undefined) { - loaderText.innerText = message; - } else { - loaderText.innerText = "Error occurred, see the Developer Console (Ctrl+Shift+I) for details." - } -} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/filters.js b/Plan/common/src/main/resources/assets/plan/web/js/filters.js deleted file mode 100644 index cc6fb40cd..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/filters.js +++ /dev/null @@ -1,274 +0,0 @@ -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 = - `
    - -
    -
    - -
    -
    - -
    -
    -
    `; - 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 JoinAddressFilter extends MultipleChoiceFilter { - constructor( - id, options - ) { - super(id, "joinAddresses", `joined with address`, options); - } -} - -// Lowercase due to locale translation: Geolocations -class geolocationsFilter extends MultipleChoiceFilter { - constructor( - id, options - ) { - super(id, "geolocations", `have joined from country`, options); - } -} - -class PluginBooleanGroupsFilter extends MultipleChoiceFilter { - constructor( - id, options - ) { - super(id, "pluginsBooleanGroups", `have Plugin boolean value`, options); - } -} - -class PluginGroupsFilter extends MultipleChoiceFilter { - constructor( - id, kind, options - ) { - super(id, kind, `are in ${options.plugin}'s ${options.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 ( - `
    - -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    ` - ); - } - - 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); - } -} - -class PlayedOnServerFilter extends MultipleChoiceFilter { - constructor(id, options) { - super(id, "playedOnServer", "have played on at least one of", options); - } -} - -function createFilter(filter, id) { - if (filter.kind.startsWith("pluginGroups-")) { - return new PluginGroupsFilter(id, filter.kind, filter.options); - } - 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 "joinAddresses": - return new JoinAddressFilter(id, filter.options); - case "geolocations": - return new geolocationsFilter(id, filter.options); - case "playedBetween": - return new PlayedBetweenFilter(id, filter.options); - case "registeredBetween": - return new RegisteredBetweenFilter(id, filter.options); - case "pluginsBooleanGroups": - return new PluginBooleanGroupsFilter(id, filter.options); - case "playedOnServer": - return new PlayedOnServerFilter(id, filter.options); - default: - throw new Error("Unsupported filter kind: '" + filter.kind + "'"); - } -} - -function getReadableFilterName(filter) { - if (filter.kind.startsWith("pluginGroups-")) { - return "Group: " + filter.kind.substring(13); - } - switch (filter.kind) { - case "allPlayers": - return "All players" - case "activityIndexNow": - return "Current activity group"; - case "banned": - return "Ban status"; - case "operators": - return "Operator status"; - case "joinAddresses": - return "Join Addresses"; - case "geolocations": - return "Geolocations"; - case "playedBetween": - return "Played between"; - case "registeredBetween": - return "Registered between"; - case "pluginsBooleanGroups": - return "Has plugin boolean value"; - case "playedOnServer": - return "Has played on one of servers"; - default: - return filter.kind; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/graphs.js b/Plan/common/src/main/resources/assets/plan/web/js/graphs.js deleted file mode 100644 index 0a6674150..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/graphs.js +++ /dev/null @@ -1,831 +0,0 @@ -const linegraphButtons = [{ - type: 'hour', - count: 12, - text: '12h' -}, { - type: 'hour', - count: 24, - text: '24h' -}, { - type: 'day', - count: 7, - text: '7d' -}, { - type: 'month', - count: 1, - text: '30d' -}, { - type: 'all', - text: 'All' -}]; - -const graphs = []; -window.calendars = {}; - -function activityPie(id, activitySeries) { - graphs.push(Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie' - }, - title: {text: ''}, - tooltip: { - pointFormat: '{series.name}: {point.y}' - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - series: [activitySeries] - })); -} - -function diskChart(id, series) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - yAxis: { - labels: { - formatter: function () { - return this.value + ' MB'; - } - }, - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - legend: { - enabled: true - }, - series: series - })); -} - -function horizontalBarChart(id, categories, series, text) { - graphs.push(Highcharts.chart(id, { - chart: { - type: 'bar' - }, - title: { - text: '' - }, - xAxis: { - categories: categories, - title: { - text: null - } - }, - yAxis: { - min: 0, - title: { - text: text, - align: 'high' - }, - labels: { - overflow: 'justify' - } - }, - legend: { - enabled: false - }, - plotOptions: { - bar: { - dataLabels: { - enabled: true - } - } - }, - credits: { - enabled: true - }, - series: series - })); -} - -function lineChart(id, series) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - legend: { - enabled: true - }, - series: series - })); -} - -function dayByDay(id, series) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - legend: { - enabled: true - }, - time: {timezoneOffset: 0}, - series: series - })); -} - -function onlineActivityCalendar(id, event_data, firstDay) { - window.calendars.online_activity = new FullCalendar.Calendar(document.querySelector(id), { - timeZone: "UTC", - themeSystem: 'bootstrap', - eventColor: '#2196F3', - firstDay: firstDay, - initialView: 'dayGridMonth', - - eventDidMount: function (info) { - $(info.el).popover({ - content: info.event.title, - trigger: 'hover', - placement: 'top', - container: 'body' - }); - }, - - events: function (fetchInfo, successCallback, failureCallback) { - successCallback(event_data) - }, - - height: 800, - contentHeight: 795, - headerToolbar: { - left: 'title', - center: '', - right: 'today prev,next' - } - }); - - window.calendars.online_activity.render(); -} - -function mapToDataSeries(performanceData) { - const playersOnline = []; - const tps = []; - const cpu = []; - const ram = []; - const entities = []; - const chunks = []; - const disk = []; - - return new Promise((resolve => { - let i = 0; - const length = performanceData.length; - - function processNextThousand() { - const to = Math.min(i + 1000, length); - for (i; i < to; i++) { - const entry = performanceData[i]; - const date = entry[0]; - playersOnline[i] = [date, entry[1]]; - tps[i] = [date, entry[2]]; - cpu[i] = [date, entry[3]]; - ram[i] = [date, entry[4]]; - entities[i] = [date, entry[5]]; - chunks[i] = [date, entry[6]]; - disk[i] = [date, entry[7]]; - } - if (i >= length) { - resolve({playersOnline, tps, cpu, ram, entities, chunks, disk}) - } else { - setTimeout(processNextThousand, 10); - } - } - - processNextThousand(); - })) -} - -function performanceChart(id, playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries) { - const chart = Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - title: {text: ''}, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' P'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' TPS'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + '%'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' MB'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' E'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' C'; - } - } - }], - legend: { - enabled: true - }, - series: [playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries] - }); - - function toggleLabels() { - if (!chart || !chart.yAxis || !chart.yAxis.length) return; - const newWidth = $(window).width(); - chart.yAxis[0].update({labels: {enabled: newWidth >= 900}}); - chart.yAxis[1].update({labels: {enabled: newWidth >= 900}}); - chart.yAxis[2].update({labels: {enabled: newWidth >= 1000}}); - chart.yAxis[3].update({labels: {enabled: newWidth >= 1000}}); - chart.yAxis[4].update({labels: {enabled: newWidth >= 1400}}); - chart.yAxis[5].update({labels: {enabled: newWidth >= 1400}}); - } - - $(window).resize(toggleLabels); - toggleLabels(); - - graphs.push(chart); -} - -function playersChart(id, playersOnlineSeries, sel) { - function groupByIntervalStartingFrom(startDate, interval) { - let previousGroupStart = startDate; - const groupByInterval = [[]]; - - for (let point of playersOnlineSeries.data) { - const date = point[0]; - if (date < startDate) { - continue; - } - - if (previousGroupStart + interval < date) { - previousGroupStart = date; - groupByInterval.push([]); - } - - const currentGroup = groupByInterval[groupByInterval.length - 1]; - currentGroup.push(point); - } - return groupByInterval; - } - - function averageGroupPoints(groupByInterval, minDate) { - const averages = []; - for (let group of groupByInterval) { - let totalDate = 0; - let total = 0; - let count = group.length; - for (let point of group) { - totalDate += (point[0] - minDate); // Remove the minDate from dates to calculate a smaller total - total += point[1]; - } - - if (count !== 0) { - const middleDate = Math.trunc((totalDate / count) + minDate); - const average = Math.trunc(total / count); - averages.push([middleDate, average]); - } - } - return averages; - } - - function getAveragePlayersSeries(minDate, twentyPointInterval) { - const groupByInterval = groupByIntervalStartingFrom(minDate, twentyPointInterval); - - return { - name: s.name.averagePlayersOnline, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: averageGroupPoints(groupByInterval, minDate), - color: "#02458d", - yAxis: 0 - }; - } - - function updateAveragePlayers(event) { - const minDate = event.min; - const maxDate = event.max; - const twentyPointInterval = (maxDate - minDate) / 20; - - const averagePlayersSeries = getAveragePlayersSeries(minDate, twentyPointInterval); - - const playersOnlineGraph = graphs.find(graph => graph && graph.renderTo && graph.renderTo.id === id); - playersOnlineGraph.series[1].update(averagePlayersSeries); - } - - const emptyAveragePlayersSeries = { - name: s.name.averagePlayersOnline, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: [], - color: "#02458d", - yAxis: 0 - }; - - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: sel, - buttons: linegraphButtons - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - /* Average online players graph Disabled - xAxis: { - events: { - afterSetExtremes: updateAveragePlayers - } - }, - */ - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - series: [playersOnlineSeries, /*emptyAveragePlayersSeries*/] - })); -} - -function playersChartNoNav(id, playersOnlineSeries) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 3, - buttons: linegraphButtons - }, - navigator: { - enabled: false - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - series: [playersOnlineSeries] - })); -} - -function punchCard(id, punchcardSeries) { - graphs.push(Highcharts.chart(id, { - chart: { - defaultSeriesType: 'scatter' - }, - title: {text: ''}, - xAxis: { - type: 'datetime', - dateTimeLabelFormats: { - // https://www.php.net/manual/en/function.strftime.php - hour: '%I %P', - day: '%I %P' - }, - tickInterval: 3600000 - }, - time: { - timezoneOffset: 0 - }, - yAxis: { - title: { - text: "Day of the Week" - }, - reversed: true, - categories: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - }, - tooltip: { - pointFormat: 'Activity: {point.z}' - }, - series: [punchcardSeries] - })); -} - -function resourceChart(id, cpuSeries, ramSeries, playersOnlineSeries) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: linegraphButtons - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + '%'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' MB'; - } - } - }], - legend: { - enabled: true - }, - series: [cpuSeries, ramSeries, playersOnlineSeries] - })); -} - -function serverPie(id, serverSeries) { - graphs.push(Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie' - }, - title: {text: ''}, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - tooltip: { - formatter: function () { - return '' + this.point.name + ': ' + formatTimeAmount(this.y) + ' (' + this.percentage.toFixed(2) + '%)'; - } - }, - series: [serverSeries] - })); -} - -function joinAddressPie(id, joinAddresses) { - if (joinAddresses.data.length < 2) { - document.getElementById(id).innerHTML = '

    ' - document.getElementById(id).classList.remove('chart-area'); - - // XSS danger appending join addresses directly, using innerText is safe. - for (let slice of joinAddresses.data) { - document.querySelector(`#${id} p`).innerText = `${slice.name}: ${slice.y}`; - } - } else { - document.getElementById(id).innerHTML = ''; - document.getElementById(id).classList.add('chart-area'); - graphs.push(Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie' - }, - title: {text: ''}, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - tooltip: { - formatter: function () { - return '' + this.point.name + ': ' + this.y + ' (' + this.percentage.toFixed(2) + '%)'; - } - }, - series: [joinAddresses] - })); - } -} - -function formatTimeAmount(ms) { - let out = ""; - - let seconds = Math.floor(ms / 1000); - - const dd = Math.floor(seconds / 86400); - seconds -= (dd * 86400); - const dh = Math.floor(seconds / 3600); - seconds -= (dh * 3600); - const dm = Math.floor(seconds / 60); - seconds -= (dm * 60); - seconds = Math.floor(seconds); - if (dd !== 0) { - out += dd.toString() + "d "; - } - if (dh !== 0) { - out += dh.toString() + "h "; - } - if (dm !== 0) { - out += dm.toString() + "m "; - } - out += seconds.toString() + "s "; - - return out; -} - -function sessionCalendar(id, event_data, firstDay) { - document.querySelector(id + " .loader").remove(); - window.calendars.sessions = new FullCalendar.Calendar(document.querySelector(id), { - timeZone: "UTC", - themeSystem: 'bootstrap', - eventColor: '#009688', - dayMaxEventRows: 4, - firstDay: firstDay, - initialView: 'dayGridMonth', - - eventDidMount: function (info) { - $(info.el).popover({ - content: info.event.title, - trigger: 'hover', - placement: 'top', - container: 'body' - }); - }, - - events: function (fetchInfo, successCallback, failureCallback) { - successCallback(event_data) - }, - - navLinks: true, - height: 450, - contentHeight: 445, - headerToolbar: { - left: 'title', - center: '', - right: 'dayGridMonth dayGridWeek dayGridDay today prev,next' - } - }); - - setTimeout(function () { - window.calendars.sessions.render(); - }, 0); -} - -function stackChart(id, categories, series, label) { - graphs.push(Highcharts.chart(id, { - chart: { - type: 'area' - }, - title: { - text: '' - }, - xAxis: { - categories: categories, - tickmarkPlacement: 'on', - title: { - enabled: false - }, - ordinal: false - }, - yAxis: { - title: { - text: label - }, - labels: { - formatter: function () { - return this.value; - } - }, - softMax: 2, - softMin: 0 - }, - tooltip: { - split: true, - valueSuffix: ' ' + label - }, - plotOptions: { - area: { - stacking: 'normal', - lineWidth: 1 - } - }, - series: series - })); -} - -function tpsChart(id, tpsSeries, playersOnlineSeries) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: linegraphButtons - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' TPS'; - } - } - }], - legend: { - enabled: true - }, - series: [tpsSeries, playersOnlineSeries] - })); -} - -function worldChart(id, entitySeries, chunkSeries, playersOnlineSeries) { - graphs.push(Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: linegraphButtons - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' Entities'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' Chunks'; - } - } - }], - legend: { - enabled: true - }, - series: [entitySeries, chunkSeries, playersOnlineSeries] - })); -} - -function worldMap(id, colorMin, colorMax, mapSeries) { - graphs.push(Highcharts.mapChart(id, { - chart: { - animation: true - }, - title: {text: ''}, - - mapNavigation: { - enabled: true, - enableDoubleClickZoomTo: true - }, - - colorAxis: { - min: 1, - type: 'logarithmic', - minColor: colorMin, - maxColor: colorMax - }, - series: [mapSeries] - })); -} - -function worldPie(id, worldSeries, gmSeries) { - const defaultTitle = ''; - const defaultSubtitle = 'Click to expand'; - const chart = Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie', - events: { - drilldown: function (e) { - chart.setTitle({text: '' + e.point.name}, {text: ''}); - }, - drillup: function (e) { - chart.setTitle({text: defaultTitle}, {text: defaultSubtitle}); - } - } - }, - title: {text: defaultTitle}, - subtitle: { - text: defaultSubtitle - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - tooltip: { - formatter: function () { - return '' + this.point.name + ': ' + formatTimeAmount(this.y) + ' (' + this.percentage.toFixed(2) + '%)'; - } - }, - series: [worldSeries], - drilldown: { - series: gmSeries.map(function (d) { - return {name: d.name, id: d.id, colors: gmPieColors, data: d.data} - }) - } - }); - graphs.push(chart); -} - -function updateGraphs() { - // HighCharts nukes the scrollbar variable from the given parameter - // If the graph doesn't support srollbars (bar, pie and map charts for example) - // This workaround stores a copy of the scrollbar so that it can be set - const scrollbar = {...Highcharts.theme.scrollbar}; - - function updateGraph(graph, index, array) { - // Empty objects can be left in the array if existing graph is re-rendered - if (Object.keys(graph).length === 0) { - array.splice(index, 1); - return; - } - - // scrollbar workaround - if (!Highcharts.theme["scrollbar"]) Highcharts.theme["scrollbar"] = {...scrollbar}; - - graph.update(Highcharts.theme); - } - - graphs.forEach(updateGraph); -} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/localeSystem.js b/Plan/common/src/main/resources/assets/plan/web/js/localeSystem.js deleted file mode 100644 index ecc7dddf9..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/localeSystem.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * A locale system for localizing the website. - */ -const localeSystem = { - /** - * @function - * Localizes an element. - * @param {Element} element Element to localize - * @param {Object} [options] Options - */ - localize: {}, - - /** - * The current default language reported by the server. - * @type {string} - * @readonly - */ - defaultLanguage: "", - - /** - * The current available languages reported by the server. - * @type {Object.} - * @readonly - */ - availableLanguages: {}, - clientLocale: "", - - /** - * Initializes the locale system. Gets the default & available languages from `/v1/locale`, and initializes i18next. - */ - init: function () { - jsonRequest("./v1/locale", (json, error) => { - if (error) { - throw "Error occurred when downloading language information: " + error; - } - - this.defaultLanguage = json.defaultLanguage; - this.availableLanguages = json.languages; - - this.clientLocale = window.localStorage.getItem("locale"); - if (!this.clientLocale) { - this.clientLocale = this.defaultLanguage; - } - - i18next.use(i18nextChainedBackend) - .init({ - lng: this.clientLocale, - fallbackLng: false, - supportedLngs: Object.keys(this.availableLanguages), - backend: { - backends: [ - i18nextLocalStorageBackend, - i18nextHttpBackend - ], - backendOptions: [{ - expirationTime: 7 * 24 * 60 * 60 * 1000 // 7 days - }, { - loadPath: './v1/locale/{{lng}}' - }] - }, - }, () => { - this.loadSelector(); - this.initLoci18next(); - }); - }) - }, - - initLoci18next: function () { - this.localize = locI18next.init(i18next, { - selectorAttr: 'data-i18n', // selector for translating elements - targetAttr: 'i18n-target', - optionsAttr: 'i18n-options', - useOptionsAttr: false, - parseDefaultValueFromContent: false // show key as fallback if locale fails to load - }); - this.localize("title"); - this.localize("body"); - }, - - /** - * Loads a locale and translates the page. - * - * @param {string} langCode The two-character code for the language to be loaded, e.g. EN - * @throws Error if an invalid langCode is given - * @see /v1/language endpoint for available language codes - */ - loadLocale: function (langCode) { - if (i18next.language === langCode) { - return; - } - if (!(langCode in this.availableLanguages)) { - throw `The locale ${langCode} isn't available!`; - } - - window.localStorage.setItem("locale", langCode); - i18next.changeLanguage(langCode, () => { - this.localize("title"); - this.localize("body"); - }) - }, - - loadSelector: function () { - const selector = document.getElementById("langSelector"); - - let languages = Object.fromEntries(Object.entries(this.availableLanguages).sort()); - if ('CUSTOM' in languages) { - // Move "Custom" to first in list - delete languages["CUSTOM"] - languages = Object.assign({"CUSTOM": "Custom"}, languages); - } - - for (let lang in languages) { - let option = document.createElement("option"); - option.value = lang; - option.innerText = languages[lang]; - if (this.clientLocale === lang) { - option.setAttribute("selected", "") - } - selector.append(option); - } - - selector.addEventListener('change', event => { - this.loadLocale(event.target.value); - }) - } -} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/logonsine.js b/Plan/common/src/main/resources/assets/plan/web/js/logonsine.js deleted file mode 100644 index 445dae676..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/logonsine.js +++ /dev/null @@ -1,75 +0,0 @@ -// https://gist.github.com/gkhays/e264009c0832c73d5345847e673a64ab -function drawSine(canvasId) { - let step; - - function drawPoint(ctx, x, y) { - const radius = 2; - ctx.beginPath(); - - // Hold x constant at 4 so the point only moves up and down. - ctx.arc(x - 5, y, radius, 0, 2 * Math.PI, false); - - ctx.fillStyle = '#fff'; - ctx.fill(); - ctx.lineWidth = 1; - ctx.stroke(); - } - - function plotSine(ctx, xOffset) { - const width = ctx.canvas.width; - const height = ctx.canvas.height; - - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.strokeStyle = "#fff"; - - // Drawing point - - let x = -2; - let y = 0; - const amplitude = 50; - const frequency = 50; - - ctx.moveTo(x, 50); - while (x <= width) { - y = height / 2 + amplitude * Math.sin((x + xOffset) / frequency) * Math.cos((x + xOffset) / (frequency * 0.54515978463)); - ctx.lineTo(x, y); - x += 5; - } - ctx.stroke(); - ctx.save(); - drawPoint(ctx, x, y); - - ctx.stroke(); - ctx.restore(); - } - - function draw() { - const canvas = document.getElementById(canvasId); - if (canvas == null) return; - const context = canvas.getContext("2d"); - - context.clearRect(0, 0, 1000, 150); - context.save(); - - plotSine(context, step); - context.restore(); - - step += 0.5; - window.requestAnimationFrame(draw); - } - - function fix_dpi() { - const canvas = document.getElementById(canvasId); - if (canvas == null) return; - let dpi = window.devicePixelRatio; - canvas.getContext('2d'); - const style_width = getComputedStyle(canvas).getPropertyValue("width").slice(0, -2); - // Scale the canvas - canvas.setAttribute('width', `${style_width * dpi}`); - } - - fix_dpi(); - step = -1; - window.requestAnimationFrame(draw); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/network-values.js b/Plan/common/src/main/resources/assets/plan/web/js/network-values.js deleted file mode 100644 index fec870817..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/network-values.js +++ /dev/null @@ -1,677 +0,0 @@ -const trend_up_good = " "; -const trend_up_bad = " "; -const trend_down_bad = " "; -const trend_down_good = " "; -const trend_same = " "; - -const trend_end = ""; - -function trend(trend) { - if (!trend) { - return trend_same + '?' + trend_end; - } - switch (trend.direction) { - case '+': - return (trend.reversed ? trend_up_bad : trend_up_good) + trend.text + trend_end; - case '-': - return (trend.reversed ? trend_down_good : trend_down_bad) + trend.text + trend_end; - default: - return trend_same + trend.text + trend_end; - } -} - -function smallTrend(trend) { - if (!trend) { - return ' '; - } - switch (trend.direction) { - case '+': - trend_color = trend.reversed ? 'text-danger' : 'text-success'; - return ` `; - case '-': - trend_color = trend.reversed ? 'text-success' : 'text-danger'; - return ` `; - default: - return ` `; - } -} - -function displayError(element, error) { - insertElementAfterElement(element.querySelector('.d-sm-flex'), () => { - const alert = document.createElement('div'); - alert.classList.add('alert', 'alert-danger'); - alert.setAttribute('role', 'alert'); - alert.innerText = `Failed to load values: ${error}`; - return alert; - }) -} - -/* This function loads Network Overview tab */ -function loadNetworkOverviewValues(json, error) { - const tab = document.getElementById('network-overview'); - if (error) { - displayError(tab, error); - return; - } - - // Last 7 days - let data = json.players; - let element = tab.querySelector('#data_players'); - - element.querySelector('#data_unique_players_1d').innerText = data.unique_players_1d; - element.querySelector('#data_unique_players_7d').innerText = data.unique_players_7d; - element.querySelector('#data_unique_players_30d').innerText = data.unique_players_30d; - element.querySelector('#data_new_players_1d').innerText = data.new_players_1d; - element.querySelector('#data_new_players_7d').innerText = data.new_players_7d; - element.querySelector('#data_new_players_30d').innerText = data.new_players_30d; - - // Server As Numbers - data = json.numbers; - element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_current_uptime').innerText = data.current_uptime; - element.querySelector('#data_total').innerText = data.total_players; - element.querySelector('#data_regular').innerText = data.regular_players; - element.querySelector('#data_online').innerText = data.online_players; - - element.querySelector('#data_last_peak_date').innerText = data.last_peak_date; - element.querySelector('#data_last_peak_players').innerText = data.last_peak_players; - element.querySelector('#data_best_peak_date').innerText = data.best_peak_date; - element.querySelector('#data_best_peak_players').innerText = data.best_peak_players; - - element.querySelector('#data_playtime').innerText = data.playtime; - element.querySelector('#data_player_playtime').innerText = data.player_playtime; - element.querySelector('#data_session_length_avg').innerText = data.session_length_avg; - element.querySelector('#data_sessions').innerText = data.sessions; - - // Week Comparison - data = json.weeks; - element = tab.querySelector('#data_weeks'); - - element.querySelector('#data_start').innerText = data.start; - element.querySelector('#data_midpoint').innerText = data.midpoint; - element.querySelector('#data_midpoint2').innerText = data.midpoint; - element.querySelector('#data_end').innerText = data.end; - - element.querySelector('#data_unique_before').innerText = data.unique_before; - element.querySelector('#data_unique_after').innerText = data.unique_after; - element.querySelector('#data_unique_trend').innerHTML = trend(data.unique_trend); - element.querySelector('#data_new_before').innerText = data.new_before; - element.querySelector('#data_new_after').innerText = data.new_after; - element.querySelector('#data_new_trend').innerHTML = trend(data.new_trend); - element.querySelector('#data_regular_before').innerText = data.regular_before; - element.querySelector('#data_regular_after').innerText = data.regular_after; - element.querySelector('#data_regular_trend').innerHTML = trend(data.regular_trend); - - element.querySelector('#data_average_playtime_before').innerText = data.average_playtime_before; - element.querySelector('#data_average_playtime_after').innerText = data.average_playtime_after; - element.querySelector('#data_average_playtime_trend').innerHTML = trend(data.average_playtime_trend); - element.querySelector('#data_sessions_before').innerText = data.sessions_before; - element.querySelector('#data_sessions_after').innerText = data.sessions_after; - element.querySelector('#data_sessions_trend').innerHTML = trend(data.sessions_trend); - element.querySelector('#data_session_length_average_before').innerText = data.session_length_average_before; - element.querySelector('#data_session_length_average_after').innerText = data.session_length_average_after; - element.querySelector('#data_session_length_average_trend').innerHTML = trend(data.session_length_average_trend); - -} - -/* This function loads Online Activity Overview tab */ -function loadOnlineActivityOverviewValues(json, error) { - const tab = document.getElementById('online-activity-overview'); - if (error) { - displayError(tab, error); - return; - } - - // Online Activity as Numbers - let data = json.numbers; - let element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_unique_players_30d').innerHTML = data.unique_players_30d + smallTrend(data.unique_players_30d_trend); - element.querySelector('#data_unique_players_7d').innerText = data.unique_players_7d; - element.querySelector('#data_unique_players_24h').innerText = data.unique_players_24h; - - element.querySelector('#data_unique_players_30d_avg').innerHTML = data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend); - element.querySelector('#data_unique_players_7d_avg').innerText = data.unique_players_7d_avg; - element.querySelector('#data_unique_players_24h_avg').innerText = data.unique_players_24h_avg; - - element.querySelector('#data_new_players_30d').innerHTML = data.new_players_30d + smallTrend(data.new_players_30d_trend); - element.querySelector('#data_new_players_7d').innerText = data.new_players_7d; - element.querySelector('#data_new_players_24h').innerText = data.new_players_24h; - - element.querySelector('#data_new_players_30d_avg').innerHTML = data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend); - element.querySelector('#data_new_players_7d_avg').innerText = data.new_players_7d_avg; - element.querySelector('#data_new_players_24h_avg').innerText = data.new_players_24h_avg; - - element.querySelector('#data_new_players_retention_30d').innerText = '(' + data.new_players_retention_30d + '/' + data.new_players_30d + ') ' + data.new_players_retention_30d_perc; - element.querySelector('#data_new_players_retention_7d').innerText = '(' + data.new_players_retention_7d + '/' + data.new_players_7d + ') ' + data.new_players_retention_7d_perc; - element.querySelector('#data_new_players_retention_24h').innerHTML = '(' + data.new_players_retention_24h + '/' + data.new_players_24h + ') ' + data.new_players_retention_24h_perc + ' '; - - element.querySelector('#data_playtime_30d').innerHTML = data.playtime_30d + smallTrend(data.playtime_30d_trend); - element.querySelector('#data_playtime_7d').innerText = data.playtime_7d; - element.querySelector('#data_playtime_24h').innerText = data.playtime_24h; - - element.querySelector('#data_playtime_30d_avg').innerHTML = data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend); - element.querySelector('#data_playtime_7d_avg').innerText = data.playtime_7d_avg; - element.querySelector('#data_playtime_24h_avg').innerText = data.playtime_24h_avg; - - element.querySelector('#data_session_length_30d_avg').innerHTML = data.session_length_30d_avg + smallTrend(data.session_length_30d_trend); - element.querySelector('#data_session_length_7d_avg').innerText = data.session_length_7d_avg; - element.querySelector('#data_session_length_24h_avg').innerText = data.session_length_24h_avg; - - element.querySelector('#data_sessions_30d').innerHTML = data.sessions_30d + smallTrend(data.sessions_30d_trend); - element.querySelector('#data_sessions_7d').innerText = data.sessions_7d; - element.querySelector('#data_sessions_24h').innerText = data.sessions_24h; - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_players_first_join_avg').innerHTML = data.players_first_join_avg + smallTrend(data.players_first_join_trend); - element.querySelector('#data_first_session_length_avg').innerHTML = data.first_session_length_avg + smallTrend(data.first_session_length_trend); - element.querySelector('#data_lone_joins').innerHTML = data.lone_joins + smallTrend(data.lone_joins_trend); - element.querySelector('#data_lone_new_joins').innerHTML = data.lone_new_joins + smallTrend(data.lone_new_joins_trend); -} - -/* This function loads Sessions tab */ -function loadSessionValues(json, error) { - const tab = document.getElementById('sessions-overview'); - if (error) { - displayError(tab, error); - return; - } - - // Insights - let data = json.insights; - let element = tab.querySelector('#data_insights'); - - element.querySelector('#data_total_playtime').innerText = data.total_playtime; - element.querySelector('#data_afk_time').innerText = data.afk_time; - element.querySelector('#data_afk_time_perc').innerText = data.afk_time_perc -} - -/* This function loads Playerbase Overview tab */ -function loadPlayerbaseOverviewValues(json, error) { - const tab = document.getElementById('playerbase-overview'); - if (error) { - displayError(tab, error); - return; - } - - // Trends - let data = json.trends; - let element = tab.querySelector('#data_trends'); - - element.querySelector('#data_total_players_then').innerText = data.total_players_then; - element.querySelector('#data_total_players_now').innerText = data.total_players_now; - element.querySelector('#data_total_players_trend').innerHTML = trend(data.total_players_trend); - element.querySelector('#data_regular_players_then').innerText = data.regular_players_then; - element.querySelector('#data_regular_players_now').innerText = data.regular_players_now; - element.querySelector('#data_regular_players_trend').innerHTML = trend(data.regular_players_trend); - element.querySelector('#data_playtime_avg_then').innerText = data.playtime_avg_then; - element.querySelector('#data_playtime_avg_now').innerText = data.playtime_avg_now; - element.querySelector('#data_playtime_avg_trend').innerHTML = trend(data.playtime_avg_trend); - element.querySelector('#data_afk_then').innerText = data.afk_then; - element.querySelector('#data_afk_now').innerText = data.afk_now; - element.querySelector('#data_afk_trend').innerHTML = trend(data.afk_trend); - element.querySelector('#data_regular_playtime_avg_then').innerText = data.regular_playtime_avg_then; - element.querySelector('#data_regular_playtime_avg_now').innerText = data.regular_playtime_avg_now; - element.querySelector('#data_regular_playtime_avg_trend').innerHTML = trend(data.regular_playtime_avg_trend); - element.querySelector('#data_regular_session_avg_then').innerText = data.regular_session_avg_then; - element.querySelector('#data_regular_session_avg_now').innerText = data.regular_session_avg_now; - element.querySelector('#data_regular_session_avg_trend').innerHTML = trend(data.regular_session_avg_trend); - element.querySelector('#data_regular_afk_then').innerText = data.regular_afk_avg_then; - element.querySelector('#data_regular_afk_now').innerText = data.regular_afk_avg_now; - element.querySelector('#data_regular_afk_trend').innerHTML = trend(data.regular_afk_avg_trend); - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_new_to_regular').innerHTML = data.new_to_regular + smallTrend(data.new_to_regular_trend); - element.querySelector('#data_regular_to_inactive').innerHTML = data.regular_to_inactive + smallTrend(data.regular_to_inactive_trend); -} - -// Lowercase due to locale translation: Server -function loadservers(json, error) { - if (error) { - displayError(document.getElementById('servers-tab'), error); - return; - } - - const servers = json.servers; - - if (!servers || !servers.length) { - let elements = document.getElementsByClassName('nav-servers'); - for (let i = 0; i < elements.length; i++) { - elements[i].style.display = 'none'; - } - document.getElementById('game-server-warning').classList.remove('hidden'); - document.getElementById('data_server_list').innerHTML = - `

    No servers found in the database.

    It appears that Plan is not installed on any game servers or not connected to the same database. See wiki for Network tutorial.

    ` - document.getElementById('quick_view_players_online').innerText = `No server to display online activity for.`; - return; - } - - let navserversHtml = ''; - let serversHtml = ''; - for (let i = 0; i < servers.length; i++) { - navserversHtml += addserverToNav(servers[i]); - serversHtml += createnetworkserverBox(i, servers[i]); - } - - document.getElementById("navSrvContainer").innerHTML = navserversHtml; - document.getElementById("data_server_list").innerHTML = serversHtml; - - for (let i = 0; i < servers.length; i++) { - document.getElementById(`server_quick_view_${i}`) - .addEventListener('click', onViewserver(i, servers)); - } - onViewserver(0, servers)(); // Open first server. -} - -// Lowercase due to locale translation: Server -function addserverToNav(server) { - return ` ${server.name}`; -} - -// Lowercase due to locale translation: Network -function createnetworkserverBox(i, server) { - return `
    -
    -
    - ${server.name} -
    -
    -

    Registered Players${server.players}

    -

    Players Online${server.online}

    -
    -
    -
    - - Server Analysis - - -
    -
    `; -} - -// Lowercase due to locale translation: Server -function onViewserver(i, servers) { - return function () { - setTimeout(function () { - const server = servers[i]; - const playersOnlineSeries = { - name: s.name.playersOnline, - type: s.type.areaSpline, - tooltip: s.tooltip.zeroDecimals, - data: server.playersOnline, - color: v.colors.playersOnline, - yAxis: 0 - }; - document.querySelector('.data_server_name').innerText = server.name - playersChart('quick_view_players_online', playersOnlineSeries, 2); - - const quickView = document.getElementById('data_quick_view'); - - quickView.querySelector('#data_last_peak_date').innerText = server.last_peak_date; - quickView.querySelector('#data_last_peak_players').innerText = server.last_peak_players; - quickView.querySelector('#data_best_peak_date').innerText = server.best_peak_date; - quickView.querySelector('#data_best_peak_players').innerText = server.best_peak_players; - - quickView.querySelector('#data_unique').innerText = server.unique_players; - quickView.querySelector('#data_new').innerText = server.new_players; - quickView.querySelector('#data_avg_tps').innerText = server.avg_tps; - quickView.querySelector('#data_low_tps_spikes').innerText = server.low_tps_spikes; - quickView.querySelector('#data_downtime').innerText = server.downtime; - quickView.querySelector('#data_current_uptime').innerText = server.current_uptime; - }, 0); - } -} - -function loadPlayersOnlineGraph(json, error) { - if (json) { - const series = { - playersOnline: { - name: s.name.playersOnline, type: s.type.areaSpline, tooltip: s.tooltip.zeroDecimals, - data: json.playersOnline, color: v.colors.playersOnline, yAxis: 0 - } - }; - playersChart('playersOnlineChart', series.playersOnline, 2); - } else if (error) { - document.getElementById('playersOnlineChart').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadUniqueAndNewGraph(json, error) { - if (json) { - const uniquePlayers = { - name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.uniquePlayers, color: v.colors.playersOnline - }; - const newPlayers = { - name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.newPlayers, color: v.colors.newPlayers - }; - dayByDay('uniqueChart', [uniquePlayers, newPlayers]); - } else if (error) { - document.getElementById('uniqueChart').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadHourlyUniqueAndNewGraph(json, error) { - if (json) { - const uniquePlayers = { - name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.uniquePlayers, color: v.colors.playersOnline - }; - const newPlayers = { - name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.newPlayers, color: v.colors.newPlayers - }; - dayByDay('hourlyUniqueChart', [uniquePlayers, newPlayers]); - } else if (error) { - document.getElementById('uniqueChart').innerText = `Failed to load graph data: ${error}`; - } -} - -// Lowercase due to locale translation: Server -function loadserverPie(json, error) { - if (json) { - serverPieSeries = { - name: 'Server Playtime', - colorByPoint: true, - colors: json.server_pie_colors, - data: json.server_pie_series_30d - }; - serverPie('serverPie', serverPieSeries); - } else if (error) { - document.getElementById('serverPie').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadActivityGraphs(json, error) { - if (json) { - activityPie('activityPie', { - name: s.name.unit_players, colorByPoint: true, data: json.activity_pie_series - }); - stackChart('activityStackGraph', json.activity_labels, json.activity_series, s.name.unit_players); - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('activityPie').innerText = errorMessage; - document.getElementById('activityStackGraph').innerText = errorMessage; - } -} - -function loadGeolocationGraph(json, error) { - if (json) { - const geolocationSeries = { - name: s.name.unit_players, - type: 'map', - mapData: Highcharts.maps['custom/world'], - data: json.geolocation_series, - joinBy: ['iso-a3', 'code'] - }; - const geolocationBarSeries = { - color: json.colors.bars, - name: s.name.unit_players, - data: json.geolocation_bar_series.map(function (bar) { - return bar.value - }) - }; - const geolocationBarCategories = json.geolocation_bar_series.map(function (bar) { - return bar.label - }); - worldMap('worldMap', json.colors.low, json.colors.high, geolocationSeries); - horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], s.name.unit_players); - if (!json.geolocations_enabled) { - document.getElementById('geolocation-warning').classList.remove('hidden'); - } - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('worldMap').innerText = errorMessage; - document.getElementById('countryBarChart').innerText = errorMessage; - } -} - -function loadJoinAddressPie(json, error) { - if (json) { - const series = { - name: 'Used IP Addresses', - colorByPoint: true, - colors: json.colors, - data: json.slices - }; - joinAddressPie('joinAddressPie', series); - } else if (error) { - document.getElementById('joinAddressPie').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadperformanceserverOptions() { - const refreshElement = document.querySelector(`#performance .refresh-element`); - refreshElement.querySelector('i').addEventListener('click', () => { - if (refreshElement.querySelector('.refresh-notice').innerHTML.length) { - return; - } - onSelectperformanceservers(); - refreshElement.querySelector('.refresh-notice').innerHTML = ' Updating..'; - }); - const selector = document.getElementById('performance-server-selector'); - jsonRequest('./v1/network/serverOptions', function (json, error) { - if (json) { - let options = ``; - for (let server of json.servers) { - options += `${server.serverName}` - } - selector.innerHTML = options; - onSelectperformanceservers(); - } else if (error) { - selector.innerText = `Failed to load server list: ${error}` - } - }); -} - -async function onSelectperformanceservers() { - const selector = document.getElementById('performance-server-selector'); - const selectedServerUUIDs = []; - - for (const option of selector.selectedOptions) { - selectedServerUUIDs.push(option.getAttribute('data-plan-server-uuid')); - } - - const serverUUIDs = encodeURIComponent(JSON.stringify(selectedServerUUIDs)); - const loadedJson = { - servers: [], - errors: [], - zones: {}, - colors: {}, - timestamp_f: '' - } - const time = new Date().getTime(); - const monthMs = 2592000000; - const after = time - monthMs; - for (const serverUUID of selectedServerUUIDs) { - jsonRequest(`./v1/graph?type=optimizedPerformance&server=${serverUUID}&after=${after}`, (json, error) => { - if (json) { - loadedJson.servers.push(json); - loadedJson.zones = json.zones; - loadedJson.colors = json.colors; - loadedJson.timestamp_f = json.timestamp_f; - } else if (error) { - loadedJson.errors.push(error); - } - }); - } - await awaitUntil(() => selectedServerUUIDs.length === (loadedJson.servers.length + loadedJson.errors.length)); - - jsonRequest(`./v1/network/performanceOverview?servers=${serverUUIDs}`, loadPerformanceValues); - if (loadedJson.errors.length) { - await loadPerformanceGraph(undefined, loadedJson.errors[0]); - } else { - await loadPerformanceGraph({ - servers: loadedJson.servers, - zones: loadedJson.zones, - colors: loadedJson.colors - }, undefined); - } - const refreshElement = document.querySelector(`#performance .refresh-element`); - refreshElement.querySelector('.refresh-time').innerText = loadedJson.timestamp_f; - refreshElement.querySelector('.refresh-notice').innerHTML = ""; -} - -async function loadPerformanceGraph(json, error) { - if (json) { - const zones = { - tps: [{ - value: json.zones.tpsThresholdMed, - color: json.colors.low - }, { - value: json.zones.tpsThresholdHigh, - color: json.colors.med - }, { - value: 30, - color: json.colors.high - }], - disk: [{ - value: json.zones.diskThresholdMed, - color: json.colors.low - }, { - value: json.zones.diskThresholdHigh, - color: json.colors.med - }, { - value: Number.MAX_VALUE, - color: json.colors.high - }] - }; - const serverData = []; - for (const server of json.servers) { - serverData.push({ - serverName: server.serverName, - values: await mapToDataSeries(server.values) - }); - } - - const series = { - tps: [], - cpu: [], - ram: [], - entities: [], - chunks: [], - disk: [] - } - for (const server of serverData) { - series.tps.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.twoDecimals, - data: server.values.tps, color: json.colors.high, zones: zones.tps, yAxis: 0 - }); - series.cpu.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.twoDecimals, - data: server.values.cpu, color: json.colors.cpu, yAxis: 0 - }); - series.ram.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: server.values.ram, color: json.colors.ram, yAxis: 0 - }); - series.entities.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: server.values.entities, color: json.colors.entities, yAxis: 0 - }); - series.chunks.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: server.values.chunks, color: json.colors.chunks, yAxis: 0 - }); - series.disk.push({ - name: server.serverName, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: server.values.disk, color: json.colors.high, zones: zones.disk, yAxis: 0 - }); - } - - setTimeout(() => lineChart('tpsGraph', series.tps), 10); - setTimeout(() => lineChart('cpuGraph', series.cpu), 20); - setTimeout(() => lineChart('ramGraph', series.ram), 30); - setTimeout(() => lineChart('entityGraph', series.entities), 40); - setTimeout(() => lineChart('chunkGraph', series.chunks), 50); - setTimeout(() => lineChart('diskGraph', series.disk), 60); - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('tpsGraph').innerText = errorMessage; - document.getElementById('cpuGraph').innerText = errorMessage; - document.getElementById('ramGraph').innerText = errorMessage; - document.getElementById('entityGraph').innerText = errorMessage; - document.getElementById('chunkGraph').innerText = errorMessage; - document.getElementById('diskGraph').innerText = errorMessage; - } -} - - -/* This function loads Performance tab */ -function loadPerformanceValues(json, error) { - const tab = document.getElementById('performance'); - if (error) { - displayError(tab, error); - return; - } - - // as Numbers - let data = json.numbers; - let element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_low_tps_spikes_30d').innerText = data.low_tps_spikes_30d; - element.querySelector('#data_low_tps_spikes_7d').innerText = data.low_tps_spikes_7d; - element.querySelector('#data_low_tps_spikes_24h').innerText = data.low_tps_spikes_24h; - element.querySelector('#data_server_downtime_30d').innerText = data.server_downtime_30d; - element.querySelector('#data_server_downtime_7d').innerText = data.server_downtime_7d; - element.querySelector('#data_server_downtime_24h').innerText = data.server_downtime_24h; - element.querySelector('#data_avg_server_downtime_30d').innerText = data.avg_server_downtime_30d; - element.querySelector('#data_avg_server_downtime_7d').innerText = data.avg_server_downtime_7d; - element.querySelector('#data_avg_server_downtime_24h').innerText = data.avg_server_downtime_24h; - element.querySelector('#data_tps_30d').innerText = data.tps_30d; - element.querySelector('#data_tps_7d').innerText = data.tps_7d; - element.querySelector('#data_tps_24h').innerText = data.tps_24h; - element.querySelector('#data_cpu_30d').innerText = data.cpu_30d; - element.querySelector('#data_cpu_7d').innerText = data.cpu_7d; - element.querySelector('#data_cpu_24h').innerText = data.cpu_24h; - element.querySelector('#data_ram_30d').innerText = data.ram_30d; - element.querySelector('#data_ram_7d').innerText = data.ram_7d; - element.querySelector('#data_ram_24h').innerText = data.ram_24h; - element.querySelector('#data_entities_30d').innerText = data.entities_30d; - element.querySelector('#data_entities_7d').innerText = data.entities_7d; - element.querySelector('#data_entities_24h').innerText = data.entities_24h; - element.querySelector('#data_chunks_30d').innerText = data.chunks_30d; - element.querySelector('#data_chunks_7d').innerText = data.chunks_7d; - element.querySelector('#data_chunks_24h').innerText = data.chunks_24h; -} - -function loadPingGraph(json, error) { - if (json) { - const series = { - avgPing: { - name: s.name.avgPing, - type: s.type.spline, - tooltip: s.tooltip.twoDecimals, - data: json.avg_ping_series, - color: json.colors.avg - }, - maxPing: { - name: s.name.maxPing, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: json.max_ping_series, - color: json.colors.max - }, - minPing: { - name: s.name.minPing, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: json.min_ping_series, - color: json.colors.min - } - }; - lineChart('pingGraph', [series.avgPing, series.maxPing, series.minPing]); - } else if (error) { - document.getElementById('pingGraph').innerText = `Failed to load graph data: ${error}`; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js b/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js deleted file mode 100644 index b36ada6f9..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js +++ /dev/null @@ -1,26 +0,0 @@ -function loadPingTable(json, error) { - const pingTable = document.querySelector('#geolocations #data_ping_table tbody'); - - if (error) { - pingTable.innerHTML = `Error: ${error}---`; - return; - } - - const countries = json.table; - - if (!countries.length) { - pingTable.innerHTML = 'No Data---'; - return; - } - - pingTable.innerHTML = countries.map(createPingTableRow).join(''); -} - -function createPingTableRow(entry) { - return ` - ${entry.country} - ${entry.avg_ping} - ${entry.min_ping} - ${entry.max_ping} - ` -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/player-values.js b/Plan/common/src/main/resources/assets/plan/web/js/player-values.js deleted file mode 100644 index 459daee68..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/player-values.js +++ /dev/null @@ -1,236 +0,0 @@ -function displayError(element, error) { - element.find('.d-sm-flex').after(`') -} - -function loadPlayerOverviewValues(json, error) { - tab = $("#player-overview"); - if (error) { - displayError(tab, error); - return; - } - - /* Player information card */ - data = json.info; - element = $(tab).find("#data_player_info"); - - $(element).find("#data_online").replaceWith(data.online ? ` Online` : ' Offline'); - $(element).find("#data_titles").replaceWith((data.operator ? ` Operator` : '') + (data.banned ? ` Banned` : '')); - $(element).find("#data_kick_count").text(data.kick_count); - $(element).find("#data_player_kills").text(data.player_kill_count); - $(element).find("#data_mob_kills").text(data.mob_kill_count); - $(element).find("#data_deaths").text(data.death_count); - - $(element).find("#data_playtime").text(data.playtime); - $(element).find("#data_active_playtime").text(data.active_playtime); - $(element).find("#data_afk_time").text(data.afk_time); - $(element).find("#data_session_count").text(data.session_count); - $(element).find("#data_longest_session_length").text(data.longest_session_length); - $(element).find("#data_session_median").text(data.session_median); - - $(element).find("#data_activity_index").text(data.activity_index); - $(element).find("#data_activity_index_group").text(data.activity_index_group); - $(element).find("#data_favorite_server").text(data.favorite_server); - $(element).find("#data_latest_join_address").text(data.latest_join_address); - $(element).find("#data_average_ping").text(data.average_ping); - $(element).find("#data_best_ping").text(data.best_ping); - $(element).find("#data_worst_ping").text(data.worst_ping); - - $(element).find("#data_registered").text(data.registered); - $(element).find("#data_last_seen").text(data.last_seen); - - $('#data_nicknames').replaceWith(createNicknameTableBody(json.nicknames)); - - $('#data_connections').replaceWith(createConnectionsTableBody(json.connections)); - - // Online activity - data = json.online_activity; - element = $(tab).find("#data_online_activity"); - - $(element).find("#data_playtime_30d").text(data.playtime_30d); - $(element).find("#data_playtime_7d").text(data.playtime_7d); - $(element).find("#data_active_playtime_30d").text(data.active_playtime_30d); - $(element).find("#data_active_playtime_7d").text(data.active_playtime_7d); - $(element).find("#data_afk_time_30d").text(data.afk_time_30d); - $(element).find("#data_afk_time_7d").text(data.afk_time_7d); - $(element).find("#data_median_session_length_30d").text(data.median_session_length_30d); - $(element).find("#data_median_session_length_7d").text(data.median_session_length_7d); - $(element).find("#data_session_count_30d").text(data.session_count_30d); - $(element).find("#data_session_count_7d").text(data.session_count_7d); - $(element).find("#data_player_kills_30d").text(data.player_kill_count_30d); - $(element).find("#data_player_kills_7d").text(data.player_kill_count_7d); - $(element).find("#data_mob_kills_30d").text(data.mob_kill_count_30d); - $(element).find("#data_mob_kills_7d").text(data.mob_kill_count_7d); - $(element).find("#data_deaths_30d").text(data.death_count_30d); - $(element).find("#data_deaths_7d").text(data.death_count_7d) -} - -/* This function loads PvP & PvE tab */ -function loadPvPPvEValues(json, error) { - tab = $('#pvp-pve'); - if (error) { - displayError(tab, error); - return; - } - - // as Numbers - data = json.kill_data; - element = $(tab).find('#data_numbers'); - - $(element).find('#data_player_kills_total').text(data.player_kills_total); - $(element).find('#data_player_kills_30d').text(data.player_kills_30d); - $(element).find('#data_player_kills_7d').text(data.player_kills_7d); - - $(element).find('#data_player_deaths_total').text(data.player_deaths_total); - $(element).find('#data_player_deaths_30d').text(data.player_deaths_30d); - $(element).find('#data_player_deaths_7d').text(data.player_deaths_7d); - - $(element).find('#data_player_kdr_total').text(data.player_kdr_total); - $(element).find('#data_player_kdr_30d').text(data.player_kdr_30d); - $(element).find('#data_player_kdr_7d').text(data.player_kdr_7d); - - $(element).find('#data_mob_kills_total').text(data.mob_kills_total); - $(element).find('#data_mob_kills_30d').text(data.mob_kills_30d); - $(element).find('#data_mob_kills_7d').text(data.mob_kills_7d); - - $(element).find('#data_mob_deaths_total').text(data.mob_deaths_total); - $(element).find('#data_mob_deaths_30d').text(data.mob_deaths_30d); - $(element).find('#data_mob_deaths_7d').text(data.mob_deaths_7d); - - $(element).find('#data_mob_kdr_total').text(data.mob_kdr_total); - $(element).find('#data_mob_kdr_30d').text(data.mob_kdr_30d); - $(element).find('#data_mob_kdr_7d').text(data.mob_kdr_7d); - - $(element).find('#data_deaths_total').text(data.deaths_total); - $(element).find('#data_deaths_30d').text(data.deaths_30d); - $(element).find('#data_deaths_7d').text(data.deaths_7d); - - // Insights - element = $(tab).find('#data_insights'); - - $(element).find('#data_weapon_1st').text(data.weapon_1st); - $(element).find('#data_weapon_2nd').text(data.weapon_2nd); - $(element).find('#data_weapon_3rd').text(data.weapon_3rd); -} - -function createNicknameTableBody(nicknames) { - var table = ''; - - if (nicknames.length === 0) { - table += `No Nicknames--` - } - - for (var i = 0; i < nicknames.length; i++) { - var nickname = nicknames[i]; - table += '' + nickname.nickname + '' + - '' + nickname.server + '' + - '' + nickname.date + '' - } - - table += ''; - return table; -} - -function createConnectionsTableBody(connections) { - var table = ''; - - if (connections.length === 0) { - table += `No Data-` - } - - for (var i = 0; i < connections.length; i++) { - var connection = connections[i]; - table += '' + connection.geolocation + '' + - '' + connection.date + '' - } - - table += ''; - return table; -} - -// Lowercase due to locale translation: Server -function loadserverAccordion(json, error) { - tab = $("#server-overview"); - if (error) { - displayError(tab, error); - return; - } - - serverTable = tab.find("#tableSAccordion").find("tbody"); - - var servers = json.servers; - - if (!servers.length) { - serverTable.append(`No Data---`) - } - - var serversHtml = ''; - for (var i = 0; i < servers.length; i++) { - var server = servers[i]; - var title = createserverAccordionTitle(i, server); - var body = createserverAccordionBody(i, server); - - serversHtml += title + body; - } - - serverTable.append(serversHtml); - - for (var i = 0; i < servers.length; i++) { - $('#server_h_' + i).click(onOpenserver(i, servers)); - } -} - -function onOpenserver(i, servers) { - var opened = false; - return function () { - if (opened) { - return; - } - setTimeout(function () { - var server = servers[i]; - var worldSeries = {name: `World Playtime`, colorByPoint: true, data: server.world_pie_series}; - var gmSeries = server.gm_series; - - worldPie("worldpie_server_" + i, worldSeries, gmSeries); - }, 250); - opened = true; - } -} - -// Lowercase due to locale translation: Server -function createserverAccordionTitle(i, server) { - return '' - + server.server_name + - (server.operator ? ' ' : '') + - (server.banned ? ' ' : '') + - '' - + '' + server.playtime + '' - + '' + server.registered + '' - + '' + server.last_seen + '' -} - -// Lowercase due to locale translation: Server -function createserverAccordionBody(i, server) { - - return `` + - `` + - `
    ` + - `
    ` + - (server.operator ? `

    Operator

    ` : ``) + - (server.banned ? `

    Banned

    ` : ``) + - (server.operator || server.banned ? `
    ` : ``) + - `

    Sessions` + server.session_count + `

    ` + - `

    Playtime` + server.playtime + `

    ` + - `

    AFK Time` + server.afk_time + `

    ` + - `

    Longest Session` + server.longest_session_length + `

    ` + - `

    Session Median` + server.session_median + `

    ` + - `
    ` + - `

    Join Address` + server.join_address + `

    ` + - `
    ` + - `

    Player Kills` + server.player_kills + `

    ` + - `

    Mob Kills` + server.mob_kills + `

    ` + - `

    Deaths` + server.deaths + `

    ` + - `
    ` + - `
    ` + - `
    ` + - `
    ` -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/query.js b/Plan/common/src/main/resources/assets/plan/web/js/query.js deleted file mode 100644 index 01b4223e7..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/query.js +++ /dev/null @@ -1,541 +0,0 @@ -const loadedFilters = []; -const queryState = { - filterCount: 0, - filters: [], - view: { - afterDate: null, - afterTime: null, - beforeDate: null, - beforeTime: null, - servers: [] - }, - 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; - -let serverMap = {}; - -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); - - // Load server selector or hide it - if (json.view.servers.length >= 2) { - let options = ``; - for (let server of json.view.servers) { - if (server.proxy) continue; - serverMap[server.serverUUID] = server; - options += `` - } - const serverSelector = document.getElementById("server-selector"); - serverSelector.innerHTML = options; - - serverSelector.addEventListener('click', () => { - queryState.view.servers = []; - if (serverSelector.selectedOptions.length !== serverSelector.options.length) { - for (const option of serverSelector.selectedOptions) { - queryState.view.servers.push(serverMap[option.getAttribute('data-plan-server-uuid')]); - } - } - document.getElementById("serverDropdown").innerText = queryState.view.servers.length - ? `using data of ${queryState.view.servers.length} server(s)` - : "using data of all servers" - }) - } else { - document.getElementById("serverDropdown").classList.add("hidden"); - } - - 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 `${getReadableFilterName(filter)}`; -} - -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 performNextQuery() { - 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 = - `
    -
    - -

    Loading..

    -
    -
    `; - } else { - const icon = document.createElement('template'); - icon.innerHTML = '' - 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 = '' - queryButton.querySelector('.fa').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 += `

    `; - for (let j = 0; j < i * 4; j++) { - pathHtml += " "; - } - pathHtml += ` ${getReadableFilterName(step)} matched ${step.size} players

    ` - } - - 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, - deferRender: true, - columns: json.data.players.columns, - data: json.data.players.data, - order: [[5, "desc"]] - }); - - if ('undefined' !== typeof nightmode && nightmode == true) { - document.querySelector('.table').classList.add('table-dark'); - } - - 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 = - `
    -
    -

    Plan · - Query Results

    -

    (matched ${resultCount} players)

    -
    - -
    -
    -
    -
    -
    - View: ${afterDate} - ${beforeDate}, -${view.servers.length ? "using data of servers: " + view.servers.map(server => server.serverName).join(', ') : "using data of all servers"}
    -
    - - - - -
    Loading..
    -
    -
    -
    - -
    -
    -
    -
    -
    - Activity of matched players
    -
    -
    -
    -
    -
    -
    -
    -
    - Activity on
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    Sessions within view
    -
    -
    -

    Sessions

    -

    Average Sessions / Player

    -

    Average Session Length

    -
    -

    Playtime

    -

    Active Playtime

    -

    AFK Time

    -
    -

    Average Playtime / Player

    -

    Average Active Playtime / Player

    -

    Average AFK Time / Player

    -
    -
    -
    - -
    -
    -
    -
    - Geolocations
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    `; -} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js b/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js deleted file mode 100644 index 45668c7ee..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js +++ /dev/null @@ -1,138 +0,0 @@ -const content = document.getElementById("content"); -const navButtons = Array.from(document.getElementsByClassName("nav-button")); -const tabs = Array.from(document.getElementsByClassName("tab")).filter(tab => tab.id); // TABS NEED IDS -const tabCount = tabs.length; - -function openTab(openIndex) { - for (let navButton of navButtons) { - if (navButton.classList.contains('active')) { - navButton.classList.remove('active'); - } - } - - const outOfRange = openIndex < 0 || tabCount < openIndex; - const slideIndex = outOfRange ? 0 : openIndex; - - navButtons[slideIndex].classList.add('active'); - - window.scrollTo(0, 0); // Scroll to top - const tabWidthPercent = -100 / tabCount; - const verticalScrollPercent = slideIndex * tabWidthPercent; - content.style.transition = "0.5s"; - content.style.transform = `translate3d(${verticalScrollPercent}%,0px,0)`; -} - -function openPage() { - // substr removes tab- from the id - const uriHash = (window.location.hash.substr(5)).split("&").filter(part => part); - - if (!uriHash.length) { - openTab(0); - return; - } - - const tabId = uriHash[0]; - const openIndex = tabs.map(tab => tab.id).indexOf(tabId); - openTab(openIndex); - - if (uriHash.length > 1) { - const bootstrapTabId = uriHash[1]; - let tab = document.querySelector('a[href="#' + bootstrapTabId + '"]'); - let tabInstance = bootstrap.Tab.getInstance(tab); - - if (tabInstance) { // show tab if it has been instantiated - tabInstance.show(); - } else { // create new Tab object and show the tab - new bootstrap.Tab(tab).show(); - } - } -} - -// Prepare tabs for display -content.style.transform = "translate3d(0px,0px,0)"; -content.style.width = (Math.max(100, tabCount * 100)) + "%"; -content.style.opacity = "1"; -for (let tab of tabs) { - tab.style.width = `${100 / tabCount}%`; -} - -window.addEventListener('hashchange', openPage); - -//Sidebar navigation tabs -$('#accordionSidebar .nav-item a').click(event => { - if (history.replaceState && event.currentTarget.href.split('#')[1].length > 0) { - event.preventDefault(); - history.replaceState(undefined, undefined, '#' + event.currentTarget.href.split('#')[1]); - openPage(); - } -}); - -// Persistent Bootstrap tabs -document.querySelectorAll(".nav-tabs a.nav-link").forEach(item => { - item.addEventListener("click", event => { - let uriHash; - if (window.location.hash) { - uriHash = (window.location.hash).split("&"); - } else { - window.location.hash = document.querySelector(".sidebar a.nav-link").href.split("#")[1] - uriHash = [window.location.hash]; - } - const targetTab = event.currentTarget.href.split('#')[1]; - if (history.replaceState) { - event.preventDefault(); - history.replaceState(undefined, undefined, uriHash[0] + '&' + targetTab); - openPage(); - } else - window.location.hash = uriHash[0] + '&' + targetTab; - }); -}); - -let oldWidth = null; - -function reduceSidebar() { - const newWidth = $(window).width(); - if (oldWidth && oldWidth === newWidth) { - return; - } - - 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 (!isSidebarHidden) body.classList.add('sidebar-hidden'); - if (!isModalCloserHidden) closeModal.classList.add('hidden'); - - // Close any open menu accordions when window is resized - document.querySelectorAll('.sidebar .collapse').forEach(element => { - let elCollapse = bootstrap.Collapse.getInstance(element); - if (elCollapse) { elCollapse.hide(); } - }) - } else if ($(window).width() > 1400 && isSidebarHidden) { - body.classList.remove('sidebar-hidden'); - if (!isModalCloserHidden) closeModal.classList.add('hidden'); - } - oldWidth = newWidth; -} - -reduceSidebar(); -$(window).resize(reduceSidebar); - -function toggleSidebar() { - document.querySelector('body').classList.toggle('sidebar-hidden'); - // Close any open menu accordions - document.querySelectorAll('.sidebar .collapse').forEach(element => { - let elCollapse = bootstrap.Collapse.getInstance(element); - if (elCollapse) { elCollapse.hide(); } - }) - - const closeModal = document.querySelector('.sidebar-close-modal'); - if ($(window).width() < 900) { - closeModal.classList.toggle('hidden'); - } else if (!closeModal.classList.contains('hidden')) { - closeModal.classList.add('hidden'); - } -} - -document.querySelectorAll('.sidebar-toggler,.sidebar-close-modal') - .forEach(element => element.addEventListener('click', toggleSidebar)); diff --git a/Plan/common/src/main/resources/assets/plan/web/js/server-values.js b/Plan/common/src/main/resources/assets/plan/web/js/server-values.js deleted file mode 100644 index e657b3d55..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/server-values.js +++ /dev/null @@ -1,608 +0,0 @@ -const trend_up_good = " "; -const trend_up_bad = " "; -const trend_down_bad = " "; -const trend_down_good = " "; -const trend_same = " "; - -const trend_end = ""; - -function trend(trend) { - if (!trend) { - return trend_same + '?' + trend_end; - } - switch (trend.direction) { - case '+': - return (trend.reversed ? trend_up_bad : trend_up_good) + trend.text + trend_end; - case '-': - return (trend.reversed ? trend_down_good : trend_down_bad) + trend.text + trend_end; - default: - return trend_same + trend.text + trend_end; - } -} - -function smallTrend(trend) { - if (!trend) { - return ' '; - } - switch (trend.direction) { - case '+': - trend_color = trend.reversed ? 'text-danger' : 'text-success'; - return ` `; - case '-': - trend_color = trend.reversed ? 'text-success' : 'text-danger'; - return ` `; - default: - return ` `; - } -} - -function displayError(element, error) { - insertElementAfterElement(element.querySelector('.d-sm-flex'), () => { - const alert = document.createElement('div'); - alert.classList.add('alert', 'alert-danger'); - alert.setAttribute('role', 'alert'); - alert.innerText = `Failed to load values: ${error}`; - return alert; - }) -} - -/* This function loads Server Overview tab */ -// Lowercase due to locale translation: Server -function loadserverOverviewValues(json, error) { - const tab = document.getElementById('server-overview'); - - if (error) { - displayError(tab, error); - return; - } - - // Last 7 days - let data = json.last_7_days; - let element = tab.querySelector('#data_7_days'); - - element.querySelector('#data_unique').innerText = data.unique_players; - element.querySelector('#data_unique_day').innerText = data.unique_players_day; - element.querySelector('#data_new').innerText = data.new_players; - element.querySelector('#data_retention').innerText = '(' + data.new_players_retention + '/' + data.new_players + ')'; - element.querySelector('#data_retention_perc').innerText = data.new_players_retention_perc; - - element.querySelector('#data_avg_tps').innerText = data.average_tps; - element.querySelector('#data_low_tps_spikes').innerText = data.low_tps_spikes; - element.querySelector('#data_downtime').innerText = data.downtime; - - // Server As Numbers - data = json.numbers; - element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_current_uptime').innerText = data.current_uptime; - element.querySelector('#data_total').innerText = data.total_players; - element.querySelector('#data_regular').innerText = data.regular_players; - element.querySelector('#data_online').innerText = data.online_players; - - element.querySelector('#data_last_peak_date').innerText = data.last_peak_date; - element.querySelector('#data_last_peak_players').innerText = data.last_peak_players; - element.querySelector('#data_best_peak_date').innerText = data.best_peak_date; - element.querySelector('#data_best_peak_players').innerText = data.best_peak_players; - - element.querySelector('#data_playtime').innerText = data.playtime; - element.querySelector('#data_player_playtime').innerText = data.player_playtime; - element.querySelector('#data_sessions').innerText = data.sessions; - - element.querySelector('#data_player_kills').innerText = data.player_kills; - element.querySelector('#data_mob_kills').innerText = data.mob_kills; - element.querySelector('#data_deaths').innerText = data.deaths; - - // Week Comparison - data = json.weeks; - element = tab.querySelector('#data_weeks'); - - element.querySelector('#data_start').innerText = data.start; - element.querySelector('#data_midpoint').innerText = data.midpoint; - element.querySelector('#data_midpoint2').innerText = data.midpoint; - element.querySelector('#data_end').innerText = data.end; - - element.querySelector('#data_unique_before').innerText = data.unique_before; - element.querySelector('#data_unique_after').innerText = data.unique_after; - element.querySelector('#data_unique_trend').innerHTML = trend(data.unique_trend); - element.querySelector('#data_new_before').innerText = data.new_before; - element.querySelector('#data_new_after').innerText = data.new_after; - element.querySelector('#data_new_trend').innerHTML = trend(data.new_trend); - element.querySelector('#data_regular_before').innerText = data.regular_before; - element.querySelector('#data_regular_after').innerText = data.regular_after; - element.querySelector('#data_regular_trend').innerHTML = trend(data.regular_trend); - - element.querySelector('#data_average_playtime_before').innerText = data.average_playtime_before; - element.querySelector('#data_average_playtime_after').innerText = data.average_playtime_after; - element.querySelector('#data_average_playtime_trend').innerHTML = trend(data.average_playtime_trend); - element.querySelector('#data_sessions_before').innerText = data.sessions_before; - element.querySelector('#data_sessions_after').innerText = data.sessions_after; - element.querySelector('#data_sessions_trend').innerHTML = trend(data.sessions_trend); - - element.querySelector('#data_player_kills_before').innerText = data.player_kills_before; - element.querySelector('#data_player_kills_after').innerText = data.player_kills_after; - element.querySelector('#data_player_kills_trend').innerHTML = trend(data.player_kills_trend); - element.querySelector('#data_mob_kills_before').innerText = data.mob_kills_before; - element.querySelector('#data_mob_kills_after').innerText = data.mob_kills_after; - element.querySelector('#data_mob_kills_trend').innerHTML = trend(data.mob_kills_trend); - element.querySelector('#data_deaths_before').innerText = data.deaths_before; - element.querySelector('#data_deaths_after').innerText = data.deaths_after; - element.querySelector('#data_deaths_trend').innerHTML = trend(data.deaths_trend); -} - -/* This function loads Online Activity Overview tab */ -function loadOnlineActivityOverviewValues(json, error) { - const tab = document.getElementById('online-activity-overview'); - - if (error) { - displayError(tab, error); - return; - } - - // Online Activity as Numbers - let data = json.numbers; - let element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_unique_players_30d').innerHTML = data.unique_players_30d + smallTrend(data.unique_players_30d_trend); - element.querySelector('#data_unique_players_7d').innerText = data.unique_players_7d; - element.querySelector('#data_unique_players_24h').innerText = data.unique_players_24h; - - element.querySelector('#data_unique_players_30d_avg').innerHTML = data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend); - element.querySelector('#data_unique_players_7d_avg').innerText = data.unique_players_7d_avg; - element.querySelector('#data_unique_players_24h_avg').innerText = data.unique_players_24h_avg; - - element.querySelector('#data_new_players_30d').innerHTML = data.new_players_30d + smallTrend(data.new_players_30d_trend); - element.querySelector('#data_new_players_7d').innerText = data.new_players_7d; - element.querySelector('#data_new_players_24h').innerText = data.new_players_24h; - - element.querySelector('#data_new_players_30d_avg').innerHTML = data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend); - element.querySelector('#data_new_players_7d_avg').innerText = data.new_players_7d_avg; - element.querySelector('#data_new_players_24h_avg').innerText = data.new_players_24h_avg; - - element.querySelector('#data_new_players_retention_30d').innerText = `(${data.new_players_retention_30d}/${data.new_players_30d}) ${data.new_players_retention_30d_perc}`; - element.querySelector('#data_new_players_retention_7d').innerText = `(${data.new_players_retention_7d}/${data.new_players_7d}) ${data.new_players_retention_7d_perc}`; - element.querySelector('#data_new_players_retention_24h').innerHTML = `(${data.new_players_retention_24h}/${data.new_players_24h}) ${data.new_players_retention_24h_perc} `; - - element.querySelector('#data_playtime_30d').innerHTML = data.playtime_30d + smallTrend(data.playtime_30d_trend); - element.querySelector('#data_playtime_7d').innerText = data.playtime_7d; - element.querySelector('#data_playtime_24h').innerText = data.playtime_24h; - - element.querySelector('#data_playtime_30d_avg').innerHTML = data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend); - element.querySelector('#data_playtime_7d_avg').innerText = data.playtime_7d_avg; - element.querySelector('#data_playtime_24h_avg').innerText = data.playtime_24h_avg; - - element.querySelector('#data_session_length_30d_avg').innerHTML = data.session_length_30d_avg + smallTrend(data.session_length_30d_trend); - element.querySelector('#data_session_length_7d_avg').innerText = data.session_length_7d_avg; - element.querySelector('#data_session_length_24h_avg').innerText = data.session_length_24h_avg; - - element.querySelector('#data_sessions_30d').innerHTML = data.sessions_30d + smallTrend(data.sessions_30d_trend); - element.querySelector('#data_sessions_7d').innerText = data.sessions_7d; - element.querySelector('#data_sessions_24h').innerText = data.sessions_24h; - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_players_first_join_avg').innerHTML = data.players_first_join_avg + smallTrend(data.players_first_join_trend); - element.querySelector('#data_first_session_length_avg').innerHTML = data.first_session_length_avg + smallTrend(data.first_session_length_trend); - element.querySelector('#data_first_session_length_median').innerHTML = data.first_session_length_median + smallTrend(data.first_session_length_median_trend); - element.querySelector('#data_lone_joins').innerHTML = data.lone_joins + smallTrend(data.lone_joins_trend); - element.querySelector('#data_lone_new_joins').innerHTML = data.lone_new_joins + smallTrend(data.lone_new_joins_trend); -} - -/* This function loads Sessions tab */ -function loadSessionValues(json, error) { - const tab = document.getElementById('sessions-overview'); - - if (error) { - displayError(tab, error); - return; - } - - // Insights - let data = json.insights; - const element = tab.querySelector('#data_insights'); - - element.querySelector('#data_most_active_gamemode').innerText = data.most_active_gamemode; - element.querySelector('#data_most_active_gamemode_perc').innerText = data.most_active_gamemode_perc; - element.querySelector('#data_server_occupied').innerText = "~" + data.server_occupied; - element.querySelector('#data_server_occupied_perc').innerText = data.server_occupied_perc; - element.querySelector('#data_total_playtime').innerText = data.total_playtime; - element.querySelector('#data_afk_time').innerText = data.afk_time; - element.querySelector('#data_afk_time_perc').innerText = data.afk_time_perc; -} - -/* This function loads PvP & PvE tab */ -function loadPvPPvEValues(json, error) { - const tab = document.getElementById('pvp-pve'); - - if (error) { - displayError(tab, error); - return; - } - - // as Numbers - let data = json.numbers; - let element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_player_kills_total').innerText = data.player_kills_total; - element.querySelector('#data_player_kills_30d').innerText = data.player_kills_30d; - element.querySelector('#data_player_kills_7d').innerText = data.player_kills_7d; - - element.querySelector('#data_player_kdr_avg').innerText = data.player_kdr_avg; - element.querySelector('#data_player_kdr_avg_30d').innerText = data.player_kdr_avg_30d; - element.querySelector('#data_player_kdr_avg_7d').innerText = data.player_kdr_avg_7d; - - element.querySelector('#data_mob_kills_total').innerText = data.mob_kills_total; - element.querySelector('#data_mob_kills_30d').innerText = data.mob_kills_30d; - element.querySelector('#data_mob_kills_7d').innerText = data.mob_kills_7d; - - element.querySelector('#data_mob_deaths_total').innerText = data.mob_deaths_total; - element.querySelector('#data_mob_deaths_30d').innerText = data.mob_deaths_30d; - element.querySelector('#data_mob_deaths_7d').innerText = data.mob_deaths_7d; - - element.querySelector('#data_mob_kdr_total').innerText = data.mob_kdr_total; - element.querySelector('#data_mob_kdr_30d').innerText = data.mob_kdr_30d; - element.querySelector('#data_mob_kdr_7d').innerText = data.mob_kdr_7d; - - element.querySelector('#data_deaths_total').innerText = data.deaths_total; - element.querySelector('#data_deaths_30d').innerText = data.deaths_30d; - element.querySelector('#data_deaths_7d').innerText = data.deaths_7d; - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_weapon_1st').innerText = data.weapon_1st; - element.querySelector('#data_weapon_2nd').innerText = data.weapon_2nd; - element.querySelector('#data_weapon_3rd').innerText = data.weapon_3rd; -} - -/* This function loads Playerbase Overview tab */ -function loadPlayerbaseOverviewValues(json, error) { - const tab = document.getElementById('playerbase-overview'); - if (error) { - displayError(tab, error); - return; - } - - // Trends - let data = json.trends; - let element = tab.querySelector('#data_trends'); - - element.querySelector('#data_total_players_then').innerText = data.total_players_then; - element.querySelector('#data_total_players_now').innerText = data.total_players_now; - element.querySelector('#data_total_players_trend').innerHTML = trend(data.total_players_trend); - element.querySelector('#data_regular_players_then').innerText = data.regular_players_then; - element.querySelector('#data_regular_players_now').innerText = data.regular_players_now; - element.querySelector('#data_regular_players_trend').innerHTML = trend(data.regular_players_trend); - element.querySelector('#data_playtime_avg_then').innerText = data.playtime_avg_then; - element.querySelector('#data_playtime_avg_now').innerText = data.playtime_avg_now; - element.querySelector('#data_playtime_avg_trend').innerHTML = trend(data.playtime_avg_trend); - element.querySelector('#data_afk_then').innerText = data.afk_then; - element.querySelector('#data_afk_now').innerText = data.afk_now; - element.querySelector('#data_afk_trend').innerHTML = trend(data.afk_trend); - element.querySelector('#data_regular_playtime_avg_then').innerText = data.regular_playtime_avg_then; - element.querySelector('#data_regular_playtime_avg_now').innerText = data.regular_playtime_avg_now; - element.querySelector('#data_regular_playtime_avg_trend').innerHTML = trend(data.regular_playtime_avg_trend); - element.querySelector('#data_regular_session_avg_then').innerText = data.regular_session_avg_then; - element.querySelector('#data_regular_session_avg_now').innerText = data.regular_session_avg_now; - element.querySelector('#data_regular_session_avg_trend').innerHTML = trend(data.regular_session_avg_trend); - element.querySelector('#data_regular_afk_then').innerText = data.regular_afk_avg_then; - element.querySelector('#data_regular_afk_now').innerText = data.regular_afk_avg_now; - element.querySelector('#data_regular_afk_trend').innerHTML = trend(data.regular_afk_avg_trend); - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_new_to_regular').innerHTML = data.new_to_regular + smallTrend(data.new_to_regular_trend); - element.querySelector('#data_regular_to_inactive').innerHTML = data.regular_to_inactive + smallTrend(data.regular_to_inactive_trend); -} - -/* This function loads Performance tab */ -function loadPerformanceValues(json, error) { - const tab = document.getElementById('performance'); - if (error) { - displayError(tab, error); - return; - } - - // as Numbers - let data = json.numbers; - let element = tab.querySelector('#data_numbers'); - - element.querySelector('#data_low_tps_spikes_30d').innerText = data.low_tps_spikes_30d; - element.querySelector('#data_low_tps_spikes_7d').innerText = data.low_tps_spikes_7d; - element.querySelector('#data_low_tps_spikes_24h').innerText = data.low_tps_spikes_24h; - element.querySelector('#data_server_downtime_30d').innerText = data.server_downtime_30d; - element.querySelector('#data_server_downtime_7d').innerText = data.server_downtime_7d; - element.querySelector('#data_server_downtime_24h').innerText = data.server_downtime_24h; - element.querySelector('#data_players_30d').innerText = data.players_30d; - element.querySelector('#data_players_7d').innerText = data.players_7d; - element.querySelector('#data_players_24h').innerText = data.players_24h; - element.querySelector('#data_tps_30d').innerText = data.tps_30d; - element.querySelector('#data_tps_7d').innerText = data.tps_7d; - element.querySelector('#data_tps_24h').innerText = data.tps_24h; - element.querySelector('#data_cpu_30d').innerText = data.cpu_30d; - element.querySelector('#data_cpu_7d').innerText = data.cpu_7d; - element.querySelector('#data_cpu_24h').innerText = data.cpu_24h; - element.querySelector('#data_ram_30d').innerText = data.ram_30d; - element.querySelector('#data_ram_7d').innerText = data.ram_7d; - element.querySelector('#data_ram_24h').innerText = data.ram_24h; - element.querySelector('#data_entities_30d').innerText = data.entities_30d; - element.querySelector('#data_entities_7d').innerText = data.entities_7d; - element.querySelector('#data_entities_24h').innerText = data.entities_24h; - if ("Unavailable" === data.chunks_30d) { - document.getElementById('sponge-chunk-warning').classList.remove('hidden'); - } - element.querySelector('#data_chunks_30d').innerText = data.chunks_30d; - element.querySelector('#data_chunks_7d').innerText = data.chunks_7d; - element.querySelector('#data_chunks_24h').innerText = data.chunks_24h; - element.querySelector('#data_max_disk_30d').innerText = data.max_disk_30d; - element.querySelector('#data_max_disk_7d').innerText = data.max_disk_7d; - element.querySelector('#data_max_disk_24h').innerText = data.max_disk_24h; - element.querySelector('#data_min_disk_30d').innerText = data.min_disk_30d; - element.querySelector('#data_min_disk_7d').innerText = data.min_disk_7d; - element.querySelector('#data_min_disk_24h').innerText = data.min_disk_24h; - - // Insights - data = json.insights; - element = tab.querySelector('#data_insights'); - - element.querySelector('#data_low_tps_players').innerText = data.low_tps_players; - element.querySelector('#data_low_tps_entities').innerText = data.low_tps_entities; - element.querySelector('#data_low_tps_chunks').innerText = data.low_tps_chunks; - element.querySelector('#data_low_tps_cpu').innerText = data.low_tps_cpu; - element.querySelector('#data_low_tps_tps').innerText = data.low_tps_tps; -} - -async function loadOptimizedPerformanceGraph(json, error) { - if (json) { - const zones = { - tps: [{ - value: json.zones.tpsThresholdMed, - color: json.colors.low - }, { - value: json.zones.tpsThresholdHigh, - color: json.colors.med - }, { - value: 30, - color: json.colors.high - }], - disk: [{ - value: json.zones.diskThresholdMed, - color: json.colors.low - }, { - value: json.zones.diskThresholdHigh, - color: json.colors.med - }, { - value: Number.MAX_VALUE, - color: json.colors.high - }] - }; - const dataSeries = await mapToDataSeries(json.values); - const series = { - playersOnline: { - name: s.name.playersOnline, type: s.type.areaSpline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.playersOnline, color: json.colors.playersOnline, yAxis: 0 - }, - tps: { - name: s.name.tps, type: s.type.spline, color: json.colors.high, - zones: zones.tps, tooltip: s.tooltip.twoDecimals, data: dataSeries.tps, - yAxis: 1 - }, - cpu: { - name: s.name.cpu, type: s.type.spline, tooltip: s.tooltip.twoDecimals, - data: dataSeries.cpu, color: json.colors.cpu, yAxis: 2 - }, - cpu_alt: { - name: s.name.cpu, type: s.type.spline, tooltip: s.tooltip.twoDecimals, - data: dataSeries.cpu, color: json.colors.cpu, yAxis: 1 - }, - ram: { - name: s.name.ram, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.ram, color: json.colors.ram, yAxis: 3 - }, - ram_alt: { - name: s.name.ram, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.ram, color: json.colors.ram, yAxis: 2 - }, - entities: { - name: s.name.entities, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.entities, color: json.colors.entities, yAxis: 4 - }, - entities_alt: { - name: s.name.entities, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.entities, color: json.colors.entities, yAxis: 1 - }, - chunks: { - name: s.name.chunks, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.chunks, color: json.colors.chunks, yAxis: 5 - }, - chunks_alt: { - name: s.name.chunks, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: dataSeries.chunks, color: json.colors.chunks, yAxis: 2 - }, - disk: { - name: s.name.disk, type: s.type.spline, color: json.colors.high, - zones: zones.disk, tooltip: s.tooltip.zeroDecimals, data: dataSeries.disk - } - }; - setTimeout(() => playersChart('playersOnlineChart', series.playersOnline, 2), 10) - setTimeout(() => performanceChart('performanceGraph', series.playersOnline, series.tps, series.cpu, series.ram, series.entities, series.chunks), 20) - setTimeout(() => tpsChart('tpsGraph', series.tps, series.playersOnline), 30) - setTimeout(() => resourceChart('resourceGraph', series.cpu_alt, series.ram_alt, series.playersOnline), 40) - setTimeout(() => worldChart('worldGraph', series.entities_alt, series.chunks_alt, series.playersOnline), 50) - setTimeout(() => diskChart('diskGraph', [series.disk]), 60) - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('playersOnlineChart').innerText = errorMessage; - document.getElementById('performanceGraph').innerText = errorMessage; - document.getElementById('tpsGraph').innerText = errorMessage; - document.getElementById('resourceGraph').innerText = errorMessage; - document.getElementById('worldGraph').innerText = errorMessage; - document.getElementById('diskGraph').innerText = errorMessage; - } -} - -function loadPingGraph(json, error) { - if (json) { - const series = { - avgPing: { - name: s.name.avgPing, - type: s.type.spline, - tooltip: s.tooltip.twoDecimals, - data: json.avg_ping_series, - color: json.colors.avg - }, - maxPing: { - name: s.name.maxPing, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: json.max_ping_series, - color: json.colors.max - }, - minPing: { - name: s.name.minPing, - type: s.type.spline, - tooltip: s.tooltip.zeroDecimals, - data: json.min_ping_series, - color: json.colors.min - } - }; - lineChart('pingGraph', [series.avgPing, series.maxPing, series.minPing]); - } else if (error) { - document.getElementById('pingGraph').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadWorldPie(json, error) { - if (json) { - const worldSeries = {name: 'World Playtime', colorByPoint: true, data: json.world_series}; - const gmSeries = json.gm_series; - worldPie("worldPie", worldSeries, gmSeries); - } else if (error) { - document.getElementById('worldPie').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadActivityGraph(json, error) { - if (json) { - activityPie('activityPie', { - name: s.name.unit_players, colorByPoint: true, data: json.activity_pie_series - }); - stackChart('activityStackGraph', json.activity_labels, json.activity_series, s.name.unit_players); - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('activityPie').innerText = errorMessage; - document.getElementById('activityStackGraph').innerText = errorMessage; - } -} - -function loadGeolocationGraph(json, error) { - if (json) { - const geolocationSeries = { - name: s.name.unit_players, - type: 'map', - mapData: Highcharts.maps['custom/world'], - data: json.geolocation_series, - joinBy: ['iso-a3', 'code'] - }; - const geolocationBarSeries = { - color: json.colors.bars, - name: s.name.unit_players, - data: json.geolocation_bar_series.map(function (bar) { - return bar.value - }) - }; - const geolocationBarCategories = json.geolocation_bar_series.map(function (bar) { - return bar.label - }); - worldMap('worldMap', json.colors.low, json.colors.high, geolocationSeries); - horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], s.name.unit_players); - if (!json.geolocations_enabled) { - document.getElementById('geolocation-warning').classList.remove('hidden'); - } - } else if (error) { - const errorMessage = `Failed to load graph data: ${error}`; - document.getElementById('worldMap').innerText = errorMessage; - document.getElementById('countryBarChart').innerText = errorMessage; - } -} - -function loadUniqueAndNewGraph(json, error) { - if (json) { - const uniquePlayers = { - name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.uniquePlayers, color: json.colors.playersOnline - }; - const newPlayers = { - name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.newPlayers, color: json.colors.newPlayers - }; - dayByDay('uniqueChart', [uniquePlayers, newPlayers]); - } else if (error) { - document.getElementById('uniqueChart').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadHourlyUniqueAndNewGraph(json, error) { - if (json) { - const uniquePlayers = { - name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.uniquePlayers, color: json.colors.playersOnline - }; - const newPlayers = { - name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals, - data: json.newPlayers, color: json.colors.newPlayers - }; - dayByDay('hourlyUniqueChart', [uniquePlayers, newPlayers]); - } else if (error) { - document.getElementById('uniqueChart').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadserverCalendar(json, error) { - if (json) { - document.getElementById('calendar').innerText = ''; - if (window.calendars.online_activity) window.calendars.online_activity.destroy(); - onlineActivityCalendar('#calendar', json.data, json.firstDay); - document.getElementById('online-calendar-tab').addEventListener('click', function () { - // 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 () { - window.calendars.online_activity.render(); - }, 0); - }); - } else if (error) { - document.getElementById('calendar').innerText = `Failed to load calendar data: ${error}`; - } -} - -function loadPunchCard(json, error) { - if (json) { - const punchCardSeries = { - name: 'Relative Join Activity', - color: json.color, - data: json.punchCard - }; - punchCard('punchCard', punchCardSeries); - } else if (error) { - document.getElementById('punchCard').innerText = `Failed to load graph data: ${error}`; - } -} - -function loadJoinAddressPie(json, error) { - if (json) { - const joinAddressPieSeries = { - name: 'Used IP Addresses', - colorByPoint: true, - colors: json.colors, - data: json.slices - }; - joinAddressPie('joinAddressPie', joinAddressPieSeries); - } else if (error) { - document.getElementById('joinAddressPie').innerText = `Failed to load graph data: ${error}`; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js b/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js deleted file mode 100644 index 1bedea23c..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js +++ /dev/null @@ -1,130 +0,0 @@ -function loadSessionAccordion(json, error) { - const sessionTable = document.querySelector('#sessions-overview #tableAccordion tbody'); - - if (error) { - sessionTable.innerHTML = `Error: ${error}---`; - return; - } - - const sessions = json.sessions; - - if (!sessions.length) { - sessionTable.innerHTML = `No Data---`; - return; - } - - // sessions_per_page can be undefined (-> NaN) or higher than amount of sessions. - let limit = json.sessions_per_page ? json.sessions_per_page : sessions.length; - limit = Math.min(limit, sessions.length); - - let sessionsHtml = ''; - for (let i = 0; i < limit; i++) { - const session = sessions[i]; - const title = createAccordionTitle(i, session); - const body = createAccordionBody(i, session); - sessionsHtml += title + body; - } - sessionTable.innerHTML = sessionsHtml; - - for (let i = 0; i < limit; i++) { - document.getElementById(`session_h_${i}`).addEventListener('click', onOpenSession(i, sessions)); - } -} - -function onOpenSession(i, sessions) { - let opened = false; - return function () { - if (opened) { - return; - } - setTimeout(function () { - const session = sessions[i]; - const worldSeries = {name: `World Playtime`, colorByPoint: true, data: session.world_series}; - const gmSeries = session.gm_series; - - worldPie(`worldpie_${i}`, worldSeries, gmSeries); - }, 250); - opened = true; - } -} - -function loadPlayerKills(json, error) { - if (error) { - $('#playerKillTable').replaceWith(`

    Failed to load player kills: ${error}

    `); - return; - } - $('#playerKillTable').replaceWith(createKillsTable(json.player_kills)); -} - -function loadPlayerdeaths(json, error) { - if (error) { - $('#playerDeathTable').replaceWith(`

    Failed to load player deaths: ${error}

    `); - return; - } - $('#playerDeathTable').replaceWith(createKillsTable(json.player_deaths)); -} - -function createAccordionTitle(i, session) { - let style = session.start.includes("Online") ? 'bg-teal' : 'bg-teal-outline'; - return ` - ${session.name}${session.first_session ? ` ` : ``} - ${session.start}${session.length} - ${session.network_server ? session.network_server : session.most_used_world} - ` -} - -function createAccordionBody(i, session) { - return ` - -
    -
    -

    Ended${session.end}

    -

    Length${session.length}

    -

    AFK Time${session.afk_time}

    -

    Server${session.server_name}

    - ${session.avg_ping ? `

    Average Ping` + session.avg_ping + `

    ` : ``} -
    -

    Player Kills${session.player_kills.length}

    -

    Mob Kills${session.mob_kills}

    -

    Deaths${session.deaths}

    -
    - ${createKillsTable(session.player_kills)} -
    -
    -
    - - Player Page - - ${session.network_server ? ` - Server Analysis - ` : ``} -
    -
    - - `; -} - -function createKillsTable(player_kills) { - let table = ''; - - if (!player_kills.length) { - table += `` - } - - for (const kill of player_kills) { - table += ` - - - - - ` - } - - table += '
    None---
    ${kill.date}${kill.killerName} ${ - kill.killerUUID === kill.victimUUID - ? '' - : '' - } ${kill.victimName}${kill.weapon}${kill.serverName}
    '; - return table; -} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js b/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js deleted file mode 100644 index 611a50463..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js +++ /dev/null @@ -1,136 +0,0 @@ -// Stored by tab {'tab-id': ['address', 'address']} -const currentlyRefreshing = {}; -let refreshBarrierMs = 0; - -function refreshingJsonRequest(address, callback, tabID, skipOldData) { - const timestamp = Date.now(); - const addressWithTimestamp = address.includes('?') - ? `${address}×tamp=${timestamp}` - : `${address}?timestamp=${timestamp}` - - const refreshElement = document.querySelector(`#${tabID} .refresh-element`); - refreshElement.querySelector('i').addEventListener('click', () => { - if (currentlyRefreshing[tabID].includes(address)) { - return; - } - refreshElement.querySelector('.refresh-notice').innerHTML = ' Updating..'; - refreshingJsonRequest(address, callback, tabID, true); - }); - - let timeout = 1000; - - if (!currentlyRefreshing[tabID]) currentlyRefreshing[tabID] = []; - currentlyRefreshing[tabID].push(address); - - function makeTheRequest(skipOldData) { - jsonRequest(addressWithTimestamp, (json, error) => { - if (error) { - currentlyRefreshing[tabID].splice(currentlyRefreshing[tabID].indexOf(address), 1); - if (error.status === 400 && error.error.includes('Attempt to get data from the future!')) { - console.error(error.error); // System time not in sync with UTC - refreshElement.innerHTML = "System times out of sync with UTC"; - return jsonRequest(address, callback); - } - refreshElement.querySelector('.refresh-notice').innerHTML = ""; - return callback(json, error); - } - - refreshElement.querySelector('.refresh-time').innerText = json.timestamp_f; - - const lastUpdated = json.timestamp; - if (lastUpdated + refreshBarrierMs < timestamp) { - setTimeout(() => makeTheRequest(true), timeout); - timeout = timeout >= 12000 ? timeout : timeout * 2; - if (!skipOldData) callback(json, error); - } else { - currentlyRefreshing[tabID].splice(currentlyRefreshing[tabID].indexOf(address), 1); - if (!currentlyRefreshing[tabID].length) { - refreshElement.querySelector('.refresh-notice').innerHTML = ""; - } - callback(json, error); - } - }) - } - - makeTheRequest(skipOldData); -} - -/** - * Make a GET XMLHttpRequest for JSON data. - * @param address Address to request from - * @param callback function with (json, error) parameters to call after the request. - */ -function jsonRequest(address, callback) { - setTimeout(function () { - const xhr = newConfiguredXHR(callback); - - xhr.open("GET", address, true); - xhr.send(); - }, 0); -} - -/** - * Make a POST XMLHttpRequest for JSON data. - * @param address Address to request from - * @param postBody POST body (form). - * @param callback function with (json, error) parameters to call after the request. - */ -function jsonPostRequest(address, postBody, callback) { - setTimeout(function () { - const xhr = newConfiguredXHR(callback, address); - - xhr.open("POST", address, true); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - xhr.send(postBody); - }, 0); -} - -/** - * Create new XMLHttpRequest configured for methods such as jsonRequest - * @param callback function with (json, error) parameters to call after the request. - */ -function newConfiguredXHR(callback, address) { - const xhr = new XMLHttpRequest(); - - xhr.withCredentials = true; - xhr.onreadystatechange = function () { - if (this.readyState === 4) { - try { - if (this.status === 200 || (this.status === 0 && this.responseText)) { - var json = JSON.parse(this.responseText); - setTimeout(function () { - callback(json, null) - }, 0); - } else if (this.status === 404 || this.status === 403 || this.status === 500) { - callback(null, "HTTP " + this.status + " (See " + address + ")") - } else if (this.status === 400) { - const json = JSON.parse(this.responseText); - callback(json, json.error) - } else if (this.status === 0) { - callback(null, "Request did not reach the server. (Server offline / Adblocker?)") - } - } catch (e) { - callback(null, e.message) - } - } - }; - xhr.timeout = 45000; - xhr.ontimeout = function () { - callback(null, "Timed out after 45 seconds.") - }; - - return xhr; -} - -function awaitUntil(predicateFunction) { - return new Promise((resolve => { - const handlerFunction = () => { - if (predicateFunction.apply()) { - resolve(); - } else { - setTimeout(handlerFunction, 10) - } - }; - handlerFunction(); - })) -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/login.html b/Plan/common/src/main/resources/assets/plan/web/login.html deleted file mode 100644 index d6f5bbed7..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/login.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - - - Plan | Login - - - - - - - - - - - - - - - - -
    - - -
    - logo -
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - diff --git a/Plan/common/src/main/resources/assets/plan/web/network.html b/Plan/common/src/main/resources/assets/plan/web/network.html deleted file mode 100644 index c1db61f04..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/network.html +++ /dev/null @@ -1,1167 +0,0 @@ - - - - - - - - - - - - Plan | Network - - - - - - - - - - - - - -
    -
    - -

    Please wait..

    -
    -
    - - -
    - - - - - - -
    - - - -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Network Overview - - - Updating.. - -

    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    Players
    -
    -
    -

    Last 24 hours

    -

    Unique Players

    -

    New Players

    -

    Last 7 days

    -

    Unique Players

    -

    New Players

    -

    Last 30 days

    -

    Unique Players

    -

    New Players

    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Network as Numbers
    -
    - -
    -

    Current Uptime

    -
    -

    Total Players

    -

    Regular Players

    -

    Players Online

    -
    -

    Last Peak: - Players

    -

    Best Peak: - Players

    -
    -

    Total Playtime

    -

    Average Playtime / Player

    -

    Average Session Length

    -

    Sessions

    -
    -
    -
    - - -
    -
    -
    -
    - Week Comparison
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Comparing 7 days - - - Trend
    Unique Players
    New Players
    Regular Players
    - Average Playtime / Player -
    Average Session Length
    Sessions
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Servers - - - Updating.. - -

    -
    - -
    -
    -
    -
    -
    -
    -
    -
      - Online Activity (30 days)
    -
    -
    -
    - -
    -
    -
      - as Numbers
    -
    -
    -

    Current Uptime

    -

    Last Peak: Players -

    -

    Best Peak: Players -

    -
    -

    Last 7 days

    -

    Unique Players

    -

    New Players

    -

    -

    Average TPS

    -

    Low TPS Spikes

    -

    Downtime

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Sessions - - - Updating.. - -

    -
    - -
    - -
    -
    -
    -
    - Recent Sessions - Click to expand
    -
    - - - - - - - - - - - -
    Player Session Started Length Server
    -
    -
    -
    - -
    -
    -
    - Server Playtime for 30 days
    -
    -
    -
    - -
    -
    -
    - Insights for 30 days
    -
    -
    -

    Playtime

    -

    AFK Time ()

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Performance - - - Updating.. - -

    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    This data is from only the proxy server (bungeecord or velocity)

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Performance as Numbers
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Last 30 daysLast 7 daysLast 24 hours
    - Low TPS Spikes -
    - Total Downtime (No Data) -
    - Average Downtime / Server -
    Average TPS
    Average CPU Usage
    Average RAM Usage
    Average Entities
    - Average Chunks - -
    -
    -
    - -
    -
    -
    -
    - Server selector
    -
    - - -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Playerbase Overview - - - Updating.. - -

    -
    - -
    - -
    -
    -
    -
    - Playerbase development
    -
    -
    -
    -
    - - -
    -
    -
    -
    - Current Playerbase
    -
    -
    -
    -
    -
    - -
    - - -
    -
    -
    -
    - Trends for 30 days
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - -
    -
    -
    -
    -
    - Insights for 30 days
    -
    -
    -

    New Regular

    -

    Regular Inactive

    -

    - Comparing 30d ago to Now -

    -
    -
    - - -
    -
    -
    - - Join Addresses -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${networkDisplayName} - · Geolocations - - - Updating.. - -

    -
    - -
    - -
    -
    -
    -
    - Geolocations
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Connection Information
    -
    -
    - - - - - - - - - - -
    Country Average Ping Best Ping Worst Ping
    -
    -
    -
    -
    -
    -
    - - ${tabsPlugins} -
    -
    - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/player.html b/Plan/common/src/main/resources/assets/plan/web/player.html deleted file mode 100644 index b5de10a37..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/player.html +++ /dev/null @@ -1,782 +0,0 @@ - - - - - - - - - - - - Plan | ${playerName} - - - - - - - - - - - - - -
    -
    - -

    Please wait..

    -
    -
    - - -
    - - - - - - -
    - - - -
    - -
    -
    - -
    -

    ${playerName} - · Player Overview

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - ${playerName}
    -
    -
    -
    -
    -

    -

    -

    Times Kicked:

    -
    -
    - -
    -
    -

    Player Kills

    -

    Mob Kills

    -

    Deaths

    -
    -
    -
    -
    -
    -

    Total Playtime

    -

    Total Active

    -

    Total AFK

    -
    -

    Sessions

    -

    Longest Session

    -

    Session Median

    -
    -

    - Registered -

    -
    -
    -

    Activity Index ()

    -

    Favorite Server

    -

    Join Address

    -
    -

    Average Ping

    -

    Best Ping

    -

    Worst Ping

    -
    -

    Last Seen

    -
    -
    -
    -
    -
    -
    -
    - Seen Nicknames
    -
    -
    - - - - - - - - - -
    Nickname Server Last Seen
    -
    -
    -
    -
    -
    - Connection information
    -
    -
    - - - - - - - - -
    Country Last Connected
    -
    -
    -
    - - -
    -
    -
    -
    - Punchcard
    -
    -
    - -
    -
    -
    -
    -
    - Online Activity
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Last 30 daysLast 7 days
    Playtime
    Active Playtime
    AFK Time
    Median Session Length -
    Sessions
    Player Kills
    Mob Kills
    Deaths
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${playerName} - · Sessions

    - ${backButton} -
    - -
    -
    - -
    -
    -
    - Session Calendar
    -
    -
    -
    - - -
    -
    -
    - Recent Sessions - Click to expand
    -
    -
    - - - - - - - - - - -
    Server Session Started Length Most played World
    -
    -
    -
    -
    - -
    -
    -
    - World Playtime
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${playerName} - · PvP & PvE

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - PvP & PvE as Numbers
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    All TimeLast 30 daysLast 7 days
    KDR
    Player Kills
    Player Caused Deaths
    Mob KDR
    Mob Kills
    Mob Caused Deaths
    Deaths
    -
    -
    - -
    -
    -
    -
    - Insights
    -
    -
    -

    Deadliest PvP Weapon -

    -

    2nd PvP Weapon -

    -

    3rd PvP Weapon -

    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Recent PvP Kills
    -
    -
    -
    -
    - -
    -
    -
    -
    - Recent PvP Deaths
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${playerName} - · Servers Overview

    - ${backButton} -
    - -
    -
    - -
    -
    -
    - Servers - Click to expand
    -
    -
    - - - - - - - - - - -
    Server Playtime Registered Last Seen
    -
    -
    -
    -
    - -
    -
    -
    - Server Playtime
    -
    -
    -
    -
    -
    -
    -
    - ${pluginsTabs} -
    -
    - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/players.html b/Plan/common/src/main/resources/assets/plan/web/players.html deleted file mode 100644 index 539a4095a..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/players.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - - - - - Plan | Players - - - - - - - - - - - - - - - -
    - - - - - - -
    - - - -
    - -
    -
    - -
    -

    ${networkName} - | Players - - - Updating.. - -

    -
    - -
    - -
    -
    -
    -
    - Player List
    -
    - - - - -
    Loading..
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/query.html b/Plan/common/src/main/resources/assets/plan/web/query.html deleted file mode 100644 index 266aefe31..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/query.html +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - - - - Plan | Query - - - - - - - - - - - - - - - -
    - - - - - - -
    - - - -
    -
    -
    - -
    -

    Plan · - Query

    -
    - -
    -
    -
    -
    - - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    - -
    - -
    -
    -
    - -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/register.html b/Plan/common/src/main/resources/assets/plan/web/register.html deleted file mode 100644 index 63f7a5a39..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/register.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - Plan | Register - - - - - - - - - - - - - - -
    - - -
    - logo -
    -
    -
    -
    -
    - -
    -
    -
    -
    -

    Create a new user

    -
    - -
    -
    - -
    - Username can be up to 50 characters. -
    -
    -
    - -
    - Password should be more than 8 characters, but there are no limitations. -
    -
    - - Register - -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - diff --git a/Plan/common/src/main/resources/assets/plan/web/robots.txt b/Plan/common/src/main/resources/assets/plan/web/robots.txt deleted file mode 100644 index 1f53798bb..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/Plan/common/src/main/resources/assets/plan/web/server.html b/Plan/common/src/main/resources/assets/plan/web/server.html deleted file mode 100644 index df2edffc0..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/server.html +++ /dev/null @@ -1,1478 +0,0 @@ - - - - - - - - - - - Plan | Server Analysis - - - - - - - - - - - - - -
    -
    - -

    Please wait..

    -
    -
    - - -
    - - - - - - -
    - - - -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Server Overview - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - Online Activity
    -
    -
    -
    -
    - - -
    -
    -
    -
    Last 7 days
    -
    -
    -

    Unique Players

    -

    Unique Players / Day

    -

    New Players

    -

    New Player Retention -  

    -
    -

    Average TPS

    -

    Low TPS Spikes

    -

    Downtime

    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Server as Numbers
    -
    -
    -

    Current Uptime

    -
    -

    Total Players

    -

    Regular Players

    -

    Players Online

    -
    -

    Last Peak: - Players

    -

    Best Peak: - Players

    -
    -

    Total Playtime

    -

    Average Playtime / Player

    -

    Sessions

    -
    -

    Player Kills

    -

    Mob Kills

    -

    Deaths

    -
    -
    -
    - - -
    -
    -
    -
    - Week Comparison
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Comparing 7 days - - - Trend
    Unique Players
    New Players
    Regular Players
    - Average Playtime / Player -
    Sessions
    Player Kills
    Mob Kills
    Deaths
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Online Activity Overview - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Online Activity as Numbers
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Comparing 15 days - Last 30 daysLast 7 daysLast 24 hours
    Unique Players
    Unique Players / Day
    New Players
    - New Players / Day -
    - New Player Retention -
    Playtime
    - Average Playtime / Day -
    Average Session Length -
    Sessions
    -
    -
    - -
    -
    -
    -
    - Insights for 30 days
    -
    -
    -

    - Players online on first join -

    -

    - Average First session length -

    -

    - Median First session length -

    -

    Lone joins -

    -

    Lone newbie joins -

    -

    - Comparing 15 days -

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Sessions - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - Recent Sessions - Click to expand
    -
    -
    - - - - - - - - - - - -
    Player Session Started Length Most played World
    -
    -
    -
    -
    - -
    -
    -
    - World Playtime
    -
    -
    - -
    -
    - -
    -
    -
    - Insights for 30 days
    -
    -
    -

    Most Active Gamemode - ()

    -

    Server occupied ()

    -

    Playtime

    -

    AFK Time ()

    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · PvP & PvE - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - PvP & PvE as Numbers
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    All TimeLast 30 daysLast 7 days
    Average KDR / Player -
    Player Kills
    Average Mob KDR
    Mob Kills
    Mob Caused Deaths
    Deaths
    -
    -
    - -
    -
    -
    -
    - Insights for 30 days
    -
    -
    -

    Deadliest PvP Weapon -

    -

    2nd PvP Weapon -

    -

    3rd PvP Weapon -

    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Recent Kills
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Playerbase Overview - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - Playerbase development
    -
    -
    -
    -
    - - -
    -
    -
    -
    - Current Playerbase
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Trends for 30 days
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    - Insights for 30 days
    -
    -
    -

    New Regular

    -

    Regular Inactive

    -

    - Comparing 30d ago to Now -

    -
    -
    - - -
    -
    -
    - - Join Addresses -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Player List - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - Player List
    -
    - - - - -
    Loading..
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Geolocations - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    -
    -
    - Geolocations
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Connection Information
    -
    -
    - - - - - - - - - - -
    Country Average Ping Best Ping Worst Ping
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -

    ${serverDisplayName} - · Performance - - - Updating.. - -

    - ${backButton} -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - Performance as Numbers
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Last 30 daysLast 7 daysLast 24 hours
    - Low TPS Spikes -
    - Server Downtime (No Data) -
    Average Players
    Average TPS
    Average CPU Usage
    Average RAM Usage
    Average Entities
    - Average Chunks - -
    Max Free Disk
    Min Free Disk
    -
    -
    - -
    -
    -
    -
    - Insights for 30 days
    -
    -
    -

    During Low TPS Spikes:

    -

    Average Players

    -

    Average Entities

    -

    Average Chunks

    -

    Average CPU

    -

    Average TPS

    -
    -
    -
    -
    -
    -
    - - ${tabsPlugins} -
    -
    - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.css b/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.css deleted file mode 100644 index 064afa191..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.css +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This combined file was created by the DataTables downloader builder: - * https://datatables.net/download - * - * To rebuild or modify this file with the latest versions of the included - * software please visit: - * https://datatables.net/download/#bs5/dt-1.10.25/r-2.2.9 - * - * Included libraries: - * DataTables 1.10.25, Responsive 2.2.9 - */ - -/*! Bootstrap 5 integration for DataTables - * - * ©2020 SpryMedia Ltd, all rights reserved. - * License: MIT datatables.net/license/mit - */table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:.85em}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable>thead>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}table.dataTable>thead>tr>th:not(.sorting_disabled),table.dataTable>thead>tr>td:not(.sorting_disabled){padding-right:30px}table.dataTable>thead .sorting,table.dataTable>thead .sorting_asc,table.dataTable>thead .sorting_desc,table.dataTable>thead .sorting_asc_disabled,table.dataTable>thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{position:absolute;bottom:.5em;display:block;opacity:.3}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:before{right:1em;content:"↑"}table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:after{right:.5em;content:"↓"}table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:after{opacity:1}table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{opacity:0}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:before,div.dataTables_scrollBody table thead .sorting_asc:before,div.dataTables_scrollBody table thead .sorting_desc:before,div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:center !important}}table.dataTable.table-sm>thead>tr>th:not(.sorting_disabled){padding-right:20px}table.dataTable.table-sm .sorting:before,table.dataTable.table-sm .sorting_asc:before,table.dataTable.table-sm .sorting_desc:before{top:5px;right:.85em}table.dataTable.table-sm .sorting:after,table.dataTable.table-sm .sorting_asc:after,table.dataTable.table-sm .sorting_desc:after{top:5px}table.table-bordered.dataTable{border-right-width:0}table.table-bordered.dataTable thead tr:first-child th,table.table-bordered.dataTable thead tr:first-child td{border-top-width:1px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:first-child,table.table-bordered.dataTable th:first-child,table.table-bordered.dataTable td:first-child,table.table-bordered.dataTable td:first-child{border-left-width:1px}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:1px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-bottom-width:1px}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:last-child{padding-right:0} - - -table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty{cursor:default !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before{display:none !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control{position:relative;padding-left:30px;cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before{top:50%;left:5px;height:1em;width:1em;margin-top:-9px;display:block;position:absolute;color:white;border:.15em solid white;border-radius:1em;box-shadow:0 0 .2em #444;box-sizing:content-box;text-align:center;text-indent:0 !important;font-family:"Courier New",Courier,monospace;line-height:1em;content:"+";background-color:#0d6efd}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before{content:"-";background-color:#d33333}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td.dtr-control,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th.dtr-control{padding-left:27px}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td.dtr-control:before,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th.dtr-control:before{left:4px;height:14px;width:14px;border-radius:14px;line-height:14px;text-indent:3px}table.dataTable.dtr-column>tbody>tr>td.dtr-control,table.dataTable.dtr-column>tbody>tr>th.dtr-control,table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>th.control{position:relative;cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.dtr-control:before,table.dataTable.dtr-column>tbody>tr>th.dtr-control:before,table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>th.control:before{top:50%;left:50%;height:.8em;width:.8em;margin-top:-0.5em;margin-left:-0.5em;display:block;position:absolute;color:white;border:.15em solid white;border-radius:1em;box-shadow:0 0 .2em #444;box-sizing:content-box;text-align:center;text-indent:0 !important;font-family:"Courier New",Courier,monospace;line-height:1em;content:"+";background-color:#0d6efd}table.dataTable.dtr-column>tbody>tr.parent td.dtr-control:before,table.dataTable.dtr-column>tbody>tr.parent th.dtr-control:before,table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before{content:"-";background-color:#d33333}table.dataTable>tbody>tr.child{padding:.5em 1em}table.dataTable>tbody>tr.child:hover{background:transparent !important}table.dataTable>tbody>tr.child ul.dtr-details{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul.dtr-details>li{border-bottom:1px solid #efefef;padding:.5em 0}table.dataTable>tbody>tr.child ul.dtr-details>li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul.dtr-details>li:last-child{border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:bold}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:50%;overflow:auto;margin:auto;z-index:102;overflow:auto;background-color:#f5f5f7;border:1px solid black;border-radius:.5em;box-shadow:0 12px 30px rgba(0, 0, 0, 0.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:1em}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;border:1px solid #eaeaea;background-color:#f9f9f9;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-close:hover{background-color:#eaeaea}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0, 0, 0, 0.6)}@media screen and (max-width: 767px){div.dtr-modal div.dtr-modal-display{width:95%}}div.dtr-bs-modal table.table tr:first-child td{border-top:none} - - diff --git a/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.js b/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.js deleted file mode 100644 index 602235f1f..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/vendor/datatables/datatables.min.js +++ /dev/null @@ -1,278 +0,0 @@ -/* - * This combined file was created by the DataTables downloader builder: - * https://datatables.net/download - * - * To rebuild or modify this file with the latest versions of the included - * software please visit: - * https://datatables.net/download/#bs5/dt-1.10.25/r-2.2.9 - * - * Included libraries: - * DataTables 1.10.25, Responsive 2.2.9 - */ - -/*! - Copyright 2008-2021 SpryMedia Ltd. - - This source file is free software, available under the following license: - MIT license - http://datatables.net/license - - This source file 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 license files for details. - - For details please refer to: http://www.datatables.net - DataTables 1.10.25 - ©2008-2021 SpryMedia Ltd - datatables.net/license -*/ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(k,y,z){k instanceof String&&(k=String(k));for(var q=k.length,G=0;G").css({position:"fixed",top:0,left:-1*k(y).scrollLeft(),height:1, -width:1,overflow:"hidden"}).append(k("
    ").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(k("
    ").css({width:"100%",height:10}))).appendTo("body"),d=c.children(),e=d.children();b.barWidth=d[0].offsetWidth-d[0].clientWidth;b.bScrollOversize=100===e[0].offsetWidth&&100!==d[0].clientWidth;b.bScrollbarLeft=1!==Math.round(e.offset().left);b.bBounding=c[0].getBoundingClientRect().width?!0:!1;c.remove()}k.extend(a.oBrowser,u.__browser);a.oScroll.iBarWidth=u.__browser.barWidth} -function Cb(a,b,c,d,e,f){var g=!1;if(c!==q){var h=c;g=!0}for(;d!==e;)a.hasOwnProperty(d)&&(h=g?b(h,a[d],d,a):a[d],g=!0,d+=f);return h}function Xa(a,b){var c=u.defaults.column,d=a.aoColumns.length;c=k.extend({},u.models.oColumn,c,{nTh:b?b:z.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=k.extend({},u.models.oSearch,c[d]);Ea(a,d,k(b).data())}function Ea(a,b,c){b=a.aoColumns[b]; -var d=a.oClasses,e=k(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==q&&null!==c&&(Ab(c),O(u.defaults.column,c,!0),c.mDataProp===q||c.mData||(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),c.sClass&&e.addClass(c.sClass),k.extend(b,c),V(b,c,"sWidth","sWidthOrig"),c.iDataSort!==q&&(b.aDataSort=[c.iDataSort]),V(b,c,"aDataSort"));var g=b.mData,h=ia(g), -l=b.mRender?ia(b.mRender):null;c=function(n){return"string"===typeof n&&-1!==n.indexOf("@")};b._bAttrSrc=k.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b._setter=null;b.fnGetData=function(n,m,p){var t=h(n,m,q,p);return l&&m?l(t,m,n,p):t};b.fnSetData=function(n,m,p){return da(g)(n,m,p)};"number"!==typeof g&&(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));a=-1!==k.inArray("asc",b.asSorting);c=-1!==k.inArray("desc",b.asSorting);b.bSortable&&(a||c)?a&&!c? -(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI):(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI="")}function ra(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ya(a);for(var c=0,d=b.length;cn[m])d(h.length+n[m],l);else if("string"===typeof n[m]){var p=0;for(g=h.length;pb&&a[e]--; -1!=d&&c===q&&a.splice(d,1)}function va(a,b,c,d){var e=a.aoData[b],f,g=function(l,n){for(;l.childNodes.length;)l.removeChild(l.firstChild);l.innerHTML=S(a,b,n,"display")};if("dom"!==c&&(c&&"auto"!==c||"dom"!==e.src)){var h=e.anCells;if(h)if(d!==q)g(h[d],d);else for(c=0,f=h.length;c").appendTo(d));var l=0;for(b=h.length;l=a.fnRecordsDisplay()?0:h,a.iInitDisplayStart=-1);h=a._iDisplayStart;var m=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,U(a,!1);else if(!l)a.iDraw++;else if(!a.bDestroying&&!b){Gb(a);return}if(0!==n.length)for(b=l?a.aoData.length:m,g=l?0:h;g",{"class":f?e[0]:""}).append(k("",{valign:"top",colSpan:na(a),"class":a.oClasses.sRowEmpty}).html(d))[0];H(a,"aoHeaderCallback","header",[k(a.nTHead).children("tr")[0], -cb(a),h,m,n]);H(a,"aoFooterCallback","footer",[k(a.nTFoot).children("tr")[0],cb(a),h,m,n]);e=k(a.nTBody);e.children().detach();e.append(k(c));H(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function ja(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&Hb(a);d?ya(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;fa(a);a._drawHold=!1}function Ib(a){var b=a.oClasses,c=k(a.nTable);c=k("
    ").insertBefore(c);var d=a.oFeatures, -e=k("
    ",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,h,l,n,m,p,t=0;t")[0];n=f[t+1];if("'"==n||'"'==n){m="";for(p=2;f[t+p]!=n;)m+=f[t+p],p++;"H"==m?m=b.sJUIHeader:"F"==m&&(m=b.sJUIFooter);-1!=m.indexOf(".")?(n=m.split("."),l.id=n[0].substr(1,n[0].length-1),l.className=n[1]):"#"==m.charAt(0)?l.id=m.substr(1, -m.length-1):l.className=m;t+=p}e.append(l);e=k(l)}else if(">"==h)e=e.parent();else if("l"==h&&d.bPaginate&&d.bLengthChange)g=Jb(a);else if("f"==h&&d.bFilter)g=Kb(a);else if("r"==h&&d.bProcessing)g=Lb(a);else if("t"==h)g=Mb(a);else if("i"==h&&d.bInfo)g=Nb(a);else if("p"==h&&d.bPaginate)g=Ob(a);else if(0!==u.ext.feature.length)for(l=u.ext.feature,p=0,n=l.length;p',h=d.sSearch;h=h.match(/_INPUT_/)?h.replace("_INPUT_",g):h+g;b=k("
    ",{id:f.f?null:c+"_filter","class":b.sFilter}).append(k("
    ").addClass(b.sLength);a.aanFeatures.l||(l[0].id=c+"_length");l.children().append(a.oLanguage.sLengthMenu.replace("_MENU_", -e[0].outerHTML));k("select",l).val(a._iDisplayLength).on("change.DT",function(n){jb(a,k(this).val());fa(a)});k(a.nTable).on("length.dt.DT",function(n,m,p){a===m&&k("select",l).val(p)});return l[0]}function Ob(a){var b=a.sPaginationType,c=u.ext.pager[b],d="function"===typeof c,e=function(g){fa(g)};b=k("
    ").addClass(a.oClasses.sPaging+b)[0];var f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(g){if(d){var h=g._iDisplayStart,l=g._iDisplayLength, -n=g.fnRecordsDisplay(),m=-1===l;h=m?0:Math.ceil(h/l);l=m?1:Math.ceil(n/l);n=c(h,l);var p;m=0;for(p=f.p.length;mf&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==b?d+e",{id:a.aanFeatures.r?null:a.sTableId+"_processing","class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function U(a,b){a.oFeatures.bProcessing&&k(a.aanFeatures.r).css("display",b?"block":"none");H(a,null,"processing",[a,b])}function Mb(a){var b=k(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX, -e=c.sY,f=a.oClasses,g=b.children("caption"),h=g.length?g[0]._captionSide:null,l=k(b[0].cloneNode(!1)),n=k(b[0].cloneNode(!1)),m=b.children("tfoot");m.length||(m=null);l=k("
    ",{"class":f.sScrollWrapper}).append(k("
    ",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?d?K(d):null:"100%"}).append(k("
    ",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(l.removeAttr("id").css("margin-left",0).append("top"===h? -g:null).append(b.children("thead"))))).append(k("
    ",{"class":f.sScrollBody}).css({position:"relative",overflow:"auto",width:d?K(d):null}).append(b));m&&l.append(k("
    ",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?d?K(d):null:"100%"}).append(k("
    ",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",0).append("bottom"===h?g:null).append(b.children("tfoot")))));b=l.children();var p=b[0];f=b[1];var t=m?b[2]:null;if(d)k(f).on("scroll.DT",function(v){v= -this.scrollLeft;p.scrollLeft=v;m&&(t.scrollLeft=v)});k(f).css("max-height",e);c.bCollapse||k(f).css("height",e);a.nScrollHead=p;a.nScrollBody=f;a.nScrollFoot=t;a.aoDrawCallback.push({fn:Fa,sName:"scrolling"});return l[0]}function Fa(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY;b=b.iBarWidth;var f=k(a.nScrollHead),g=f[0].style,h=f.children("div"),l=h[0].style,n=h.children("table");h=a.nScrollBody;var m=k(h),p=h.style,t=k(a.nScrollFoot).children("div"),v=t.children("table"),x=k(a.nTHead),r=k(a.nTable), -A=r[0],D=A.style,I=a.nTFoot?k(a.nTFoot):null,W=a.oBrowser,M=W.bScrollOversize,B=T(a.aoColumns,"nTh"),E=[],aa=[],X=[],Aa=[],mb,Ba=function(F){F=F.style;F.paddingTop="0";F.paddingBottom="0";F.borderTopWidth="0";F.borderBottomWidth="0";F.height=0};var ha=h.scrollHeight>h.clientHeight;if(a.scrollBarVis!==ha&&a.scrollBarVis!==q)a.scrollBarVis=ha,ra(a);else{a.scrollBarVis=ha;r.children("thead, tfoot").remove();if(I){var ka=I.clone().prependTo(r);var la=I.find("tr");ka=ka.find("tr")}var nb=x.clone().prependTo(r); -x=x.find("tr");ha=nb.find("tr");nb.find("th, td").removeAttr("tabindex");c||(p.width="100%",f[0].style.width="100%");k.each(La(a,nb),function(F,Y){mb=sa(a,F);Y.style.width=a.aoColumns[mb].sWidth});I&&ba(function(F){F.style.width=""},ka);f=r.outerWidth();""===c?(D.width="100%",M&&(r.find("tbody").height()>h.offsetHeight||"scroll"==m.css("overflow-y"))&&(D.width=K(r.outerWidth()-b)),f=r.outerWidth()):""!==d&&(D.width=K(d),f=r.outerWidth());ba(Ba,ha);ba(function(F){X.push(F.innerHTML);E.push(K(k(F).css("width")))}, -ha);ba(function(F,Y){-1!==k.inArray(F,B)&&(F.style.width=E[Y])},x);k(ha).height(0);I&&(ba(Ba,ka),ba(function(F){Aa.push(F.innerHTML);aa.push(K(k(F).css("width")))},ka),ba(function(F,Y){F.style.width=aa[Y]},la),k(ka).height(0));ba(function(F,Y){F.innerHTML='
    '+X[Y]+"
    ";F.childNodes[0].style.height="0";F.childNodes[0].style.overflow="hidden";F.style.width=E[Y]},ha);I&&ba(function(F,Y){F.innerHTML='
    '+Aa[Y]+"
    ";F.childNodes[0].style.height= -"0";F.childNodes[0].style.overflow="hidden";F.style.width=aa[Y]},ka);r.outerWidth()h.offsetHeight||"scroll"==m.css("overflow-y")?f+b:f,M&&(h.scrollHeight>h.offsetHeight||"scroll"==m.css("overflow-y"))&&(D.width=K(la-b)),""!==c&&""===d||ca(a,1,"Possible column misalignment",6)):la="100%";p.width=K(la);g.width=K(la);I&&(a.nScrollFoot.style.width=K(la));!e&&M&&(p.height=K(A.offsetHeight+b));c=r.outerWidth();n[0].style.width=K(c);l.width=K(c);d=r.height()>h.clientHeight||"scroll"== -m.css("overflow-y");e="padding"+(W.bScrollbarLeft?"Left":"Right");l[e]=d?b+"px":"0px";I&&(v[0].style.width=K(c),t[0].style.width=K(c),t[0].style[e]=d?b+"px":"0px");r.children("colgroup").insertBefore(r.children("thead"));m.trigger("scroll");!a.bSorted&&!a.bFiltered||a._drawHold||(h.scrollTop=0)}}function ba(a,b,c){for(var d=0,e=0,f=b.length,g,h;e").appendTo(h.find("tbody"));h.find("thead, tfoot").remove();h.append(k(a.nTHead).clone()).append(k(a.nTFoot).clone());h.find("tfoot th, tfoot td").css("width","");n=La(a,h.find("thead")[0]);for(v=0;v").css({width:r.sWidthOrig,margin:0,padding:0,border:0,height:1}));if(a.aoData.length)for(v=0;v").css(f||e?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(h).appendTo(p);f&&g?h.width(g):f?(h.css("width","auto"),h.removeAttr("width"),h.width()").css("width",K(a)).appendTo(b||z.body);b=a[0].offsetWidth;a.remove();return b}function $b(a,b){var c=ac(a,b);if(0>c)return null;var d=a.aoData[c];return d.nTr?d.anCells[b]:k("").html(S(a,c,b,"display"))[0]}function ac(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;fd&&(d=c.length,e=f);return e}function K(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function pa(a){var b=[],c=a.aoColumns;var d=a.aaSortingFixed;var e=k.isPlainObject(d);var f=[];var g=function(m){m.length&&!Array.isArray(m[0])?f.push(m):k.merge(f,m)};Array.isArray(d)&&g(d);e&&d.pre&&g(d.pre);g(a.aaSorting);e&&d.post&&g(d.post);for(a=0;aI?1:0;if(0!==D)return"asc"===A.dir?D:-D}D=c[m];I=c[p];return DI?1:0}):g.sort(function(m,p){var t,v=h.length,x=e[m]._aSortData,r=e[p]._aSortData;for(t=0;tI?1:0})}a.bSorted=!0}function cc(a){var b=a.aoColumns,c=pa(a);a=a.oLanguage.oAria;for(var d=0,e=b.length;d/g,"");var l=f.nTh;l.removeAttribute("aria-sort");f.bSortable&&(0e?e+1:3))}e=0;for(f=d.length;ee?e+1:3))}a.aLastSort=d}function bc(a,b){var c=a.aoColumns[b],d=u.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,ta(a,b)));for(var f,g=u.ext.type.order[c.sType+ -"-pre"],h=0,l=a.aoData.length;h= -f.length?[0,m[1]]:m)}));h.search!==q&&k.extend(a.oPreviousSearch,Wb(h.search));if(h.columns)for(d=0,e=h.columns.length;d=c&&(b=c-d);b-=b%d;if(-1===d||0>b)b=0;a._iDisplayStart=b}function fb(a,b){a=a.renderer;var c=u.ext.renderer[b];return k.isPlainObject(a)&&a[b]?c[a[b]]||c._:"string"===typeof a?c[a]||c._:c._}function P(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Ca(a,b){var c=ec.numbers_length,d=Math.floor(c/2); -b<=c?a=qa(0,b):a<=d?(a=qa(0,c-2),a.push("ellipsis"),a.push(b-1)):(a>=b-1-d?a=qa(b-(c-2),b):(a=qa(a-d+2,a+d-1),a.push("ellipsis"),a.push(b-1)),a.splice(0,0,"ellipsis"),a.splice(0,0,0));a.DT_el="span";return a}function Wa(a){k.each({num:function(b){return Ta(b,a)},"num-fmt":function(b){return Ta(b,a,rb)},"html-num":function(b){return Ta(b,a,Ua)},"html-num-fmt":function(b){return Ta(b,a,Ua,rb)}},function(b,c){L.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(L.type.search[b+a]=L.type.search.html)})}function fc(a){return function(){var b= -[Sa(this[u.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return u.ext.internal[a].apply(this,b)}}var u=function(a){this.$=function(f,g){return this.api(!0).$(f,g)};this._=function(f,g){return this.api(!0).rows(f,g).data()};this.api=function(f){return f?new C(Sa(this[L.iApiIndex])):new C(this)};this.fnAddData=function(f,g){var h=this.api(!0);f=Array.isArray(f)&&(Array.isArray(f[0])||k.isPlainObject(f[0]))?h.rows.add(f):h.row.add(f);(g===q||g)&&h.draw();return f.flatten().toArray()}; -this.fnAdjustColumnSizing=function(f){var g=this.api(!0).columns.adjust(),h=g.settings()[0],l=h.oScroll;f===q||f?g.draw(!1):(""!==l.sX||""!==l.sY)&&Fa(h)};this.fnClearTable=function(f){var g=this.api(!0).clear();(f===q||f)&&g.draw()};this.fnClose=function(f){this.api(!0).row(f).child.hide()};this.fnDeleteRow=function(f,g,h){var l=this.api(!0);f=l.rows(f);var n=f.settings()[0],m=n.aoData[f[0][0]];f.remove();g&&g.call(this,n,m);(h===q||h)&&l.draw();return m};this.fnDestroy=function(f){this.api(!0).destroy(f)}; -this.fnDraw=function(f){this.api(!0).draw(f)};this.fnFilter=function(f,g,h,l,n,m){n=this.api(!0);null===g||g===q?n.search(f,h,l,m):n.column(g).search(f,h,l,m);n.draw()};this.fnGetData=function(f,g){var h=this.api(!0);if(f!==q){var l=f.nodeName?f.nodeName.toLowerCase():"";return g!==q||"td"==l||"th"==l?h.cell(f,g).data():h.row(f).data()||null}return h.data().toArray()};this.fnGetNodes=function(f){var g=this.api(!0);return f!==q?g.row(f).node():g.rows().nodes().flatten().toArray()};this.fnGetPosition= -function(f){var g=this.api(!0),h=f.nodeName.toUpperCase();return"TR"==h?g.row(f).index():"TD"==h||"TH"==h?(f=g.cell(f).index(),[f.row,f.columnVisible,f.column]):null};this.fnIsOpen=function(f){return this.api(!0).row(f).child.isShown()};this.fnOpen=function(f,g,h){return this.api(!0).row(f).child(g,h).show().child()[0]};this.fnPageChange=function(f,g){f=this.api(!0).page(f);(g===q||g)&&f.draw(!1)};this.fnSetColumnVis=function(f,g,h){f=this.api(!0).column(f).visible(g);(h===q||h)&&f.columns.adjust().draw()}; -this.fnSettings=function(){return Sa(this[L.iApiIndex])};this.fnSort=function(f){this.api(!0).order(f).draw()};this.fnSortListener=function(f,g,h){this.api(!0).order.listener(f,g,h)};this.fnUpdate=function(f,g,h,l,n){var m=this.api(!0);h===q||null===h?m.row(g).data(f):m.cell(g,h).data(f);(n===q||n)&&m.columns.adjust();(l===q||l)&&m.draw();return 0};this.fnVersionCheck=L.fnVersionCheck;var b=this,c=a===q,d=this.length;c&&(a={});this.oApi=this.internal=L.internal;for(var e in u.ext.internal)e&&(this[e]= -fc(e));this.each(function(){var f={},g=1").appendTo(p));r.nTHead=E[0];var aa=p.children("tbody");0===aa.length&&(aa=k("").insertAfter(E));r.nTBody=aa[0];E=p.children("tfoot"); -0===E.length&&0").appendTo(p));0===E.length||0===E.children().length?p.addClass(A.sNoFooter):0/g,tc=/^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/,uc=/(\/|\.|\*|\+|\?|\||\(|\)|\[|\]|\{|\}|\\|\$|\^|\-)/g,rb=/['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi,Z=function(a){return a&&!0!==a&&"-"!==a?!1:!0},hc=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},ic=function(a,b){sb[b]||(sb[b]=new RegExp(ib(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(sb[b],"."): -a},tb=function(a,b,c){var d="string"===typeof a;if(Z(a))return!0;b&&d&&(a=ic(a,b));c&&d&&(a=a.replace(rb,""));return!isNaN(parseFloat(a))&&isFinite(a)},jc=function(a,b,c){return Z(a)?!0:Z(a)||"string"===typeof a?tb(a.replace(Ua,""),b,c)?!0:null:null},T=function(a,b,c){var d=[],e=0,f=a.length;if(c!==q)for(;ea.length)){var b=a.slice().sort();for(var c=b[0],d=1,e=b.length;d")[0],rc=Pa.textContent!==q,sc=/<.*?>/g,gb=u.util.throttle,mc=[],N=Array.prototype,vc=function(a){var b,c=u.settings,d=k.map(c,function(f,g){return f.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase()){var e=k.inArray(a,d);return-1!==e?[c[e]]:null}if(a&&"function"===typeof a.settings)return a.settings().toArray(); -"string"===typeof a?b=k(a):a instanceof k&&(b=a)}else return[];if(b)return b.map(function(f){e=k.inArray(this,d);return-1!==e?c[e]:null}).toArray()};var C=function(a,b){if(!(this instanceof C))return new C(a,b);var c=[],d=function(g){(g=vc(g))&&c.push.apply(c,g)};if(Array.isArray(a))for(var e=0,f=a.length;ea?new C(b[a],this[a]):null},filter:function(a){var b=[];if(N.filter)b=N.filter.call(this,a,this);else for(var c=0,d=this.length;c").addClass(h),k("td",l).addClass(h).html(g)[0].colSpan=na(a),e.push(l[0]))};f(c,d);b._details&&b._details.detach();b._details=k(e);b._detailsShow&&b._details.insertAfter(b.nTr)},xb=function(a,b){var c=a.context;c.length&& -(a=c[0].aoData[b!==q?b:a[0]])&&a._details&&(a._details.remove(),a._detailsShow=q,a._details=q)},pc=function(a,b){var c=a.context;c.length&&a.length&&(a=c[0].aoData[a[0]],a._details&&((a._detailsShow=b)?a._details.insertAfter(a.nTr):a._details.detach(),yc(c[0])))},yc=function(a){var b=new C(a),c=a.aoData;b.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0h){var m=k.map(d,function(p, -t){return p.bVisible?t:null});return[m[m.length+h]]}return[sa(a,h)];case "name":return k.map(e,function(p,t){return p===n[1]?t:null});default:return[]}if(g.nodeName&&g._DT_CellIndex)return[g._DT_CellIndex.column];h=k(f).filter(g).map(function(){return k.inArray(this,f)}).toArray();if(h.length||!g.nodeName)return h;h=k(g).closest("*[data-dt-column]");return h.length?[h.data("dt-column")]:[]},a,c)};w("columns()",function(a,b){a===q?a="":k.isPlainObject(a)&&(b=a,a="");b=vb(b);var c=this.iterator("table", -function(d){return Ac(d,a,b)},1);c.selector.cols=a;c.selector.opts=b;return c});J("columns().header()","column().header()",function(a,b){return this.iterator("column",function(c,d){return c.aoColumns[d].nTh},1)});J("columns().footer()","column().footer()",function(a,b){return this.iterator("column",function(c,d){return c.aoColumns[d].nTf},1)});J("columns().data()","column().data()",function(){return this.iterator("column-rows",qc,1)});J("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column", -function(a,b){return a.aoColumns[b].mData},1)});J("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return Da(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});J("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return Da(a.aoData,e,"anCells",b)},1)});J("columns().visible()","column().visible()",function(a,b){var c=this,d=this.iterator("column",function(e,f){if(a===q)return e.aoColumns[f].bVisible; -var g=e.aoColumns,h=g[f],l=e.aoData,n;if(a!==q&&h.bVisible!==a){if(a){var m=k.inArray(!0,T(g,"bVisible"),f+1);g=0;for(n=l.length;gd;return!0};u.isDataTable=u.fnIsDataTable=function(a){var b=k(a).get(0),c=!1;if(a instanceof u.Api)return!0;k.each(u.settings,function(d,e){d=e.nScrollHead?k("table",e.nScrollHead)[0]:null;var f=e.nScrollFoot?k("table",e.nScrollFoot)[0]:null;if(e.nTable===b||d===b||f===b)c=!0});return c};u.tables=u.fnTables=function(a){var b=!1;k.isPlainObject(a)&&(b=a.api,a=a.visible); -var c=k.map(u.settings,function(d){if(!a||a&&k(d.nTable).is(":visible"))return d.nTable});return b?new C(c):c};u.camelToHungarian=O;w("$()",function(a,b){b=this.rows(b).nodes();b=k(b);return k([].concat(b.filter(a).toArray(),b.find(a).toArray()))});k.each(["on","one","off"],function(a,b){w(b+"()",function(){var c=Array.prototype.slice.call(arguments);c[0]=k.map(c[0].split(/\s/),function(e){return e.match(/\.dt\b/)?e:e+".dt"}).join(" ");var d=k(this.tables().nodes());d[b].apply(d,c);return this})}); -w("clear()",function(){return this.iterator("table",function(a){Ia(a)})});w("settings()",function(){return new C(this.context,this.context)});w("init()",function(){var a=this.context;return a.length?a[0].oInit:null});w("data()",function(){return this.iterator("table",function(a){return T(a.aoData,"_aData")}).flatten()});w("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,h=b.nTFoot,l=k(e);f=k(f); -var n=k(b.nTableWrapper),m=k.map(b.aoData,function(t){return t.nTr}),p;b.bDestroying=!0;H(b,"aoDestroyCallback","destroy",[b]);a||(new C(b)).columns().visible(!0);n.off(".DT").find(":not(tbody *)").off(".DT");k(y).off(".DT-"+b.sInstance);e!=g.parentNode&&(l.children("thead").detach(),l.append(g));h&&e!=h.parentNode&&(l.children("tfoot").detach(),l.append(h));b.aaSorting=[];b.aaSortingFixed=[];Qa(b);k(m).removeClass(b.asStripeClasses.join(" "));k("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+ -" "+d.sSortableDesc+" "+d.sSortableNone);f.children().detach();f.append(m);g=a?"remove":"detach";l[g]();n[g]();!a&&c&&(c.insertBefore(e,b.nTableReinsertBefore),l.css("width",b.sDestroyWidth).removeClass(d.sTable),(p=b.asDestroyStripes.length)&&f.children().each(function(t){k(this).addClass(b.asDestroyStripes[t%p])}));c=k.inArray(b,u.settings);-1!==c&&u.settings.splice(c,1)})});k.each(["column","row","cell"],function(a,b){w(b+"s().every()",function(c){var d=this.selector.opts,e=this;return this.iterator(b, -function(f,g,h,l,n){c.call(e[b](g,"cell"===b?h:d,"cell"===b?d:q),g,h,l,n)})})});w("i18n()",function(a,b,c){var d=this.context[0];a=ia(a)(d.oLanguage);a===q&&(a=b);c!==q&&k.isPlainObject(a)&&(a=a[c]!==q?a[c]:a._);return a.replace("%d",c)});u.version="1.10.25";u.settings=[];u.models={};u.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};u.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null,idx:-1};u.models.oColumn= -{idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};u.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null, -aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null, -fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){return{}}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null, -iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"", -sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:k.extend({},u.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId"};G(u.defaults);u.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc", -"desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};G(u.defaults.column);u.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null, -sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1,bBounding:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[], -aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,jqXHR:null,json:q,oAjaxData:q,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1, -iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==P(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==P(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate; -return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};u.ext=L={buttons:{},classes:{},builder:"bs5/dt-1.10.25/r-2.2.9",errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:u.fnVersionCheck, -iApiIndex:0,oJUIClasses:{},sVersion:u.version};k.extend(L,{afnFiltering:L.search,aTypes:L.type.detect,ofnSearch:L.type.search,oSort:L.type.order,afnSortData:L.order,aoFeatures:L.feature,oApi:L.internal,oStdClasses:L.classes,oPagination:L.pager});k.extend(u.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter", -sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_desc_disabled",sSortableDesc:"sorting_asc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody", -sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var ec=u.ext.pager;k.extend(ec,{simple:function(a,b){return["previous","next"]},full:function(a,b){return["first","previous","next","last"]},numbers:function(a,b){return[Ca(a,b)]},simple_numbers:function(a,b){return["previous",Ca(a,b),"next"]}, -full_numbers:function(a,b){return["first","previous",Ca(a,b),"next","last"]},first_last_numbers:function(a,b){return["first",Ca(a,b),"last"]},_numbers:Ca,numbers_length:7});k.extend(!0,u.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,h=a.oLanguage.oPaginate,l=a.oLanguage.oAria.paginate||{},n,m,p=0,t=function(x,r){var A,D=g.sPageButtonDisabled,I=function(E){lb(a,E.data.action,!0)};var W=0;for(A=r.length;W").appendTo(x); -t(B,M)}else{n=null;m=M;B=a.iTabIndex;switch(M){case "ellipsis":x.append('');break;case "first":n=h.sFirst;0===e&&(B=-1,m+=" "+D);break;case "previous":n=h.sPrevious;0===e&&(B=-1,m+=" "+D);break;case "next":n=h.sNext;if(0===f||e===f-1)B=-1,m+=" "+D;break;case "last":n=h.sLast;if(0===f||e===f-1)B=-1,m+=" "+D;break;default:n=a.fnFormatNumber(M+1),m=e===M?g.sPageButtonActive:""}null!==n&&(B=k("",{"class":g.sPageButton+" "+m,"aria-controls":a.sTableId,"aria-label":l[M], -"data-dt-idx":p,tabindex:B,id:0===c&&"string"===typeof M?a.sTableId+"_"+M:null}).html(n).appendTo(x),pb(B,{action:M},I),p++)}}};try{var v=k(b).find(z.activeElement).data("dt-idx")}catch(x){}t(k(b).empty(),d);v!==q&&k(b).find("[data-dt-idx="+v+"]").trigger("focus")}}});k.extend(u.ext.type.detect,[function(a,b){b=b.oLanguage.sDecimal;return tb(a,b)?"num"+b:null},function(a,b){if(a&&!(a instanceof Date)&&!tc.test(a))return null;b=Date.parse(a);return null!==b&&!isNaN(b)||Z(a)?"date":null},function(a, -b){b=b.oLanguage.sDecimal;return tb(a,b,!0)?"num-fmt"+b:null},function(a,b){b=b.oLanguage.sDecimal;return jc(a,b)?"html-num"+b:null},function(a,b){b=b.oLanguage.sDecimal;return jc(a,b,!0)?"html-num-fmt"+b:null},function(a,b){return Z(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);k.extend(u.ext.type.search,{html:function(a){return Z(a)?a:"string"===typeof a?a.replace(gc," ").replace(Ua,""):""},string:function(a){return Z(a)?a:"string"===typeof a?a.replace(gc," "):a}});var Ta=function(a, -b,c,d){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=ic(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};k.extend(L.type.order,{"date-pre":function(a){a=Date.parse(a);return isNaN(a)?-Infinity:a},"html-pre":function(a){return Z(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return Z(a)?"":"string"===typeof a?a.toLowerCase():a.toString?a.toString():""},"string-asc":function(a,b){return ab?1:0},"string-desc":function(a,b){return a< -b?1:a>b?-1:0}});Wa("");k.extend(!0,u.ext.renderer,{header:{_:function(a,b,c,d){k(a.nTable).on("order.dt.DT",function(e,f,g,h){a===f&&(e=c.idx,b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass("asc"==h[e]?d.sSortAsc:"desc"==h[e]?d.sSortDesc:c.sSortingClass))})},jqueryui:function(a,b,c,d){k("
    ").addClass(d.sSortJUIWrapper).append(b.contents()).append(k("").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);k(a.nTable).on("order.dt.DT",function(e,f,g,h){a===f&&(e=c.idx,b.removeClass(d.sSortAsc+ -" "+d.sSortDesc).addClass("asc"==h[e]?d.sSortAsc:"desc"==h[e]?d.sSortDesc:c.sSortingClass),b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass("asc"==h[e]?d.sSortJUIAsc:"desc"==h[e]?d.sSortJUIDesc:c.sSortingClassJUI))})}}});var yb=function(a){return"string"===typeof a?a.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""):a};u.render={number:function(a,b,c,d,e){return{display:function(f){if("number"!== -typeof f&&"string"!==typeof f)return f;var g=0>f?"-":"",h=parseFloat(f);if(isNaN(h))return yb(f);h=h.toFixed(c);f=Math.abs(h);h=parseInt(f,10);f=c?b+(f-h).toFixed(c).substring(2):"";0===h&&0===parseFloat(f)&&(g="");return g+(d||"")+h.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+f+(e||"")}}},text:function(){return{display:yb,filter:yb}}};k.extend(u.ext.internal,{_fnExternApiFunc:fc,_fnBuildAjax:Ma,_fnAjaxUpdate:Gb,_fnAjaxParameters:Pb,_fnAjaxUpdateDraw:Qb,_fnAjaxDataSrc:Na,_fnAddColumn:Xa,_fnColumnOptions:Ea, -_fnAdjustColumnSizing:ra,_fnVisibleToColumnIndex:sa,_fnColumnIndexToVisible:ta,_fnVisbleColumns:na,_fnGetColumns:Ga,_fnColumnTypes:Za,_fnApplyColumnDefs:Db,_fnHungarianMap:G,_fnCamelToHungarian:O,_fnLanguageCompat:ma,_fnBrowserDetect:Bb,_fnAddData:ea,_fnAddTr:Ha,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==q?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return k.inArray(c,a.aoData[b].anCells)},_fnGetCellData:S,_fnSetCellData:Eb,_fnSplitObjNotation:bb,_fnGetObjectDataFn:ia, -_fnSetObjectDataFn:da,_fnGetDataMaster:cb,_fnClearTable:Ia,_fnDeleteIndex:Ja,_fnInvalidate:va,_fnGetRowElements:ab,_fnCreateTr:$a,_fnBuildHead:Fb,_fnDrawHead:xa,_fnDraw:fa,_fnReDraw:ja,_fnAddOptionsHtml:Ib,_fnDetectHeader:wa,_fnGetUniqueThs:La,_fnFeatureHtmlFilter:Kb,_fnFilterComplete:ya,_fnFilterCustom:Tb,_fnFilterColumn:Sb,_fnFilter:Rb,_fnFilterCreateSearch:hb,_fnEscapeRegex:ib,_fnFilterData:Ub,_fnFeatureHtmlInfo:Nb,_fnUpdateInfo:Xb,_fnInfoMacros:Yb,_fnInitialise:za,_fnInitComplete:Oa,_fnLengthChange:jb, -_fnFeatureHtmlLength:Jb,_fnFeatureHtmlPaginate:Ob,_fnPageChange:lb,_fnFeatureHtmlProcessing:Lb,_fnProcessingDisplay:U,_fnFeatureHtmlTable:Mb,_fnScrollDraw:Fa,_fnApplyToChildren:ba,_fnCalculateColumnWidths:Ya,_fnThrottle:gb,_fnConvertToWidth:Zb,_fnGetWidestNode:$b,_fnGetMaxLenString:ac,_fnStringToCss:K,_fnSortFlatten:pa,_fnSort:Hb,_fnSortAria:cc,_fnSortListener:ob,_fnSortAttachListener:eb,_fnSortingClasses:Qa,_fnSortData:bc,_fnSaveState:Ra,_fnLoadState:dc,_fnSettingsFromNode:Sa,_fnLog:ca,_fnMap:V, -_fnBindAction:pb,_fnCallbackReg:Q,_fnCallbackFire:H,_fnLengthOverflow:kb,_fnRenderer:fb,_fnDataSource:P,_fnRowAttributes:db,_fnExtend:qb,_fnCalculateEnd:function(){}});k.fn.dataTable=u;u.$=k;k.fn.dataTableSettings=u.settings;k.fn.dataTableExt=u.ext;k.fn.DataTable=function(a){return k(this).dataTable(a).api()};k.each(u,function(a,b){k.fn.DataTable[a]=b});return k.fn.dataTable}); - - -/*! - DataTables Bootstrap 5 integration - 2020 SpryMedia Ltd - datatables.net/license -*/ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var e=a.length,d=0;d<'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", -renderer:"bootstrap"});a.extend(d.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap5",sFilterInput:"form-control form-control-sm",sLengthSelect:"form-select form-select-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"});d.ext.renderer.pageButton.bootstrap=function(f,l,A,B,m,t){var u=new d.Api(f),C=f.oClasses,n=f.oLanguage.oPaginate,D=f.oLanguage.oAria.paginate||{},h,k,v=0,y=function(q,w){var x,E=function(p){p.preventDefault();a(p.currentTarget).hasClass("disabled")|| -u.page()==p.data.action||u.page(p.data.action).draw("page")};var r=0;for(x=w.length;r",{"class":C.sPageButton+ -" "+k,id:0===A&&"string"===typeof g?f.sTableId+"_"+g:null}).append(a("",{href:"#","aria-controls":f.sTableId,"aria-label":D[g],"data-dt-idx":v,tabindex:f.iTabIndex,"class":"page-link"}).html(h)).appendTo(q);f.oApi._fnBindAction(F,{action:g},E);v++}}}};try{var z=a(l).find(c.activeElement).data("dt-idx")}catch(q){}y(a(l).empty().html('