diff --git a/Plan/api/build.gradle b/Plan/api/build.gradle index bd3e76ee4..465a78ecd 100644 --- a/Plan/api/build.gradle +++ b/Plan/api/build.gradle @@ -1,8 +1,8 @@ plugins { - id "com.jfrog.bintray" version "1.8.1" + id "com.jfrog.bintray" version "1.8.4" } -ext.apiVersion = '0.0.3' +ext.apiVersion = '0.0.4' bintray { user = System.getenv('BINTRAY_USER') diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java new file mode 100644 index 000000000..0ea9a689f --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java @@ -0,0 +1,56 @@ +/* + * 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.capability; + +import java.util.Optional; + +/** + * List of different capabilities current version provides. + *

+ * The enum is package private to restrict direct access. This is to avoid NoClassDefFoundError in case + * a wanted Capability is not provided in an earlier version. + *

+ * Use {@link CapabilityService#hasCapability(String)} with name of a Capability to figure out if an API is available. + * Example usage: {@code CapabilityService.getInstance().hasCapability("DATA_EXTENSION_VALUES")}. + *

+ * If a capability is not available, attempting to use the capability might lead to exceptions. + * + * @author Rsl1122 + */ +enum Capability { + + /** + * ExtensionService, DataExtension API base package, PluginInfo, Conditional, Tab, TabInfo, TabOrder and BooleanProvider, DoubleProvider, PercentageProvider, NumberProvider, StringProvider annotations. + */ + DATA_EXTENSION_VALUES, + /** + * DataExtension API table package, TableProvider, Table and Table.Factory + */ + DATA_EXTENSION_TABLES; + + static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } + try { + return Optional.of(valueOf(name)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + +} \ No newline at end of file diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java new file mode 100644 index 000000000..cd5ed969f --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java @@ -0,0 +1,76 @@ +/* + * 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.capability; + +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Service for figuring out provided API capabilities. + *

+ * {@link CapabilityService#registerEnableListener(Consumer)} to be notified of Plan reloads + * {@link CapabilityService#hasCapability(String)} to check if a capability is available. + *

+ * See {@link Capability} for list of capabilities provided by the current version. + * + * @author Rsl1122 + */ +public interface CapabilityService { + + /** + * Obtain instance of CapabilityService. + * + * @return CapabilityService implementation. + * @throws NoClassDefFoundError If Plan is not installed and this class can not be found or if older Plan version is installed. + * @throws IllegalStateException If Plan is installed, but not enabled. + */ + static CapabilityService getInstance() { + return Optional.ofNullable(CapabilityServiceHolder.service) + .orElseThrow(() -> new IllegalStateException("CapabilityService has not been initialised yet.")); + } + + /** + * Register a method to be called when Plan reloads. + * + * @param isEnabledListener The boolean given to the method tells if Plan has enabled successfully. + */ + void registerEnableListener(Consumer isEnabledListener); + + /** + * Check if the API on the current version provides a capability. + * + * @param capabilityName Name of a capability + * @return true if the capability is available. + * @see Capability for different capabilityNames. + */ + default boolean hasCapability(String capabilityName) { + return Capability.getByName(capabilityName).isPresent(); + } + + class CapabilityServiceHolder { + static CapabilityService service; + + private CapabilityServiceHolder() { + /* Static variable holder */ + } + + static void set(CapabilityService service) { + CapabilityServiceHolder.service = service; + } + } + +} diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java index 8e489a6d5..f4d3fd634 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java @@ -32,14 +32,14 @@ public enum ElementOrder { * Represents text - value pair box. */ VALUES, - /** - * Represents tables. - */ - TABLE, /** * Represents graphs. */ - GRAPH; + GRAPH, + /** + * Represents tables. + */ + TABLE; public static String serialize(ElementOrder[] order) { StringBuilder builder = new StringBuilder(); diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java index 8527a136f..ca59cfda9 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java @@ -43,6 +43,9 @@ public enum FormatType { NONE; public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } try { return Optional.of(valueOf(name)); } catch (IllegalArgumentException e) { diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java new file mode 100644 index 000000000..1a8f69936 --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java @@ -0,0 +1,49 @@ +/* + * 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.extension.annotation; + +import com.djrapitops.plan.extension.icon.Color; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Method annotation to provide a Table. + *

+ * Usage: {@code @TableProvider Table method(UUID playerUUID)} + *

+ * Tables about players can have up to 4 columns. + * Tables about server can have up to 5 columns. + *

+ * It is recommended to place each table on their own tab with a {@link Tab} annotation on the same method. + * + * @author Rsl1122 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface TableProvider { + + /** + * Determine the color of the table header. + * + * @return Preferred color. If none are specified defaults are used. + */ + Color tableColor() default Color.NONE; + +} diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java index 03f372de0..81f164a74 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.extractor; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.Group; import com.djrapitops.plan.extension.annotation.*; +import com.djrapitops.plan.extension.table.Table; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -103,6 +104,8 @@ public final class ExtensionExtractor { MethodAnnotations.get(method, Conditional.class).ifPresent(annotation -> methodAnnotations.put(method, Conditional.class, annotation)); MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation)); + + MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation)); } if (methodAnnotations.isEmpty()) { @@ -166,6 +169,7 @@ public final class ExtensionExtractor { validateDoubleProviderAnnotations(); validatePercentageProviderAnnotations(); validateStringProviderAnnotations(); + validateTableProviderAnnotations(); } private void validateBooleanProviderAnnotations() { @@ -238,6 +242,13 @@ public final class ExtensionExtractor { } } + private void validateTableProviderAnnotations() { + for (Method method : methodAnnotations.getMethodAnnotations(TableProvider.class).keySet()) { + validateReturnType(method, Table.class); + validateMethodArguments(method, false, UUID.class, String.class, Group.class); + } + } + private void validateConditionals() { Collection conditionals = methodAnnotations.getAnnotations(Conditional.class); Collection conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class); diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java index 16783c61b..58f9296fb 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java @@ -48,6 +48,9 @@ public enum Color { NONE; public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } try { return Optional.of(valueOf(name)); } catch (IllegalArgumentException e) { diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java index 204ec2660..0125d6ee9 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java @@ -29,6 +29,9 @@ public enum Family { BRAND; public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } try { return Optional.of(valueOf(name)); } catch (IllegalArgumentException e) { 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 277510d6e..8fc722d13 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 @@ -64,8 +64,9 @@ public class Icon { return color; } - public void setColor(Color color) { + public Icon setColor(Color color) { this.color = color; + return this; } @Override 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 new file mode 100644 index 000000000..b082fcf1c --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java @@ -0,0 +1,213 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.extension.table; + +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Icon; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Object for giving Plan table data. + *

+ * Usage: {@code Table.builder().columnOne("columnName", new Icon(...)).addRow("Your", "Row", "Data").build()} + *

+ * Tables about players can have up to 4 columns. + * Tables about server can have up to 5 columns. + *

+ * Icon colors are ignored. + *

+ * If a row has more values than the column limit, they are ignored. + * If a row has less values than table columns, a '-' is displayed to distinguish a missing value. + *

+ * If a table has no columns or rows, it is ignored. + * + * @author Rsl1122 + * @see com.djrapitops.plan.extension.annotation.TableProvider for associated annotation. + */ +public final class Table { + + private final String[] columns; + private final Icon[] icons; + + private final List rows; + + private Table() { + /* Tables are constructed with the factory. */ + + columns = new String[5]; + icons = new Icon[5]; + rows = new ArrayList<>(); + } + + /** + * Create a new Table Factory. + * + * @return a new Table Factory. + */ + public static Table.Factory builder() { + return new Table.Factory(); + } + + public String[] getColumns() { + return columns; + } + + public int getMaxColumnSize() { + int columnCount = 0; + for (String column : columns) { + if (column == null) { + break; // Prevent having one null column between two columns + } + columnCount++; + } + return columnCount; + } + + public Icon[] getIcons() { + return icons; + } + + public List getRows() { + return rows; + } + + /** + * Factory for creating new {@link Table} objects. + */ + public static final class Factory { + + private final Table building; + + // These variable are related to implementation, and is used when the table is being fetched from the database. + Color color; // Table color is defined with TableProvider annotation. + String tableName; // tableName is defined by method name annotated by TableProvider. + String tabName; // Tab name is defined with Tab annotation + int tabPriority; // Tab priority is defined with TabOrder annotation + ElementOrder[] tabOrder; // Tab element order is defined with TabInfo annotation + Icon tabIcon; // Tab icon is defined with TabInfo annotation + + private Factory() { + building = new Table(); + } + + private Factory column(int indx, String columnName, Icon icon) { + building.columns[indx] = columnName; + building.icons[indx] = icon != null ? Icon.called(icon.getName()).of(icon.getFamily()).build() : Icon.called("question").build(); + return this; + } + + /** + * Set first column name and icon. + * + * @param columnName Name of the column. + * @param icon Icon of the column, color is ignored. + * @return Factory. + */ + public Factory columnOne(String columnName, Icon icon) { + return column(0, columnName, icon); + } + + /** + * Set second column name and icon. + * + * @param columnName Name of the column. + * @param icon Icon of the column, color is ignored. + * @return Factory. + */ + public Factory columnTwo(String columnName, Icon icon) { + return column(1, columnName, icon); + } + + /** + * Set third column name and icon. + * + * @param columnName Name of the column. + * @param icon Icon of the column, color is ignored. + * @return Factory. + */ + public Factory columnThree(String columnName, Icon icon) { + return column(2, columnName, icon); + } + + /** + * Set fourth column name and icon. + * + * @param columnName Name of the column. + * @param icon Icon of the column, color is ignored. + * @return Factory. + */ + public Factory columnFour(String columnName, Icon icon) { + return column(3, columnName, icon); + } + + /** + * Set fifth column name and icon. + * + * @param columnName Name of the column. + * @param icon Icon of the column, color is ignored. + * @return Factory. + */ + public Factory columnFive(String columnName, Icon icon) { + return column(4, columnName, icon); + } + + /** + * Add a row of values to the table. + * + * @param values One value per column you have defined, {@code Object#toString()} will be called on the objects. + * @return Factory. + * @throws IllegalArgumentException If given varargs for 'values' is null. + */ + public Factory addRow(Object... values) { + if (values == null) { + throw new IllegalArgumentException("'values' for Table#addRow can not be null!"); + } + + if (values.length == 0 || areAllValuesNull(values)) { + return this; // Ignore row when all values are null or no values are present. + } + + building.rows.add(Arrays.copyOf(values, 5)); + return this; + } + + private boolean areAllValuesNull(Object[] values) { + boolean allNull = true; + for (Object value : values) { + if (value != null) { + allNull = false; + break; + } + } + return allNull; + } + + /** + * Finish building the table. + * + * @return Finished Table object. + */ + public Table build() { + return building; + } + } + +} \ No newline at end of file diff --git a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java index 44d45d382..c91bb62f8 100644 --- a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java +++ b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java @@ -166,6 +166,20 @@ class ExtensionExtractorTest { assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: java.lang.String", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()); } + @Test + void tableProviderMustReturnTable() { + @PluginInfo(name = "Extension") + class Extension implements DataExtension { + @TableProvider + public Double method(UUID playerUUID) { + return null; + } + } + + ExtensionExtractor underTest = new ExtensionExtractor(new Extension()); + assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()); + } + @Test void booleanProviderCanNotSupplyItsOwnConditional() { @PluginInfo(name = "Extension") diff --git a/Plan/build.gradle b/Plan/build.gradle index 2336e7789..7de08b4d6 100644 --- a/Plan/build.gradle +++ b/Plan/build.gradle @@ -2,9 +2,9 @@ plugins { id "java" id "jacoco" id "checkstyle" - id "org.sonarqube" version "2.6.2" - id "net.ltgt.apt" version "0.19" - id "net.ltgt.apt-idea" version "0.19" + id "org.sonarqube" version "2.7" + id "net.ltgt.apt" version "0.21" + id "net.ltgt.apt-idea" version "0.21" id "com.github.johnrengelman.shadow" version "5.0.0" } @@ -12,7 +12,7 @@ allprojects { wrapper.gradleVersion = "5.0" group "com.djrapitops" - version "4.8.1" + version "4.8.2" test { useJUnitPlatform() @@ -43,11 +43,11 @@ subprojects { sourceCompatibility = 1.8 targetCompatibility = 1.8 - ext.daggerVersion = "2.21" - ext.daggerCompilerVersion = "2.21" + ext.daggerVersion = "2.22.1" + ext.daggerCompilerVersion = "2.22.1" ext.abstractPluginFrameworkVersion = "3.4.1" - ext.planPluginBridgeVersion = "4.7.0" + ext.planPluginBridgeVersion = "4.8.2-R0.3" ext.bukkitVersion = "1.12.2-R0.1-SNAPSHOT" ext.spigotVersion = "1.12.2-R0.1-SNAPSHOT" @@ -61,12 +61,12 @@ subprojects { ext.commonsTextVersion = "1.6" ext.htmlCompressorVersion = "1.5.2" ext.caffeineVersion = "2.7.0" - ext.h2Version = "1.4.196" + ext.h2Version = "1.4.199" ext.hikariVersion = "3.3.1" ext.slf4jVersion = "1.7.26" ext.geoIpVersion = "2.12.0" ext.guavaVersion = "26.0-jre" - ext.bstatsVersion = "1.2" + ext.bstatsVersion = "1.4" repositories { mavenCentral() @@ -89,7 +89,7 @@ subprojects { url = "https://repo.velocitypowered.com/snapshots/" } maven { // bStats Repository - url = "http://repo.bstats.org/content/repositories/releases/" + url = "https://repo.codemc.org/repository/maven-public" } maven { // PlanPluginBridge Repository url = "https://dl.bintray.com/rsl1122/Plan-repository" @@ -103,18 +103,18 @@ subprojects { testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerCompilerVersion" // Test Tooling Dependencies - testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.1" // JUnit 5 - testCompile "org.junit.platform:junit-platform-runner:1.4.1" // JUnit 4 runner for JUnit 5 tests - testCompile "org.junit.vintage:junit-vintage-engine:5.4.1" // JUnit 4 compatibility for JUnit 5 - testCompile "org.junit.jupiter:junit-jupiter-params:5.4.1" // JUnit 5, parameterized tests - testCompile "org.mockito:mockito-core:2.25.1" // Mockito Core - testCompile "org.mockito:mockito-junit-jupiter:2.25.1" // Mockito JUnit 5 Extension + testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.2" // JUnit 5 + testCompile "org.junit.platform:junit-platform-runner:1.4.2" // JUnit 4 runner for JUnit 5 tests + testCompile "org.junit.vintage:junit-vintage-engine:5.4.2" // JUnit 4 compatibility for JUnit 5 + testCompile "org.junit.jupiter:junit-jupiter-params:5.4.2" // JUnit 5, parameterized tests + testCompile "org.mockito:mockito-core:2.27.0" // Mockito Core + testCompile "org.mockito:mockito-junit-jupiter:2.27.0" // Mockito JUnit 5 Extension testCompile "org.seleniumhq.selenium:selenium-java:3.141.59" // Selenium (Browser tests) testCompile "com.jayway.awaitility:awaitility:1.7.0" // Awaitility (Concurrent wait conditions) // Testing dependencies required by Plan testCompile "org.xerial:sqlite-jdbc:3.27.2.1" // SQLite - testCompile "mysql:mysql-connector-java:8.0.15" // MySQL + testCompile "mysql:mysql-connector-java:8.0.16" // MySQL } configurations { diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java index 2ae0e2506..e09fa21da 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java @@ -26,6 +26,7 @@ import com.djrapitops.plan.system.settings.theme.PlanColorScheme; import com.djrapitops.plugin.BukkitPlugin; import com.djrapitops.plugin.benchmarking.Benchmark; import com.djrapitops.plugin.command.ColorScheme; +import com.djrapitops.plugin.task.AbsRunnable; import org.bukkit.configuration.file.FileConfiguration; import java.util.logging.Level; @@ -52,7 +53,7 @@ public class Plan extends BukkitPlugin implements PlanPlugin { locale = system.getLocaleSystem().getLocale(); system.enable(); - new BStatsBukkit(this).registerMetrics(); + registerMetrics(); logger.debug("Verbose debug messages are enabled."); String benchTime = " (" + timings.end("Enable").map(Benchmark::toDurationString).orElse("-") + ")"; @@ -80,6 +81,19 @@ public class Plan extends BukkitPlugin implements PlanPlugin { } } + private void registerMetrics() { + Plan plugin = this; + // Spigot 1.14 requires Sync events to be fired from a server thread. + // Registering a service fires a sync event, and bStats registers a service, + // so this has to be run on the server thread. + runnableFactory.create("Register Metrics task", new AbsRunnable() { + @Override + public void run() { + new BStatsBukkit(plugin).registerMetrics(); + } + }).runTask(); + } + @Override public ColorScheme getColorScheme() { return PlanColorScheme.create(system.getConfigSystem().getConfig(), logger); diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java index bdfded339..f387b3a80 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java @@ -33,10 +33,8 @@ import com.djrapitops.plan.system.importing.data.BukkitUserImportRefiner; import com.djrapitops.plan.system.importing.data.ServerImportData; import com.djrapitops.plan.system.importing.data.UserImportData; import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.utilities.SHA256Hash; import com.djrapitops.plugin.utilities.Verify; -import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -177,11 +175,11 @@ public abstract class BukkitImporter implements Importer { } private BaseUser toBaseUser(UserImportData userImportData) { - UUID uuid = userImportData.getUuid(); - String name = userImportData.getName(); + UUID playerUUID = userImportData.getUuid(); + String playerName = userImportData.getName(); long registered = userImportData.getRegistered(); int timesKicked = userImportData.getTimesKicked(); - return new BaseUser(uuid, name, registered, timesKicked); + return new BaseUser(playerUUID, playerName, registered, timesKicked); } private UserInfo toUserInfo(UserImportData userImportData) { @@ -211,11 +209,7 @@ public abstract class BukkitImporter implements Importer { return userImportData.getIps().parallelStream() .map(ip -> { String geoLoc = geolocationCache.getCountry(ip); - try { - return new GeoInfo(ip, geoLoc, date, new SHA256Hash(ip).create()); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e); - } + return new GeoInfo(ip, geoLoc, date); }).collect(Collectors.toList()); } } diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java index ef84da41f..09bb86b44 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java @@ -27,7 +27,6 @@ public class BukkitServerProperties extends ServerProperties { public BukkitServerProperties(Server server) { super( - server.getServerId(), server.getName(), server.getPort(), server.getVersion(), diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java index 693dfc3ba..6a00fe207 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners; import com.djrapitops.plan.Plan; import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.events.PlanBukkitEnableEvent; +import com.djrapitops.plan.capability.CapabilityServiceImplementation; import com.djrapitops.plan.system.listeners.bukkit.*; import com.djrapitops.plan.system.status.Status; import org.bukkit.Bukkit; @@ -83,7 +84,9 @@ public class BukkitListenerSystem extends ListenerSystem { @Override public void callEnableEvent(PlanPlugin plugin) { - PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(plugin.isSystemEnabled()); + boolean isEnabled = plugin.isSystemEnabled(); + PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(isEnabled); Bukkit.getServer().getPluginManager().callEvent(event); + CapabilityServiceImplementation.notifyAboutEnable(isEnabled); } } diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java index 364c53215..cd2d4ea14 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.ShutdownHook; import com.djrapitops.plan.db.tasks.DBCleanTask; import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask; import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; import com.djrapitops.plan.system.settings.paths.TimeSettings; import com.djrapitops.plan.system.tasks.bukkit.BukkitTPSCountTimer; import com.djrapitops.plan.system.tasks.bukkit.PaperTPSCountTimer; @@ -90,9 +91,12 @@ public class BukkitTaskSystem extends ServerTaskSystem { public void enable() { super.enable(); try { - plugin.registerListener(pingCountTimer); - long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS); - registerTask(pingCountTimer).runTaskTimer(startDelay, 40L); + Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY); + if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) { + plugin.registerListener(pingCountTimer); + long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS); + registerTask(pingCountTimer).runTaskTimer(startDelay, 40L); + } } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { // Running CraftBukkit } diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java index 40390b164..3d98d62ba 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java @@ -64,7 +64,6 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { //the server is pinging the client every 40 Ticks (2 sec) - so check it then //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 - public static final int PING_INTERVAL = 2 * 20; private static final boolean PING_METHOD_AVAILABLE; @@ -196,6 +195,10 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent joinEvent) { Player player = joinEvent.getPlayer(); + Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); + if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { + return; + } runnableFactory.create("Add Player to Ping list", new AbsRunnable() { @Override public void run() { @@ -203,7 +206,7 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { addPlayer(player); } } - }).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS)); + }).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS)); } @EventHandler diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java index 1372a529d..b1a143beb 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java @@ -122,7 +122,7 @@ public final class Reflection { try { return (T) field.get(target); } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot access reflection.", e); + throw new IllegalStateException("Cannot access reflection.", e); } } @@ -131,7 +131,7 @@ public final class Reflection { try { field.set(target, value); } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot access reflection.", e); + throw new IllegalStateException("Cannot access reflection.", e); } } @@ -197,7 +197,7 @@ public final class Reflection { try { return method.invoke(target, arguments); } catch (Exception e) { - throw new RuntimeException("Cannot invoke method " + method, e); + throw new IllegalStateException("Cannot invoke method " + method, e); } }; } @@ -240,7 +240,7 @@ public final class Reflection { try { return constructor.newInstance(arguments); } catch (Exception e) { - throw new RuntimeException("Cannot invoke constructor " + constructor, e); + throw new IllegalStateException("Cannot invoke constructor " + constructor, e); } }; } diff --git a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java index f76eba214..65f0fabcc 100644 --- a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java +++ b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java @@ -17,7 +17,10 @@ package com.djrapitops.plan; import com.djrapitops.plan.api.exceptions.EnableException; +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.queries.objects.ServerQueries; import com.djrapitops.plan.system.PlanSystem; +import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.settings.ConfigSettingKeyTest; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.WebserverSettings; @@ -31,9 +34,11 @@ import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import rules.BukkitComponentMocker; import rules.ComponentMocker; +import utilities.OptionalAssert; import utilities.RandomData; import java.util.Collection; +import java.util.Optional; import static org.junit.Assert.assertTrue; @@ -71,6 +76,21 @@ public class BukkitSystemTest { } } + @Test + public void correctWebAddressInDatabaseAfterEnable() throws EnableException { + try { + system.enable(); + Database database = system.getDatabaseSystem().getDatabase(); + String expectedAddress = system.getWebServerSystem().getWebServer().getAccessAddress(); + Optional found = database.query(ServerQueries.fetchServerMatchingIdentifier(system.getServerInfo().getServerUUID())) + .map(Server::getWebAddress); + + OptionalAssert.equals(expectedAddress, found); + } finally { + system.disable(); + } + } + @Test public void bukkitSystemHasDefaultConfigValuesAfterEnable() throws EnableException, IllegalAccessException { try { diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java index 653713b0b..424ac8494 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java @@ -31,7 +31,6 @@ public class BungeeServerProperties extends ServerProperties { public BungeeServerProperties(ProxyServer server, PlanConfig config) { super( - server.getServers().toString(), "BungeeCord", -1, server.getVersion(), diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java index b4fba6aa4..722ed46dd 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners; import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.events.PlanBungeeEnableEvent; +import com.djrapitops.plan.capability.CapabilityServiceImplementation; import com.djrapitops.plan.system.listeners.bungee.PlayerOnlineListener; import javax.inject.Inject; @@ -46,7 +47,9 @@ public class BungeeListenerSystem extends ListenerSystem { @Override public void callEnableEvent(PlanPlugin plugin) { - PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(plugin.isSystemEnabled()); + boolean isEnabled = plugin.isSystemEnabled(); + PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(isEnabled); ((PlanBungee) plugin).getProxy().getPluginManager().callEvent(event); + CapabilityServiceImplementation.notifyAboutEnable(isEnabled); } } diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java index b34ead8a6..dca6baaa5 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java @@ -20,6 +20,7 @@ import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.db.tasks.DBCleanTask; import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask; import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; import com.djrapitops.plan.system.settings.paths.TimeSettings; import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer; import com.djrapitops.plan.system.tasks.bungee.PingCountTimerBungee; @@ -87,9 +88,12 @@ public class BungeeTaskSystem extends TaskSystem { registerTask(networkPageRefreshTask).runTaskTimerAsynchronously(1500, TimeAmount.toTicks(5L, TimeUnit.MINUTES)); registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS)); - plugin.registerListener(pingCountTimer); - long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS); - registerTask(pingCountTimer).runTaskTimer(startDelay, PingCountTimerBungee.PING_INTERVAL); + Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY); + if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) { + plugin.registerListener(pingCountTimer); + long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS); + registerTask(pingCountTimer).runTaskTimer(startDelay, 40L); + } registerTask(playersPageRefreshTask) .runTaskTimerAsynchronously(TimeAmount.toTicks(5L, TimeUnit.MINUTES), TimeAmount.toTicks(5L, TimeUnit.MINUTES)); diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java index 6d36301d0..b9245fb62 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java @@ -52,10 +52,6 @@ import java.util.concurrent.TimeUnit; @Singleton public class PingCountTimerBungee extends AbsRunnable implements Listener { - //the server is pinging the client every 40 Ticks (2 sec) - so check it then - //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 - public static final int PING_INTERVAL = 2 * 20; - private final Map>> playerHistory; private final PlanConfig config; @@ -121,6 +117,10 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener { @EventHandler public void onPlayerJoin(ServerConnectedEvent joinEvent) { ProxiedPlayer player = joinEvent.getPlayer(); + Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); + if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { + return; + } runnableFactory.create("Add Player to Ping list", new AbsRunnable() { @Override public void run() { @@ -128,7 +128,7 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener { addPlayer(player); } } - }).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS)); + }).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS)); } @EventHandler diff --git a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java index 07cf69a24..ab4a66fb0 100644 --- a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java +++ b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.db.SQLiteDB; import com.djrapitops.plan.system.PlanSystem; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.DatabaseSettings; import com.djrapitops.plan.system.settings.paths.ProxySettings; import com.djrapitops.plan.system.settings.paths.WebserverSettings; import com.google.common.util.concurrent.MoreExecutors; @@ -33,9 +34,11 @@ import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import rules.BungeeComponentMocker; import rules.ComponentMocker; +import utilities.CIProperties; import utilities.RandomData; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; /** * Test for Bungee PlanSystem. @@ -69,6 +72,7 @@ public class BungeeSystemTest { dbSystem.setActiveDatabase(db); bungeeSystem.enable(); + assertTrue(bungeeSystem.isEnabled()); } finally { bungeeSystem.disable(); } @@ -90,8 +94,7 @@ public class BungeeSystemTest { db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService); dbSystem.setActiveDatabase(db); - bungeeSystem.enable(); - assertTrue(bungeeSystem.isEnabled()); + bungeeSystem.enable(); // Throws EnableException } finally { bungeeSystem.disable(); } @@ -107,7 +110,31 @@ public class BungeeSystemTest { config.set(WebserverSettings.PORT, TEST_PORT_NUMBER); config.set(ProxySettings.IP, "8.8.8.8"); + bungeeSystem.enable(); // Throws EnableException + } finally { + bungeeSystem.disable(); + } + } + + @Test + public void testEnableWithMySQL() throws EnableException { + boolean isCI = Boolean.parseBoolean(System.getenv(CIProperties.IS_CI_SERVICE)); + assumeTrue(isCI); + + PlanSystem bungeeSystem = component.getPlanSystem(); + try { + PlanConfig config = bungeeSystem.getConfigSystem().getConfig(); + config.set(DatabaseSettings.MYSQL_DATABASE, "Plan"); + config.set(DatabaseSettings.MYSQL_USER, "travis"); + config.set(DatabaseSettings.MYSQL_PASS, ""); + config.set(DatabaseSettings.MYSQL_HOST, "127.0.0.1"); + config.set(DatabaseSettings.TYPE, "MySQL"); + + config.set(WebserverSettings.PORT, TEST_PORT_NUMBER); + config.set(ProxySettings.IP, "8.8.8.8"); + bungeeSystem.enable(); + assertTrue(bungeeSystem.isEnabled()); } finally { bungeeSystem.disable(); } diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java similarity index 52% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java rename to Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java index 7f9a36a64..342d9cb2b 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java @@ -14,31 +14,31 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.pluginbridge.plan.redprotect; +package com.djrapitops.plan.api.exceptions; -import com.djrapitops.plan.data.plugin.HookHandler; -import com.djrapitops.pluginbridge.plan.Hook; - -import javax.inject.Inject; -import javax.inject.Singleton; +import com.djrapitops.plan.extension.implementation.providers.MethodWrapper; /** - * Hook for RedProtect plugin. + * Exception that is thrown when a call to a DataExtension method throws an exception. * * @author Rsl1122 */ -@Singleton -public class RedProtectHook extends Hook { +public class DataExtensionMethodCallException extends IllegalStateException { - @Inject - public RedProtectHook() { - super("br.net.fabiozumbi12.RedProtect.Bukkit.RedProtect"); + private final String pluginName; + private final MethodWrapper method; + + public DataExtensionMethodCallException(Throwable cause, String pluginName, MethodWrapper method) { + super(cause); + this.pluginName = pluginName; + this.method = method; } - @Override - public void hook(HookHandler handler) throws NoClassDefFoundError { - if (enabled) { - handler.addPluginDataSource(new RedProtectData()); - } + public String getPluginName() { + return pluginName; + } + + public MethodWrapper getMethod() { + return method; } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java new file mode 100644 index 000000000..38696be98 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java @@ -0,0 +1,57 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.capability; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * Singleton instance implementation for {@link CapabilityService}. + *

+ * Only one instance exists per runtime in order to notify others when the plugin enables. + * + * @author Rsl1122 + */ +public class CapabilityServiceImplementation implements CapabilityService { + + private List> enableListeners; + + private CapabilityServiceImplementation() { + /* Inject required for dagger */ + CapabilityServiceHolder.set(this); + enableListeners = new ArrayList<>(); + } + + private static CapabilityServiceImplementation get() { + if (CapabilityServiceHolder.service == null) { + return new CapabilityServiceImplementation(); + } + return (CapabilityServiceImplementation) CapabilityServiceHolder.service; + } + + public static void notifyAboutEnable(boolean isEnabled) { + for (Consumer enableListener : get().enableListeners) { + enableListener.accept(isEnabled); + } + } + + @Override + public void registerEnableListener(Consumer enableListener) { + enableListeners.add(enableListener); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java index 4a9e4449d..c82c653b8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java @@ -32,7 +32,6 @@ import com.djrapitops.plan.system.locale.lang.ManageLang; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.settings.Permissions; import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; @@ -82,7 +81,7 @@ public class ManageConDebugCommand extends CommandNode { this.webServer = webServer; this.dbSystem = dbSystem; - setShortHelp(locale.getString(Check.isBungeeAvailable() || Check.isVelocityAvailable() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON)); + setShortHelp(locale.getString(serverInfo.getServer().isProxy() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON)); setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_CON)); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java index 30ab4d9b5..913c32886 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java @@ -60,7 +60,7 @@ public class ManageDisableCommand extends CommandNode { Verify.isTrue(args.length >= 1, () -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments())))); - if ("kickcount".equals(args[0].toLowerCase())) { + if ("kickcount".equalsIgnoreCase(args[0])) { status.setCountKicks(false); sender.sendMessage(locale.getString(CommandLang.FEATURE_DISABLED, "Kick Counting")); } else { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java index 02fc740f0..f5ab5b430 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java @@ -18,13 +18,11 @@ package com.djrapitops.plan.data.container; import com.djrapitops.plan.data.store.objects.DateHolder; import com.djrapitops.plan.data.store.objects.DateMap; -import com.djrapitops.plan.utilities.SHA256Hash; import com.google.common.base.Objects; import java.io.Serializable; import java.net.Inet6Address; import java.net.InetAddress; -import java.security.NoSuchAlgorithmException; /** * Data class that contains information about IP and Geolocation. @@ -35,18 +33,16 @@ public class GeoInfo implements DateHolder, Serializable { private final String ip; private final String geolocation; - private final String ipHash; private final long date; - public GeoInfo(InetAddress address, String geolocation, long lastUsed) throws NoSuchAlgorithmException { - this(formatIP(address), geolocation, lastUsed, new SHA256Hash(address.getHostAddress()).create()); + public GeoInfo(InetAddress address, String geolocation, long lastUsed) { + this(formatIP(address), geolocation, lastUsed); } - public GeoInfo(String ip, String geolocation, long date, String ipHash) { + public GeoInfo(String ip, String geolocation, long date) { this.ip = ip; this.geolocation = geolocation; this.date = date; - this.ipHash = ipHash; } public static DateMap intoDateMap(Iterable geoInfo) { @@ -106,23 +102,18 @@ public class GeoInfo implements DateHolder, Serializable { return date; } - public String getIpHash() { - return ipHash; - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GeoInfo geoInfo = (GeoInfo) o; return Objects.equal(ip, geoInfo.ip) && - Objects.equal(geolocation, geoInfo.geolocation) && - Objects.equal(ipHash, geoInfo.ipHash); + Objects.equal(geolocation, geoInfo.geolocation); } @Override public int hashCode() { - return Objects.hashCode(ip, geolocation, ipHash); + return Objects.hashCode(ip, geolocation); } @Override @@ -130,7 +121,6 @@ public class GeoInfo implements DateHolder, Serializable { return "GeoInfo{" + "ip='" + ip + '\'' + ", geolocation='" + geolocation + '\'' + - ", ipHash='" + ipHash + '\'' + ", date=" + date + '}'; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java index 8adde4ffc..d4bf8046c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java @@ -92,10 +92,7 @@ public class TableContainer { if (i > maxIndex) { body.append("-"); } else { - Serializable value = row[i]; - Formatter formatter = formatters[i]; - body.append("" : ">"); - body.append(formatter != null ? formatter.apply(value) : (value != null ? value : '-')); + appendValue(body, row[i], formatters[i]); } body.append(""); } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { @@ -105,6 +102,15 @@ public class TableContainer { body.append(""); } + private void appendValue(StringBuilder body, Serializable value, Formatter formatter) { + body.append("" : ">"); + if (formatter != null) { + body.append(formatter.apply(value)); + } else { + body.append(value != null ? value : '-'); + } + } + public final void setColor(String color) { this.color = color; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java index e6f38111d..ce42de3ed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java @@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit; public abstract class AbstractHealthInfo { - protected final String subNote = "
     "; + protected static final String SUB_NOTE = "
     "; protected final List notes; protected final long now; @@ -109,7 +109,7 @@ public abstract class AbstractHealthInfo { StringBuilder remainNote = new StringBuilder(); if (activeFWAGNum != 0) { - remainNote.append(subNote); + remainNote.append(SUB_NOTE); if (percRemain > 0.5) { remainNote.append(Icons.GREEN_THUMB); } else if (percRemain > 0.2) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java index 63b440403..ea79c05a4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java @@ -124,7 +124,7 @@ public class HealthInformation extends AbstractHealthInfo { double aboveThreshold = tpsMutator.percentageTPSAboveThreshold(lowTPSThreshold); long tpsSpikeMonth = analysisContainer.getValue(AnalysisKeys.TPS_SPIKE_MONTH).orElse(0); - StringBuilder avgLowThresholdString = new StringBuilder(subNote); + StringBuilder avgLowThresholdString = new StringBuilder(SUB_NOTE); if (aboveThreshold >= 0.96) { avgLowThresholdString.append(Icons.GREEN_THUMB); } else if (aboveThreshold >= 0.9) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java index 8e4f0f6e5..db50e1b8c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java @@ -112,7 +112,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo { .map(c -> { int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH); Server server = c.getUnsafe(serverKey); - return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + + return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + server.getName() + ": " + playersPerMonth; }).forEach(subNotes::append); addNote(icon + " " + decimalFormatter.apply(average) + uniquePlayersNote + subNotes.toString()); @@ -139,7 +139,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo { .map(c -> { int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH); Server server = c.getUnsafe(serverKey); - return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + + return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + server.getName() + ": " + playersPerMonth; }).forEach(subNotes::append); addNote(icon + " " + decimalFormatter.apply(average) + newPlayersNote + subNotes.toString()); @@ -179,7 +179,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo { .map(c -> { int playersPerMonth = c.getUnsafe(AnalysisKeys.PLAYERS_MONTH); Server server = c.getUnsafe(serverKey); - return subNote + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + + return SUB_NOTE + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + server.getName() + ": " + playersPerMonth; }).forEach(subNotes::append); addNote(icon.toHtml() + " " + uniquePlayersNote + subNotes.toString()); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java b/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java index aead059b5..b5f08b0e9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java @@ -92,7 +92,7 @@ public class H2DB extends SQLDB { String password = config.get(DatabaseSettings.MYSQL_PASS); JdbcDataSource jdbcDataSource = new JdbcDataSource(); - jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL"); + jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL;DATABASE_TO_UPPER=false"); jdbcDataSource.setUser(username); jdbcDataSource.setPassword(password); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java index 51015b4f4..f92e7c793 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java @@ -115,6 +115,10 @@ public abstract class SQLDB extends AbstractDatabase { transactionExecutor.shutdown(); try { Long waitMs = config.getOrDefault(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY, TimeUnit.SECONDS.toMillis(20L)); + if (waitMs > TimeUnit.MINUTES.toMillis(5L)) { + logger.warn(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY.getPath() + " was set to over 5 minutes, using 5 min instead."); + waitMs = TimeUnit.MINUTES.toMillis(5L); + } if (!transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS)) { List unfinished = transactionExecutor.shutdownNow(); int unfinishedCount = unfinished.size(); @@ -149,9 +153,9 @@ public abstract class SQLDB extends AbstractDatabase { new UserInfoOptimizationPatch(), new GeoInfoOptimizationPatch(), new TransferTableRemovalPatch(), - new IPHashPatch(), new IPAnonPatch(), - new BadAFKThresholdValuePatch() + new BadAFKThresholdValuePatch(), + new DeleteIPHashesPatch() }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java index a35f6aa9a..dfbf6c388 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java @@ -27,6 +27,7 @@ import com.djrapitops.plan.db.access.ExecBatchStatement; import com.djrapitops.plan.db.access.ExecStatement; import com.djrapitops.plan.db.access.Executable; import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plugin.utilities.Verify; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -88,7 +89,7 @@ public class DataStoreQueries { * @throws IllegalArgumentException If {@link Session#endSession(long)} has not yet been called. */ public static Executable storeSession(Session session) { - session.getValue(SessionKeys.END).orElseThrow(() -> new IllegalArgumentException("Attempted to save a session that has not ended.")); + Verify.isTrue(session.supports(SessionKeys.END), () -> new IllegalArgumentException("Attempted to save a session that has not ended.")); return connection -> { storeSessionInformation(session).execute(connection); storeSessionKills(session).execute(connection); @@ -164,8 +165,7 @@ public class DataStoreQueries { public void prepare(PreparedStatement statement) throws SQLException { statement.setLong(1, geoInfo.getDate()); statement.setString(2, playerUUID.toString()); - statement.setString(3, geoInfo.getIpHash()); - statement.setString(4, geoInfo.getGeolocation()); + statement.setString(3, geoInfo.getGeolocation()); } }; } @@ -176,9 +176,8 @@ public class DataStoreQueries { public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); statement.setString(2, geoInfo.getIp()); - statement.setString(3, geoInfo.getIpHash()); - statement.setString(4, geoInfo.getGeolocation()); - statement.setLong(5, geoInfo.getDate()); + statement.setString(3, geoInfo.getGeolocation()); + statement.setLong(4, geoInfo.getDate()); } }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java index 2846e1b9c..6014fc86e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java @@ -97,15 +97,13 @@ public class LargeStoreQueries { // Every GeoInfo for (GeoInfo info : playerEntry.getValue()) { String ip = info.getIp(); - String ipHash = info.getIpHash(); String geoLocation = info.getGeolocation(); long lastUsed = info.getDate(); statement.setString(1, playerUUID.toString()); statement.setString(2, ip); - statement.setString(3, ipHash); - statement.setString(4, geoLocation); - statement.setLong(5, lastUsed); + statement.setString(3, geoLocation); + statement.setLong(4, lastUsed); statement.addBatch(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java new file mode 100644 index 000000000..c1594e184 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java @@ -0,0 +1,82 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.db.access.queries.containers; + +import com.djrapitops.plan.data.container.BaseUser; +import com.djrapitops.plan.data.container.GeoInfo; +import com.djrapitops.plan.data.container.Session; +import com.djrapitops.plan.data.store.containers.PlayerContainer; +import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries; +import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries; +import com.djrapitops.plan.db.access.queries.objects.SessionQueries; +import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries; + +import java.util.*; + +/** + * Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table. + * + * @author Rsl1122 + * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For what needs to be included. + */ +public class ServerPlayersTableContainersQuery implements Query> { + + private final UUID serverUUID; + + public ServerPlayersTableContainersQuery(UUID serverUUID) { + this.serverUUID = serverUUID; + } + + @Override + public List executeQuery(SQLDB db) { + List containers = new ArrayList<>(); + + Collection baseUsers = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID)); + + Map> geoInformation = db.query(GeoInfoQueries.fetchServerGeoInformation(serverUUID)); + Map> sessions = db.query(SessionQueries.fetchSessionsOfServer(serverUUID)); + Set bannedUsers = db.query(UserInfoQueries.fetchBannedUUIDsOfServer(serverUUID)); + + for (BaseUser user : baseUsers) { + PlayerContainer container = new PlayerContainer(); + + // BaseUser + UUID uuid = user.getUuid(); + container.putRawData(PlayerKeys.UUID, uuid); + container.putRawData(PlayerKeys.NAME, user.getName()); + container.putRawData(PlayerKeys.REGISTERED, user.getRegistered()); + container.putRawData(PlayerKeys.BANNED, bannedUsers.contains(uuid)); + + // GeoInfo + container.putRawData(PlayerKeys.GEO_INFO, geoInformation.getOrDefault(uuid, new ArrayList<>())); + + container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { + List playerSessions = sessions.getOrDefault(uuid, new ArrayList<>()); + container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add); + return playerSessions; + } + ); + + containers.add(container); + } + return containers; + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java index f5cc2f439..8db220947 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java @@ -49,7 +49,6 @@ public class GeoInfoQueries { GeoInfoTable.IP + ", " + GeoInfoTable.GEOLOCATION + ", " + GeoInfoTable.LAST_USED + ", " + - GeoInfoTable.IP_HASH + ", " + GeoInfoTable.USER_UUID + " FROM " + GeoInfoTable.TABLE_NAME; @@ -64,9 +63,8 @@ public class GeoInfoQueries { String ip = set.getString(GeoInfoTable.IP); String geolocation = set.getString(GeoInfoTable.GEOLOCATION); - String ipHash = set.getString(GeoInfoTable.IP_HASH); long lastUsed = set.getLong(GeoInfoTable.LAST_USED); - userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash)); + userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed)); geoInformation.put(uuid, userGeoInfo); } @@ -97,9 +95,8 @@ public class GeoInfoQueries { while (set.next()) { String ip = set.getString(GeoInfoTable.IP); String geolocation = set.getString(GeoInfoTable.GEOLOCATION); - String ipHash = set.getString(GeoInfoTable.IP_HASH); long lastUsed = set.getLong(GeoInfoTable.LAST_USED); - geoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash)); + geoInfo.add(new GeoInfo(ip, geolocation, lastUsed)); } return geoInfo; } @@ -110,8 +107,7 @@ public class GeoInfoQueries { String sql = "SELECT " + GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + ", " + GeoInfoTable.GEOLOCATION + ", " + GeoInfoTable.LAST_USED + ", " + - GeoInfoTable.IP + ", " + - GeoInfoTable.IP_HASH + + GeoInfoTable.IP + " FROM " + GeoInfoTable.TABLE_NAME + " INNER JOIN " + UserInfoTable.TABLE_NAME + " on " + GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + "." + UserInfoTable.USER_UUID + @@ -132,9 +128,8 @@ public class GeoInfoQueries { String ip = set.getString(GeoInfoTable.IP); String geolocation = set.getString(GeoInfoTable.GEOLOCATION); - String ipHash = set.getString(GeoInfoTable.IP_HASH); long lastUsed = set.getLong(GeoInfoTable.LAST_USED); - userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash)); + userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed)); geoInformation.put(uuid, userGeoInfo); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java index 042ba64d4..8d9ec2836 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java @@ -114,6 +114,12 @@ public class UserInfoQueries { }; } + /** + * Query database for all User information of a specific server. + * + * @param serverUUID UUID of the Plan server. + * @return Map: Player UUID - user information + */ public static Query> fetchUserInformationOfServer(UUID serverUUID) { String sql = SELECT + UserInfoTable.REGISTERED + ", " + @@ -146,4 +152,34 @@ public class UserInfoQueries { } }; } + + /** + * Query database for UUIDs of banned players on a server. + * + * @param serverUUID UUID of the Plan server. + * @return Set: Player UUID of a banned player. + */ + public static Query> fetchBannedUUIDsOfServer(UUID serverUUID) { + String sql = SELECT + + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.SERVER_UUID + "=?" + + AND + UserInfoTable.BANNED + "=?"; + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setBoolean(2, true); + } + + @Override + public Set processResults(ResultSet set) throws SQLException { + Set bannedUsers = new HashSet<>(); + while (set.next()) { + bannedUsers.add(UUID.fromString(set.getString(UserInfoTable.USER_UUID))); + } + return bannedUsers; + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java index 07b5b5055..788890e72 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java @@ -157,4 +157,8 @@ public abstract class Transaction { public String toString() { return getClass().getSimpleName() + (success ? " (finished)" : ""); } + + public boolean wasSuccessful() { + return success; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java index c426cad99..2ca5cdf1a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java @@ -47,6 +47,9 @@ public class RemoveEverythingTransaction extends Transaction { clearTable(ExtensionPlayerValueTable.TABLE_NAME); clearTable(ExtensionServerValueTable.TABLE_NAME); clearTable(ExtensionProviderTable.TABLE_NAME); + clearTable(ExtensionPlayerTableValueTable.TABLE_NAME); + clearTable(ExtensionServerTableValueTable.TABLE_NAME); + clearTable(ExtensionTableProviderTable.TABLE_NAME); clearTable(ExtensionTabTable.TABLE_NAME); clearTable(ExtensionPluginTable.TABLE_NAME); clearTable(ExtensionIconTable.TABLE_NAME); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java index 6718b40ae..35fd01ee1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java @@ -53,5 +53,8 @@ public class CreateTablesTransaction extends OperationCriticalTransaction { execute(ExtensionProviderTable.createTableSQL(dbType)); execute(ExtensionPlayerValueTable.createTableSQL(dbType)); execute(ExtensionServerValueTable.createTableSQL(dbType)); + execute(ExtensionTableProviderTable.createTableSQL(dbType)); + execute(ExtensionPlayerTableValueTable.createTableSQL(dbType)); + execute(ExtensionServerTableValueTable.createTableSQL(dbType)); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java index 46b99d5c4..c86739439 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java @@ -23,7 +23,6 @@ import com.djrapitops.plan.db.access.queries.objects.TPSQueries; import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.db.sql.tables.PingTable; import com.djrapitops.plan.db.sql.tables.TPSTable; -import com.djrapitops.plugin.api.TimeAmount; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -38,11 +37,17 @@ import java.util.UUID; public class RemoveOldSampledDataTransaction extends Transaction { private final UUID serverUUID; + private final long deleteTPSOlderThanMs; + private final long deletePingOlderThanMs; public RemoveOldSampledDataTransaction( - UUID serverUUID + UUID serverUUID, + long deleteTPSOlderThanMs, + long deletePingOlderThanMs ) { this.serverUUID = serverUUID; + this.deleteTPSOlderThanMs = deleteTPSOlderThanMs; + this.deletePingOlderThanMs = deletePingOlderThanMs; } @Override @@ -61,9 +66,7 @@ public class RemoveOldSampledDataTransaction extends Transaction { return new ExecStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { - // More than 3 Months ago. - long threeMonths = TimeAmount.MONTH.toMillis(3L); - statement.setLong(1, System.currentTimeMillis() - threeMonths); + statement.setLong(1, System.currentTimeMillis() - deleteTPSOlderThanMs); statement.setInt(2, allTimePlayerPeak); } }; @@ -77,8 +80,7 @@ public class RemoveOldSampledDataTransaction extends Transaction { return new ExecStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { - long twoWeeks = TimeAmount.WEEK.toMillis(2L); - statement.setLong(1, System.currentTimeMillis() - twoWeeks); + statement.setLong(1, System.currentTimeMillis() - deletePingOlderThanMs); } }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java new file mode 100644 index 000000000..b449a317f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java @@ -0,0 +1,73 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.db.patches; + +import com.djrapitops.plan.db.access.ExecStatement; +import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.db.sql.tables.GeoInfoTable; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Patch for removing ip_hash values from plan_ips table. + *

+ * The patch is a response to a concern: + * "Hashed IP addresses are pseudonymised not anonymised and can be easily decoded using a rainbow table". + * + * @author Rsl1122 + */ +public class DeleteIPHashesPatch extends Patch { + + private static final String IP_HASH = "ip_hash"; + + private boolean hasNoHashColumn; + + @Override + public boolean hasBeenApplied() { + hasNoHashColumn = !hasColumn(GeoInfoTable.TABLE_NAME, IP_HASH); + + String sql = SELECT + "COUNT(1) as c" + FROM + GeoInfoTable.TABLE_NAME + + WHERE + IP_HASH + IS_NOT_NULL; + + return hasNoHashColumn || !query(new HasMoreThanZeroQueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) { + /* No variables needed */ + } + }); + } + + @Override + protected void applyPatch() { + if (hasNoHashColumn) { + return; + } + + String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET ip_hash=?" + WHERE + IP_HASH + IS_NOT_NULL; + execute(new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setNull(1, Types.VARCHAR); + } + }); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java index 7a1eb2cfc..38438a4b3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java @@ -44,13 +44,11 @@ public class GeoInfoOptimizationPatch extends Patch { execute("INSERT INTO " + tableName + " (" + GeoInfoTable.USER_UUID + ", " + GeoInfoTable.IP + ", " + - GeoInfoTable.IP_HASH + ", " + GeoInfoTable.LAST_USED + ", " + GeoInfoTable.GEOLOCATION + ") SELECT " + "(SELECT plan_users.uuid FROM plan_users WHERE plan_users.id = " + tempTableName + ".user_id LIMIT 1), " + GeoInfoTable.IP + ", " + - GeoInfoTable.IP_HASH + ", " + GeoInfoTable.LAST_USED + ", " + GeoInfoTable.GEOLOCATION + " FROM " + tempTableName diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java index 99f910bca..7ca218142 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java @@ -24,7 +24,6 @@ import com.djrapitops.plan.db.sql.tables.GeoInfoTable; import java.net.InetAddress; import java.net.UnknownHostException; -import java.security.NoSuchAlgorithmException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -73,8 +72,7 @@ public class IPAnonPatch extends Patch { private void anonymizeIPs(Map> allGeoInfo) { String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET " + - GeoInfoTable.IP + "=?, " + - GeoInfoTable.IP_HASH + "=? " + + GeoInfoTable.IP + "=? " + "WHERE " + GeoInfoTable.IP + "=?"; execute(new ExecBatchStatement(sql) { @@ -99,11 +97,10 @@ public class IPAnonPatch extends Patch { geoInfo.getDate() ); statement.setString(1, updatedInfo.getIp()); - statement.setString(2, updatedInfo.getIpHash()); - statement.setString(3, geoInfo.getIp()); + statement.setString(2, geoInfo.getIp()); statement.addBatch(); - } catch (UnknownHostException | NoSuchAlgorithmException ignore) { - // This ip is already anonymised or completely unusable. + } catch (UnknownHostException ignore) { + // This ip is completely unusable. } } }); @@ -120,11 +117,11 @@ public class IPAnonPatch extends Patch { String identifiers = hasUserIdColumn ? userIdColumn : "id, uuid"; execute("INSERT INTO plan_ips (" + - identifiers + ", ip, ip_hash, geolocation, last_used" + + identifiers + ", ip, geolocation, last_used" + ") SELECT " + - identifiers + ", ip, ip_hash, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip_hash, " + + identifiers + ", ip, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip, " + (hasUserIdColumn ? userIdColumn : "uuid") + - ", ip, geolocation"); + ", geolocation"); dropTable(tempTableName); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java index 5407e884f..3cb91e5fa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Types; import static com.djrapitops.plan.db.sql.parsing.Sql.*; @@ -54,9 +55,15 @@ public class ExtensionIconTable { } public static void set3IconValuesToStatement(PreparedStatement statement, int parameterIndex, Icon icon) throws SQLException { - statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50)); - statement.setString(parameterIndex + 1, icon.getFamily().name()); - statement.setString(parameterIndex + 2, icon.getColor().name()); + if (icon != null) { + statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50)); + statement.setString(parameterIndex + 1, icon.getFamily().name()); + statement.setString(parameterIndex + 2, icon.getColor().name()); + } else { + statement.setNull(parameterIndex, Types.VARCHAR); + statement.setNull(parameterIndex + 1, Types.VARCHAR); + statement.setNull(parameterIndex + 2, Types.VARCHAR); + } } private ExtensionIconTable() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java new file mode 100644 index 000000000..eb7f4c041 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.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.db.sql.tables; + +import com.djrapitops.plan.db.DBType; +import com.djrapitops.plan.db.sql.parsing.CreateTableParser; +import com.djrapitops.plan.db.sql.parsing.Sql; + +import static com.djrapitops.plan.db.sql.parsing.Sql.INT; + +/** + * Table information about 'plan_extension_user_table_values'. + * + * @author Rsl1122 + */ +public class ExtensionPlayerTableValueTable { + + public static final String TABLE_NAME = "plan_extension_user_table_values"; + + public static final String ID = "id"; + public static final String TABLE_ID = "table_id"; + public static final String USER_UUID = "uuid"; + + // All values can be null + public static final String VALUE_1 = "col_1_value"; + public static final String VALUE_2 = "col_2_value"; + public static final String VALUE_3 = "col_3_value"; + public static final String VALUE_4 = "col_4_value"; + + private ExtensionPlayerTableValueTable() { + /* Static information class */ + } + + public static String createTableSQL(DBType dbType) { + return CreateTableParser.create(TABLE_NAME, dbType) + .column(ID, INT).primaryKey() + .column(USER_UUID, Sql.varchar(36)).notNull() + .column(VALUE_1, Sql.varchar(50)) + .column(VALUE_2, Sql.varchar(50)) + .column(VALUE_3, Sql.varchar(50)) + .column(VALUE_4, Sql.varchar(50)) + .column(TABLE_ID, INT).notNull() + .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID) + .build(); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java index 1f9c08d50..cb14fad5e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java @@ -37,7 +37,7 @@ public class ExtensionProviderTable { public static final String ID = "id"; public static final String PROVIDER_NAME = "name"; - public static final String TEXT = "text"; // Can be null + public static final String TEXT = "text"; public static final String DESCRIPTION = "description"; // Can be null public static final String PRIORITY = "priority"; public static final String GROUPABLE = "groupable"; // default false diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java new file mode 100644 index 000000000..8d28a4817 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java @@ -0,0 +1,62 @@ +/* + * 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.db.sql.tables; + +import com.djrapitops.plan.db.DBType; +import com.djrapitops.plan.db.sql.parsing.CreateTableParser; +import com.djrapitops.plan.db.sql.parsing.Sql; + +import static com.djrapitops.plan.db.sql.parsing.Sql.INT; + +/** + * Table information about 'plan_extension_server_table_values'. + * + * @author Rsl1122 + */ +public class ExtensionServerTableValueTable { + + public static final String TABLE_NAME = "plan_extension_server_table_values"; + + public static final String ID = "id"; + public static final String TABLE_ID = "table_id"; + public static final String SERVER_UUID = "uuid"; + + // All values can be null + public static final String VALUE_1 = "col_1_value"; + public static final String VALUE_2 = "col_2_value"; + public static final String VALUE_3 = "col_3_value"; + public static final String VALUE_4 = "col_4_value"; + public static final String VALUE_5 = "col_5_value"; + + private ExtensionServerTableValueTable() { + /* Static information class */ + } + + public static String createTableSQL(DBType dbType) { + return CreateTableParser.create(TABLE_NAME, dbType) + .column(ID, INT).primaryKey() + .column(SERVER_UUID, Sql.varchar(36)).notNull() + .column(VALUE_1, Sql.varchar(50)) + .column(VALUE_2, Sql.varchar(50)) + .column(VALUE_3, Sql.varchar(50)) + .column(VALUE_4, Sql.varchar(50)) + .column(VALUE_5, Sql.varchar(50)) + .column(TABLE_ID, INT).notNull() + .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID) + .build(); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java new file mode 100644 index 000000000..99ae732e4 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java @@ -0,0 +1,100 @@ +/* + * 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.db.sql.tables; + +import com.djrapitops.plan.db.DBType; +import com.djrapitops.plan.db.sql.parsing.CreateTableParser; +import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.extension.icon.Color; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.UUID; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Table information about 'plan_extension_tables'. + * + * @author Rsl1122 + */ +public class ExtensionTableProviderTable { + + public static final String TABLE_NAME = "plan_extension_tables"; + + public static final String ID = "id"; + public static final String PROVIDER_NAME = "name"; + public static final String COLOR = "color"; + public static final String CONDITION = "condition_name"; // Can be null, related to @Conditional + public static final String PLUGIN_ID = "plugin_id"; + public static final String TAB_ID = "tab_id"; // Can be null, related to @Tab + + // All columns can be null + public static final String COL_1 = "col_1_name"; + public static final String COL_2 = "col_2_name"; + public static final String COL_3 = "col_3_name"; + public static final String COL_4 = "col_4_name"; + public static final String COL_5 = "col_5_name"; + + // All icons can be null + public static final String ICON_1_ID = "icon_1_id"; + public static final String ICON_2_ID = "icon_2_id"; + public static final String ICON_3_ID = "icon_3_id"; + public static final String ICON_4_ID = "icon_4_id"; + public static final String ICON_5_ID = "icon_5_id"; + + public static final String STATEMENT_SELECT_TABLE_ID = "(" + SELECT + ID + FROM + TABLE_NAME + + WHERE + PROVIDER_NAME + "=?" + + AND + PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")"; + + private ExtensionTableProviderTable() { + /* Static information class */ + } + + public static void set3PluginValuesToStatement(PreparedStatement statement, int parameterIndex, String providerName, String pluginName, UUID serverUUID) throws SQLException { + statement.setString(parameterIndex, providerName); + ExtensionPluginTable.set2PluginValuesToStatement(statement, parameterIndex + 1, pluginName, serverUUID); + } + + public static String createTableSQL(DBType dbType) { + return CreateTableParser.create(TABLE_NAME, dbType) + .column(ID, INT).primaryKey() + .column(PROVIDER_NAME, Sql.varchar(50)).notNull() + .column(COLOR, Sql.varchar(25)).notNull().defaultValue("'" + Color.NONE.name() + "'") + .column(CONDITION, Sql.varchar(54)) // 50 + 4 for "not_" + .column(COL_1, Sql.varchar(50)) + .column(COL_2, Sql.varchar(50)) + .column(COL_3, Sql.varchar(50)) + .column(COL_4, Sql.varchar(50)) + .column(COL_5, Sql.varchar(50)) + .column(PLUGIN_ID, INT).notNull() + .column(ICON_1_ID, INT) + .column(ICON_2_ID, INT) + .column(ICON_3_ID, INT) + .column(ICON_4_ID, INT) + .column(ICON_5_ID, INT) + .column(TAB_ID, INT) + .foreignKey(PLUGIN_ID, ExtensionPluginTable.TABLE_NAME, ExtensionPluginTable.ID) + .foreignKey(ICON_1_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID) + .foreignKey(ICON_2_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID) + .foreignKey(ICON_3_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID) + .foreignKey(ICON_4_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID) + .foreignKey(ICON_5_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID) + .foreignKey(TAB_ID, ExtensionTabTable.TABLE_NAME, ExtensionTabTable.ID) + .build(); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java index f45ae84cc..850fc4b64 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java @@ -28,8 +28,8 @@ import com.djrapitops.plan.db.sql.parsing.Sql; * {@link Version10Patch} * {@link GeoInfoLastUsedPatch} * {@link IPAnonPatch} - * {@link IPHashPatch} * {@link GeoInfoOptimizationPatch} + * {@link DeleteIPHashesPatch} * * @author Rsl1122 */ @@ -40,22 +40,19 @@ public class GeoInfoTable { public static final String ID = "id"; public static final String USER_UUID = "uuid"; public static final String IP = "ip"; - public static final String IP_HASH = "ip_hash"; public static final String GEOLOCATION = "geolocation"; public static final String LAST_USED = "last_used"; public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + USER_UUID + ", " + IP + ", " - + IP_HASH + ", " + GEOLOCATION + ", " + LAST_USED - + ") VALUES (?, ?, ?, ?, ?)"; + + ") VALUES (?, ?, ?, ?)"; public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + LAST_USED + "=?" + " WHERE " + USER_UUID + "=?" + - " AND " + IP_HASH + "=?" + " AND " + GEOLOCATION + "=?"; private GeoInfoTable() { @@ -68,7 +65,6 @@ public class GeoInfoTable { .column(USER_UUID, Sql.varchar(36)).notNull() .column(IP, Sql.varchar(39)).notNull() .column(GEOLOCATION, Sql.varchar(50)).notNull() - .column(IP_HASH, Sql.varchar(200)) .column(LAST_USED, Sql.LONG).notNull().defaultValue("0") .toString(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java b/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java index d2253d13f..f2c5fa681 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java @@ -24,7 +24,8 @@ import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransacti import com.djrapitops.plan.db.access.transactions.init.RemoveDuplicateUserInfoTransaction; import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTransaction; import com.djrapitops.plan.db.sql.tables.SessionsTable; -import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.locale.Locale; @@ -84,9 +85,14 @@ public class DBCleanTask extends AbsRunnable { Database database = dbSystem.getDatabase(); try { if (database.getState() != Database.State.CLOSED) { - database.executeTransaction(new RemoveOldSampledDataTransaction(serverInfo.getServerUUID())); + database.executeTransaction(new RemoveOldSampledDataTransaction( + serverInfo.getServerUUID(), + config.get(TimeSettings.DELETE_TPS_DATA_AFTER), + config.get(TimeSettings.DELETE_PING_DATA_AFTER) + )); database.executeTransaction(new RemoveDuplicateUserInfoTransaction()); - database.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction()); + database.executeTransaction(new RemoveUnsatisfiedConditionalPlayerResultsTransaction()); + database.executeTransaction(new RemoveUnsatisfiedConditionalServerResultsTransaction()); int removed = cleanOldPlayers(database); if (removed > 0) { logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed)); @@ -101,7 +107,7 @@ public class DBCleanTask extends AbsRunnable { @VisibleForTesting public int cleanOldPlayers(Database database) { long now = System.currentTimeMillis(); - long keepActiveAfter = now - config.get(TimeSettings.KEEP_INACTIVE_PLAYERS); + long keepActiveAfter = now - config.get(TimeSettings.DELETE_INACTIVE_PLAYERS_AFTER); List inactivePlayers = database.query(fetchInactivePlayerUUIDs(keepActiveAfter)); for (UUID uuid : inactivePlayers) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java index 0ca7d16eb..9a84eeaad 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.extension; +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.data.plugin.PluginsConfigSection; import com.djrapitops.plan.extension.implementation.CallerImplementation; import com.djrapitops.plan.extension.implementation.DataProviderExtractor; @@ -94,11 +95,11 @@ public class ExtensionServiceImplementation implements ExtensionService { logger.warn("DataExtension API implementation mistake for " + pluginName + ": " + warning); } - ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo, logger); + ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo); gatherer.storeExtensionInformation(); extensionGatherers.put(pluginName, gatherer); - updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER); + processing.submitNonCritical(() -> updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER)); logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension registered."); return Optional.of(new CallerImplementation(gatherer, this, processing)); @@ -111,7 +112,6 @@ public class ExtensionServiceImplementation implements ExtensionService { if (extensionGatherers.remove(pluginName) != null) { logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension unregistered."); } - } private boolean shouldNotAllowRegistration(String pluginName) { @@ -150,16 +150,29 @@ public class ExtensionServiceImplementation implements ExtensionService { gatherer.updateValues(playerUUID, playerName); logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed: " + playerName); - } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) { - logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() + - " when updating value for '" + playerName + - "', (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" + - " reason: '" + e.getMessage() + + } catch (DataExtensionMethodCallException methodCallFailed) { + logFailure(playerName, methodCallFailed); + gatherer.disableMethodFromUse(methodCallFailed.getMethod()); + // Try again + updatePlayerValues(gatherer, playerUUID, playerName, event); + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) { + logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError + + " (but failed safely) when updating value for '" + playerName + "', stack trace to follow:"); - errorHandler.log(L.WARN, gatherer.getClass(), e); + errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError); } } + private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) { + Throwable cause = methodCallFailed.getCause(); + String causeName = cause.getClass().getSimpleName(); + logger.warn(methodCallFailed.getPluginName() + " ran into " + causeName + + " (but failed safely) when updating value for '" + playerName + + "', the method was disabled temporarily (won't be called until next Plan reload)" + + ", stack trace to follow:"); + errorHandler.log(L.WARN, getClass(), cause); + } + public void updateServerValues(CallEvents event) { for (ProviderValueGatherer gatherer : extensionGatherers.values()) { updateServerValues(gatherer, event); @@ -176,13 +189,15 @@ public class ExtensionServiceImplementation implements ExtensionService { gatherer.updateValues(); logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed for server"); - } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) { - logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() + - " when updating value for server" + - ", (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" + - " reason: '" + e.getMessage() + - "', stack trace to follow:"); - errorHandler.log(L.WARN, gatherer.getClass(), e); + } catch (DataExtensionMethodCallException methodCallFailed) { + logFailure("server", methodCallFailed); + gatherer.disableMethodFromUse(methodCallFailed.getMethod()); + // Try again + updateServerValues(gatherer, event); + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) { + logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError + + " (but failed safely) when updating value for server, stack trace to follow:"); + errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError); } } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java index e3f526c99..fa05d4516 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java @@ -47,7 +47,7 @@ public class CallerImplementation implements Caller { } @Override - public void updatePlayerData(UUID playerUUID, String playerName) throws IllegalArgumentException { + public void updatePlayerData(UUID playerUUID, String playerName) { Verify.nullCheck(playerUUID, () -> new IllegalArgumentException("'playerUUID' can not be null!")); Verify.nullCheck(playerName, () -> new IllegalArgumentException("'playerName' can not be null!")); processing.submitNonCritical(() -> extensionServiceImplementation.updatePlayerValues(gatherer, playerUUID, playerName, CallEvents.MANUAL)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java index 892248dac..d3aa47fa2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java @@ -124,6 +124,7 @@ public class DataProviderExtractor { extractDataProviders(pluginInfo, tabs, conditions, PercentageProvider.class, PercentageDataProvider::placeToDataProviders); extractDataProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders); extractDataProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders); + extractDataProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders); } private void extractDataProviders(PluginInfo pluginInfo, Map tabs, Map conditions, Class ofKind, DataProviderFactory factory) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java index 4ca642e15..0e446f422 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; import org.apache.commons.lang3.StringUtils; +import java.util.Objects; import java.util.Optional; /** @@ -48,10 +49,38 @@ public class ProviderInformation extends ExtensionDescriptive { } public Optional getTab() { - return tab == null || tab.isEmpty() ? Optional.empty() : Optional.of(StringUtils.truncate(tab, 50)); + return tab == null || tab.isEmpty() + ? Optional.empty() + : Optional.of(StringUtils.truncate(tab, 50)); } public Optional getCondition() { - return condition == null || condition.value().isEmpty() ? Optional.empty() : Optional.of((condition.negated() ? "not_" : "") + StringUtils.truncate(condition.value(), 50)); + if (condition == null || condition.value().isEmpty()) { + return Optional.empty(); + } else if (condition.negated()) { + return Optional.of("not_" + getTruncatedConditionName()); + } else { + return Optional.of(getTruncatedConditionName()); + } + } + + private String getTruncatedConditionName() { + return StringUtils.truncate(condition.value(), 50); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProviderInformation)) return false; + if (!super.equals(o)) return false; + ProviderInformation that = (ProviderInformation) o; + return pluginName.equals(that.pluginName) && + Objects.equals(tab, that.tab) && + Objects.equals(condition, that.condition); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), pluginName, tab, condition); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java index 99ab3cf4b..f0cc6ecba 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.implementation.providers; import com.djrapitops.plan.extension.implementation.MethodType; import java.util.*; +import java.util.stream.Collectors; /** * Group class for handling multiple different types of {@link DataProvider}s. @@ -77,4 +78,24 @@ public class DataProviders { } return byReturnType; } + + public void removeProviderWithMethod(MethodWrapper method) { + MethodType methodType = method.getMethodType(); + Map> byResultType = byMethodType.getOrDefault(methodType, Collections.emptyMap()); + if (byResultType.isEmpty()) { + return; + } + + Class resultType = method.getResultType(); + List providers = byResultType.getOrDefault(resultType, Collections.emptyList()); + if (providers.isEmpty()) { + return; + } + + byResultType.put(resultType, providers.stream() + .filter(provider -> provider.getMethod().equals(method)) + .collect(Collectors.toList()) + ); + byMethodType.put(methodType, byResultType); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java index 4889e80b4..358d8795f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java @@ -20,8 +20,10 @@ import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.Group; import com.djrapitops.plan.extension.implementation.MethodType; +import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Objects; import java.util.UUID; /** @@ -29,7 +31,7 @@ import java.util.UUID; * * @author Rsl1122 */ -public class MethodWrapper { +public class MethodWrapper implements Serializable { private final Method method; private final Class resultType; @@ -41,39 +43,46 @@ public class MethodWrapper { methodType = MethodType.forMethod(this.method); } - public T callMethod(DataExtension extension, UUID playerUUID, String playerName) throws InvocationTargetException, IllegalAccessException { + public T callMethod(DataExtension extension, UUID playerUUID, String playerName) { if (methodType != MethodType.PLAYER_NAME && methodType != MethodType.PLAYER_UUID) { throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method."); } return callMethod(extension, playerUUID, playerName, null); } - public T callMethod(DataExtension extension, Group group) throws InvocationTargetException, IllegalAccessException { + public T callMethod(DataExtension extension, Group group) { if (methodType != MethodType.GROUP) { throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method."); } return callMethod(extension, null, null, group); } - public T callMethod(DataExtension extension) throws InvocationTargetException, IllegalAccessException { + public T callMethod(DataExtension extension) { if (methodType != MethodType.SERVER) { throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not SERVER method."); } return callMethod(extension, null, null, null); } - public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) throws InvocationTargetException, IllegalAccessException { - switch (methodType) { - case SERVER: - return resultType.cast(method.invoke(extension)); - case PLAYER_UUID: - return resultType.cast(method.invoke(extension, playerUUID)); - case PLAYER_NAME: - return resultType.cast(method.invoke(extension, playerName)); - case GROUP: - return resultType.cast(method.invoke(extension, group)); - default: - throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters."); + public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) { + try { + switch (methodType) { + case SERVER: + return resultType.cast(method.invoke(extension)); + case PLAYER_UUID: + return resultType.cast(method.invoke(extension, playerUUID)); + case PLAYER_NAME: + return resultType.cast(method.invoke(extension, playerName)); + case GROUP: + return resultType.cast(method.invoke(extension, group)); + default: + throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters."); + } + } catch (InvocationTargetException | IllegalAccessException e) { + Throwable cause = e.getCause(); + boolean hasCause = cause != null; + throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters; caused " + + (hasCause ? cause.toString() : e.toString())); } } @@ -88,4 +97,19 @@ public class MethodWrapper { public Class getResultType() { return resultType; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MethodWrapper)) return false; + MethodWrapper that = (MethodWrapper) o; + return method.equals(that.method) && + resultType.equals(that.resultType) && + methodType == that.methodType; + } + + @Override + public int hashCode() { + return Objects.hash(method, resultType, methodType); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java new file mode 100644 index 000000000..77150fe6b --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java @@ -0,0 +1,69 @@ +/* + * 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.extension.implementation.providers; + +import com.djrapitops.plan.extension.annotation.Conditional; +import com.djrapitops.plan.extension.annotation.TableProvider; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.implementation.ProviderInformation; +import com.djrapitops.plan.extension.table.Table; + +import java.lang.reflect.Method; + +/** + * Represents a DataExtension API method annotated with {@link com.djrapitops.plan.extension.annotation.TableProvider} annotation. + *

+ * Used to obtain data to place in the database. + *

+ * Please note that not all {@link ProviderInformation} is present. + * + * @author Rsl1122 + */ +public class TableDataProvider extends DataProvider { + + private final Color tableColor; + + private TableDataProvider(ProviderInformation providerInformation, MethodWrapper
methodWrapper, Color tableColor) { + super(providerInformation, methodWrapper); + + this.tableColor = tableColor; + } + + public static void placeToDataProviders( + DataProviders dataProviders, Method method, TableProvider annotation, + Conditional condition, String tab, String pluginName + ) { + MethodWrapper
methodWrapper = new MethodWrapper<>(method, Table.class); + + ProviderInformation providerInformation = new ProviderInformation( + pluginName, method.getName(), null, null, null, 0, tab, condition + ); + + dataProviders.put(new TableDataProvider(providerInformation, methodWrapper, annotation.tableColor())); + } + + public static Color getTableColor(DataProvider
provider) { + if (provider instanceof TableDataProvider) { + return ((TableDataProvider) provider).getTableColor(); + } + return Color.NONE; + } + + public Color getTableColor() { + return tableColor; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java index 2d73be796..be1112abf 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.extension.DataExtension; @@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreBooleanProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerBooleanResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerBooleanResultTransaction; -import com.djrapitops.plugin.logging.console.PluginLogger; import java.util.*; import java.util.concurrent.Callable; @@ -48,20 +48,17 @@ class BooleanProviderValueGatherer { private final Database database; private final DataProviders dataProviders; - private final PluginLogger logger; BooleanProviderValueGatherer( String pluginName, DataExtension extension, UUID serverUUID, Database database, - DataProviders dataProviders, - PluginLogger logger + DataProviders dataProviders ) { this.pluginName = pluginName; this.extension = extension; this.serverUUID = serverUUID; this.database = database; this.dataProviders = dataProviders; - this.logger = logger; } Conditions gatherBooleanDataOfPlayer(UUID playerUUID, String playerName) { @@ -127,10 +124,7 @@ class BooleanProviderValueGatherer { boolean hidden = BooleanDataProvider.isHidden(booleanProvider); MethodWrapper method = booleanProvider.getMethod(); - Boolean result = getMethodResult( - methodCaller.apply(method), - throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString() - ); + Boolean result = getMethodResult(methodCaller.apply(method), method); if (result == null) { // Error during method call satisfied.add(booleanProvider); // Prevents further attempts to call this provider for this player. @@ -155,13 +149,11 @@ class BooleanProviderValueGatherer { return satisfied; } - private T getMethodResult(Callable callable, Function errorMsg) { + private T getMethodResult(Callable callable, MethodWrapper method) { try { return callable.call(); - } catch (Exception | NoSuchFieldError | NoSuchMethodError e) { - logger.warn(errorMsg.apply(e)); - return null; + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); } } - } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java index 6f2ffcab4..3e3c668c5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.extension.DataExtension; @@ -30,7 +31,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.results import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerPercentageResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerDoubleResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerPercentageResultTransaction; -import com.djrapitops.plugin.logging.console.PluginLogger; import java.util.Optional; import java.util.UUID; @@ -51,20 +51,17 @@ class DoubleAndPercentageProviderValueGatherer { private final Database database; private final DataProviders dataProviders; - private final PluginLogger logger; DoubleAndPercentageProviderValueGatherer( String pluginName, DataExtension extension, UUID serverUUID, Database database, - DataProviders dataProviders, - PluginLogger logger + DataProviders dataProviders ) { this.pluginName = pluginName; this.extension = extension; this.serverUUID = serverUUID; this.database = database; this.dataProviders = dataProviders; - this.logger = logger; } void gatherDoubleDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) { @@ -105,10 +102,7 @@ class DoubleAndPercentageProviderValueGatherer { } MethodWrapper method = doubleProvider.getMethod(); - Double result = getMethodResult( - methodCaller.apply(method), - throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString() - ); + Double result = getMethodResult(methodCaller.apply(method), method); if (result == null) { return; } @@ -123,13 +117,11 @@ class DoubleAndPercentageProviderValueGatherer { } } - private T getMethodResult(Callable callable, Function errorMsg) { + private T getMethodResult(Callable callable, MethodWrapper method) { try { return callable.call(); - } catch (Exception | NoSuchFieldError | NoSuchMethodError e) { - logger.warn(errorMsg.apply(e)); - return null; + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); } } - } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java index ef599841d..9fb06efed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.extension.DataExtension; @@ -29,7 +30,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreNumberProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerNumberResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerNumberResultTransaction; -import com.djrapitops.plugin.logging.console.PluginLogger; import java.util.Optional; import java.util.UUID; @@ -50,20 +50,17 @@ class NumberProviderValueGatherer { private final Database database; private final DataProviders dataProviders; - private final PluginLogger logger; NumberProviderValueGatherer( String pluginName, DataExtension extension, UUID serverUUID, Database database, - DataProviders dataProviders, - PluginLogger logger + DataProviders dataProviders ) { this.pluginName = pluginName; this.extension = extension; this.serverUUID = serverUUID; this.database = database; this.dataProviders = dataProviders; - this.logger = logger; } void gatherNumberDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) { @@ -100,10 +97,7 @@ class NumberProviderValueGatherer { } MethodWrapper method = numberProvider.getMethod(); - Long result = getMethodResult( - methodCaller.apply(method), - throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString() - ); + Long result = getMethodResult(methodCaller.apply(method), method); if (result == null) { return; } @@ -115,12 +109,11 @@ class NumberProviderValueGatherer { database.executeTransaction(storeTransactionCreator.apply(method, result)); } - private T getMethodResult(Callable callable, Function errorMsg) { + private T getMethodResult(Callable callable, MethodWrapper method) { try { return callable.call(); - } catch (Exception | NoSuchFieldError | NoSuchMethodError e) { - logger.warn(errorMsg.apply(e)); - return null; + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); } } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java index 02a0e0982..45ecfc273 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java @@ -22,13 +22,14 @@ import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.DataProviderExtractor; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.providers.DataProviders; +import com.djrapitops.plan.extension.implementation.providers.MethodWrapper; import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.StorePluginTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.StoreTabInformationTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveInvalidResultsTransaction; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plugin.logging.console.PluginLogger; import java.util.UUID; @@ -43,43 +44,49 @@ public class ProviderValueGatherer { private final DataProviderExtractor extractor; private final DBSystem dbSystem; private final ServerInfo serverInfo; + + private DataProviders dataProviders; private BooleanProviderValueGatherer booleanGatherer; private NumberProviderValueGatherer numberGatherer; private DoubleAndPercentageProviderValueGatherer doubleAndPercentageGatherer; private StringProviderValueGatherer stringGatherer; + private TableProviderValueGatherer tableGatherer; + public ProviderValueGatherer( DataExtension extension, DataProviderExtractor extractor, DBSystem dbSystem, - ServerInfo serverInfo, - PluginLogger logger + ServerInfo serverInfo ) { this.callEvents = extension.callExtensionMethodsOn(); this.extractor = extractor; this.dbSystem = dbSystem; this.serverInfo = serverInfo; + String pluginName = extractor.getPluginName(); + UUID serverUUID = serverInfo.getServerUUID(); + Database database = dbSystem.getDatabase(); + dataProviders = extractor.getDataProviders(); booleanGatherer = new BooleanProviderValueGatherer( - extractor.getPluginName(), extension, - serverInfo.getServerUUID(), dbSystem.getDatabase(), - extractor.getDataProviders(), logger + pluginName, extension, serverUUID, database, dataProviders ); numberGatherer = new NumberProviderValueGatherer( - extractor.getPluginName(), extension, - serverInfo.getServerUUID(), dbSystem.getDatabase(), - extractor.getDataProviders(), logger + pluginName, extension, serverUUID, database, dataProviders ); doubleAndPercentageGatherer = new DoubleAndPercentageProviderValueGatherer( - extractor.getPluginName(), extension, - serverInfo.getServerUUID(), dbSystem.getDatabase(), - extractor.getDataProviders(), logger + pluginName, extension, serverUUID, database, dataProviders ); stringGatherer = new StringProviderValueGatherer( - extractor.getPluginName(), extension, - serverInfo.getServerUUID(), dbSystem.getDatabase(), - extractor.getDataProviders(), logger + pluginName, extension, serverUUID, database, dataProviders ); + tableGatherer = new TableProviderValueGatherer( + pluginName, extension, serverUUID, database, dataProviders + ); + } + + public void disableMethodFromUse(MethodWrapper method) { + dataProviders.removeProviderWithMethod(method); } public boolean canCallEvent(CallEvents event) { @@ -121,6 +128,7 @@ public class ProviderValueGatherer { numberGatherer.gatherNumberDataOfPlayer(playerUUID, playerName, conditions); doubleAndPercentageGatherer.gatherDoubleDataOfPlayer(playerUUID, playerName, conditions); stringGatherer.gatherStringDataOfPlayer(playerUUID, playerName, conditions); + tableGatherer.gatherTableDataOfPlayer(playerUUID, playerName, conditions); } public void updateValues() { @@ -128,5 +136,6 @@ public class ProviderValueGatherer { numberGatherer.gatherNumberDataOfServer(conditions); doubleAndPercentageGatherer.gatherDoubleDataOfServer(conditions); stringGatherer.gatherStringDataOfServer(conditions); + tableGatherer.gatherTableDataOfServer(conditions); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java index dc5932a6d..74fd5f99a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.transactions.Transaction; import com.djrapitops.plan.extension.DataExtension; @@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreStringProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerStringResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerStringResultTransaction; -import com.djrapitops.plugin.logging.console.PluginLogger; import org.apache.commons.lang3.StringUtils; import java.util.Optional; @@ -50,20 +50,17 @@ class StringProviderValueGatherer { private final Database database; private final DataProviders dataProviders; - private final PluginLogger logger; StringProviderValueGatherer( String pluginName, DataExtension extension, UUID serverUUID, Database database, - DataProviders dataProviders, - PluginLogger logger + DataProviders dataProviders ) { this.pluginName = pluginName; this.extension = extension; this.serverUUID = serverUUID; this.database = database; this.dataProviders = dataProviders; - this.logger = logger; } void gatherStringDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) { @@ -101,10 +98,7 @@ class StringProviderValueGatherer { } MethodWrapper method = stringProvider.getMethod(); - String result = getMethodResult( - methodCaller.apply(method), - throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString() - ); + String result = getMethodResult(methodCaller.apply(method), method); if (result == null) { return; } @@ -116,12 +110,11 @@ class StringProviderValueGatherer { database.executeTransaction(storeTransactionCreator.apply(method, result)); } - private T getMethodResult(Callable callable, Function errorMsg) { + private T getMethodResult(Callable callable, MethodWrapper method) { try { return callable.call(); - } catch (Exception | NoSuchFieldError | NoSuchMethodError e) { - logger.warn(errorMsg.apply(e)); - return null; + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java new file mode 100644 index 000000000..12477548e --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java @@ -0,0 +1,124 @@ +/* + * 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.extension.implementation.providers.gathering; + +import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.extension.DataExtension; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.ProviderInformation; +import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.extension.implementation.providers.DataProviders; +import com.djrapitops.plan.extension.implementation.providers.MethodWrapper; +import com.djrapitops.plan.extension.implementation.providers.TableDataProvider; +import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreTableProviderTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerTableResultTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerTableResultTransaction; +import com.djrapitops.plan.extension.table.Table; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Gathers TableProvider method data. + * + * @author Rsl1122 + */ +class TableProviderValueGatherer { + + private final String pluginName; + private final DataExtension extension; + private final UUID serverUUID; + + private final Database database; + private final DataProviders dataProviders; + + TableProviderValueGatherer( + String pluginName, DataExtension extension, + UUID serverUUID, Database database, + DataProviders dataProviders + ) { + this.pluginName = pluginName; + this.extension = extension; + this.serverUUID = serverUUID; + this.database = database; + this.dataProviders = dataProviders; + } + + void gatherTableDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) { + // Method parameters abstracted away so that same method can be used for all parameter types + // Same with Method result store transaction creation + Function, Callable
> methodCaller = method -> () -> method.callMethod(extension, playerUUID, playerName); + BiFunction, Table, Transaction> storeTransactionCreator = (method, result) -> new StorePlayerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), playerUUID, result); + + for (DataProvider
tableProvider : dataProviders.getPlayerMethodsByType(Table.class)) { + gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider); + } + } + + void gatherTableDataOfServer(Conditions conditions) { + // Method parameters abstracted away so that same method can be used for all parameter types + // Same with Method result store transaction creation + Function, Callable
> methodCaller = method -> () -> method.callMethod(extension); + BiFunction, Table, Transaction> storeTransactionCreator = (method, result) -> new StoreServerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), result); + + for (DataProvider
tableProvider : dataProviders.getServerMethodsByType(Table.class)) { + gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider); + } + } + + private void gatherTableDataOfProvider( + Function, Callable
> methodCaller, + BiFunction, Table, Transaction> storeTransactionCreator, + Conditions conditions, + DataProvider
tableProvider + ) { + ProviderInformation providerInformation = tableProvider.getProviderInformation(); + Optional condition = providerInformation.getCondition(); + if (condition.isPresent() && conditions.isNotFulfilled(condition.get())) { + return; + } + + MethodWrapper
method = tableProvider.getMethod(); + Table result = getMethodResult(methodCaller.apply(method), method); + if (result == null) { + return; + } + + for (Icon icon : result.getIcons()) { + if (icon != null) { + database.executeTransaction(new StoreIconTransaction(icon)); + } + } + database.executeTransaction(new StoreTableProviderTransaction(serverUUID, providerInformation, TableDataProvider.getTableColor(tableProvider), result)); + database.executeTransaction(storeTransactionCreator.apply(method, result)); + } + + private T getMethodResult(Callable callable, MethodWrapper method) { + try { + return callable.call(); + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); + } + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java index 29b0e305a..81e266b9b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java @@ -40,4 +40,8 @@ public class ExtensionDoubleData implements ExtensionData { public String getFormattedValue(Formatter formatter) { return formatter.apply(value); } + + public double getRawValue() { + return value; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java index a4907b7ac..a8e784325 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java @@ -47,4 +47,8 @@ public class ExtensionNumberData implements ExtensionData { public String getFormattedValue(Formatter formatter) { return formatter.apply(value); } + + public long getRawValue() { + return value; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java index 41aaf8e50..b46624713 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java @@ -36,7 +36,10 @@ public class ExtensionTabData implements Comparable { private final Map numberData; private final Map stringData; + private final List tableData; + private List order; + private List descriptives; // Table and Graph data will be added later. @@ -48,6 +51,9 @@ public class ExtensionTabData implements Comparable { percentageData = new HashMap<>(); numberData = new HashMap<>(); stringData = new HashMap<>(); + + tableData = new ArrayList<>(); + descriptives = new ArrayList<>(); } public TabInformation getTabInformation() { @@ -78,6 +84,21 @@ public class ExtensionTabData implements Comparable { return Optional.ofNullable(stringData.get(providerName)); } + public List getTableData() { + return tableData; + } + + /** + * Get all Descriptives for this tabs data. + *

+ * Only available after the Tab has been built. + * + * @return List of {@link ExtensionDescriptive}s. + */ + public List getDescriptives() { + return descriptives; + } + @Override public int compareTo(ExtensionTabData other) { return Integer.compare(this.tabInformation.getTabPriority(), other.tabInformation.getTabPriority()); // Lower is first @@ -103,6 +124,23 @@ public class ExtensionTabData implements Comparable { this.percentageData.putAll(other.percentageData); this.numberData.putAll(other.numberData); this.stringData.putAll(other.stringData); + + this.tableData.addAll(other.tableData); + + createOrderingList(); + } + + private void createOrderingList() { + booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + + order = descriptives.stream().sorted() + .map(ExtensionDescriptive::getName) + .distinct()// Method names are usually different, but in case someone had same method name with different parameters. + .collect(Collectors.toList()); } public static class Factory { @@ -138,22 +176,14 @@ public class ExtensionTabData implements Comparable { return this; } - private void createOrderingList() { - List descriptives = new ArrayList<>(); - data.booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - data.doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - data.percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - data.numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - data.stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - - data.order = descriptives.stream().sorted() - .map(ExtensionDescriptive::getName) - .distinct()// Method names are usually different, but in case someone had same method name with different parameters. - .collect(Collectors.toList()); + public Factory putTableData(ExtensionTableData extensionTableData) { + data.tableData.add(extensionTableData); + return this; } public ExtensionTabData build() { - createOrderingList(); + data.createOrderingList(); + Collections.sort(data.tableData); return data; } } 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 new file mode 100644 index 000000000..99ce47816 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java @@ -0,0 +1,106 @@ +/* + * 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.extension.implementation.results; + +import com.djrapitops.plan.data.element.TableContainer; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.table.Table; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Represents table data from a single TableProvider. + * + * @author Rsl1122 + */ +public class ExtensionTableData implements Comparable { + + private final String providerName; + private final Table table; + private final Color tableColor; + + public ExtensionTableData(String providerName, Table table, Color tableColor) { + this.providerName = providerName; + this.table = table; + this.tableColor = tableColor; + } + + public TableContainer getHtmlTable() { + String[] columns = table.getColumns(); + Icon[] icons = table.getIcons(); + List rows = table.getRows(); + + String[] header = buildHeader(columns, icons); + + TableContainer htmlTable = new TableContainer(header); + if (rows.size() > 50) { + htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows. + } else { + String colorName = com.djrapitops.plan.utilities.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.utilities.html.icon.Color.NONE).getHtmlClass() + .replace("col-", ""); // TODO after PluginData deprecation, change this thing + htmlTable.setColor(colorName); + } + + for (Object[] row : rows) { + htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new)); + } + + return htmlTable; + } + + private String[] buildHeader(String[] columns, Icon[] icons) { + ArrayList header = new ArrayList<>(); + + for (int i = 0; i < columns.length; i++) { + String column = columns[i]; + if (column == null) { + break; + } + header.add(com.djrapitops.plan.utilities.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column); + } + + return header.toArray(new String[0]); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ExtensionTableData)) return false; + ExtensionTableData that = (ExtensionTableData) o; + return providerName.equals(that.providerName) && + tableColor == that.tableColor; + } + + @Override + public int hashCode() { + return Objects.hash(providerName, tableColor); + } + + @Override + public int compareTo(ExtensionTableData other) { + return String.CASE_INSENSITIVE_ORDER.compare(providerName, other.providerName); + } + + public boolean isWideTable() { + return table.getMaxColumnSize() > 3; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java index 527134b8e..dba53f4a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java @@ -19,10 +19,7 @@ package com.djrapitops.plan.extension.implementation.results.player; import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; /** * Represents data of a single extension about a player. @@ -104,6 +101,31 @@ public class ExtensionPlayerData implements Comparable { Collections.sort(data.tabs); return data; } + + public Factory combine(Factory with) { + if (with != null) { + for (ExtensionTabData tab : with.build().getTabs()) { + Optional found = getTab(tab.getTabInformation().getTabName()); + if (found.isPresent()) { + found.get().combine(tab); + } else { + addTab(tab); + } + } + } + return this; + } + + public Optional getTab(String tabName) { + for (ExtensionTabData tab : data.tabs) { + if (tabName.equals(tab.getTabInformation().getTabName())) { + return Optional.of(tab); + } + } + return Optional.empty(); + } + + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java index a71a44393..9ff611889 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java @@ -150,8 +150,8 @@ public class ExtensionAggregateNumbersQuery implements Query> extensionsByServerUUID = db.query(ExtensionInformationQueries.allExtensions()); Map extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID()); + Map tableDataByPluginID = db.query(new ExtensionPlayerTablesQuery(playerUUID)); + combine(extensionDataByPluginID, tableDataByPluginID); + return flatMapByServerUUID(extensionsByServerUUID, extensionDataByPluginID); } + private void combine( + Map extensionDataByPluginID, + Map aggregates + ) { + for (Map.Entry entry : aggregates.entrySet()) { + Integer pluginID = entry.getKey(); + ExtensionPlayerData.Factory data = entry.getValue(); + + ExtensionPlayerData.Factory found = extensionDataByPluginID.get(pluginID); + if (found == null) { + extensionDataByPluginID.put(pluginID, data); + } else { + found.combine(data); + } + } + } + private Map> flatMapByServerUUID(Map> extensionsByServerUUID, Map extensionDataByPluginID) { Map> extensionDataByServerUUID = new HashMap<>(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java new file mode 100644 index 000000000..d106195c8 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java @@ -0,0 +1,287 @@ +/* + * 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.extension.implementation.storage.queries; + +import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.db.access.QueryStatement; +import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; +import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData; +import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.extension.table.TableAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Query player tables from tableprovider table. + *

+ * Returns Map: PluginID - ExtensionPlayerData.Factory. + *

+ * How it is done: + * - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory + * - Table values are queried and merged into the above multimap + * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data + * - (Tab Name can be empty.) + * - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID + *

+ * There are multiple data extraction methods to make extracting the value query easier. + * + * @author Rsl1122 + */ +public class ExtensionPlayerTablesQuery implements Query> { + + private final UUID playerUUID; + + public ExtensionPlayerTablesQuery(UUID playerUUID) { + this.playerUUID = playerUUID; + } + + private static Map flatMapByPluginID(Map> tabDataByPluginID) { + Map dataByPluginID = new HashMap<>(); + for (Map.Entry> entry : tabDataByPluginID.entrySet()) { + Integer pluginID = entry.getKey(); + ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID)); + for (ExtensionTabData.Factory tabData : entry.getValue().values()) { + data.addTab(tabData.build()); + } + dataByPluginID.put(pluginID, data); + } + return dataByPluginID; + } + + @Override + public Map executeQuery(SQLDB db) { + Map> tablesByPluginIDAndTableID = db.query(queryTableProviders()); + Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID)); + + Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues); + return flatMapByPluginID(tabDataByPluginID); + } + + /** + * @param tablesByPluginIDAndTableID {@code >} + * @return {@code } + */ + private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) { + Map> byPluginID = new HashMap<>(); + + for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) { + Integer pluginID = entry.getKey(); + Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>()); + for (Table.Factory table : entry.getValue().values()) { + // Extra Table information + String tableName = TableAccessor.getTableName(table); + Color tableColor = TableAccessor.getColor(table); + + // Extra tab information + String tabName = TableAccessor.getTabName(table); + int tabPriority = TableAccessor.getTabPriority(table); + ElementOrder[] tabOrder = TableAccessor.getTabOrder(table); + Icon tabIcon = TableAccessor.getTabIcon(table); + + ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority))); + tab.putTableData(new ExtensionTableData( + tableName, table.build(), tableColor + )); + byTabName.put(tabName, tab); + } + byPluginID.put(pluginID, byTabName); + } + + return byPluginID; + } + + // Map: > + private Query>> queryTableValues(Map> tables) { + String selectTableValues = SELECT + + ExtensionTableProviderTable.PLUGIN_ID + ',' + + ExtensionPlayerTableValueTable.TABLE_ID + ',' + + ExtensionPlayerTableValueTable.VALUE_1 + ',' + + ExtensionPlayerTableValueTable.VALUE_2 + ',' + + ExtensionPlayerTableValueTable.VALUE_3 + ',' + + ExtensionPlayerTableValueTable.VALUE_4 + + FROM + ExtensionPlayerTableValueTable.TABLE_NAME + + INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionPlayerTableValueTable.TABLE_NAME + '.' + ExtensionPlayerTableValueTable.TABLE_ID + + WHERE + ExtensionPlayerTableValueTable.USER_UUID + "=?"; + + return new QueryStatement>>(selectTableValues, 10000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + while (set.next()) { + Table.Factory table = getTable(set); + if (table == null) { + continue; + } + + Object[] row = extractTableRow(set); + if (row.length > 0) { + table.addRow(row); + } + } + return tables; + } + + private Table.Factory getTable(ResultSet set) throws SQLException { + int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID); + Map byTableID = tables.get(pluginID); + if (byTableID == null) { + return null; + } + int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID); + return byTableID.get(tableID); + } + }; + } + + private Object[] extractTableRow(ResultSet set) throws SQLException { + List row = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + String columnName = "col_" + i + "_value"; // See ExtensionPlayerTableValueTable.VALUE_1 + String value = set.getString(columnName); + if (value == null) { + return row.toArray(new Object[0]); + } + row.add(value); + } + return row.toArray(new Object[0]); + } + + // Map: > + private Query>> queryTableProviders() { + String selectTables = SELECT + + "p1." + ExtensionTableProviderTable.ID + " as table_id," + + "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," + + "p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," + + "p1." + ExtensionTableProviderTable.COLOR + " as table_color," + + ExtensionTableProviderTable.COL_1 + ',' + + ExtensionTableProviderTable.COL_2 + ',' + + ExtensionTableProviderTable.COL_3 + ',' + + ExtensionTableProviderTable.COL_4 + ',' + + "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," + + "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," + + "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," + + "i1." + ExtensionIconTable.ICON_NAME + " as i1_name," + + "i1." + ExtensionIconTable.FAMILY + " as i1_family," + + "i1." + ExtensionIconTable.COLOR + " as i1_color," + + "i2." + ExtensionIconTable.ICON_NAME + " as i2_name," + + "i2." + ExtensionIconTable.FAMILY + " as i2_family," + + "i2." + ExtensionIconTable.COLOR + " as i2_color," + + "i3." + ExtensionIconTable.ICON_NAME + " as i3_name," + + "i3." + ExtensionIconTable.FAMILY + " as i3_family," + + "i3." + ExtensionIconTable.COLOR + " as i3_color," + + "i4." + ExtensionIconTable.ICON_NAME + " as i4_name," + + "i4." + ExtensionIconTable.FAMILY + " as i4_family," + + "i4." + ExtensionIconTable.COLOR + " as i4_color," + + "i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," + + "i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," + + "i6." + ExtensionIconTable.COLOR + " as tab_icon_color" + + FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" + + INNER_JOIN + ExtensionPlayerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionPlayerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID + + LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID + + WHERE + "v1." + ExtensionPlayerTableValueTable.USER_UUID + "=?"; + + return new QueryStatement>>(selectTables, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + Map> byPluginID = new HashMap<>(); + + while (set.next()) { + int pluginID = set.getInt("plugin_id"); + Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>()); + + int tableID = set.getInt("table_id"); + Table.Factory table = Table.builder(); + + extractColumns(set, table); + + TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); + TableAccessor.setTableName(table, set.getString("table_name")); + TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); + TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); + TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); + TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); + + byTableID.put(tableID, table); + byPluginID.put(pluginID, byTableID); + } + + return byPluginID; + } + }; + } + + private void extractColumns(ResultSet set, Table.Factory table) throws SQLException { + String col1 = set.getString(ExtensionTableProviderTable.COL_1); + if (col1 != null) { + table.columnOne(col1, extractIcon(set, "i1")); + } + String col2 = set.getString(ExtensionTableProviderTable.COL_2); + if (col2 != null) { + table.columnTwo(col2, extractIcon(set, "i2")); + } + String col3 = set.getString(ExtensionTableProviderTable.COL_3); + if (col3 != null) { + table.columnThree(col3, extractIcon(set, "i3")); + } + String col4 = set.getString(ExtensionTableProviderTable.COL_4); + if (col4 != null) { + table.columnFour(col4, extractIcon(set, "i4")); + } + } + + private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException { + String iconName = set.getString(iconColumnName + "_name"); + if (iconName == null) { + return null; + } + return new Icon( + Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID), + iconName, + Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE) + ); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java index a855eaa64..a88d91ced 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java @@ -83,6 +83,7 @@ public class ExtensionServerDataQuery implements Query combine(extensionDataByPluginID, db.query(new ExtensionAggregateDoublesQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionAggregateNumbersQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionAggregatePercentagesQuery(serverUUID))); + combine(extensionDataByPluginID, db.query(new ExtensionServerTablesQuery(serverUUID))); return combineWithExtensionInfo(extensionsOfServer, extensionDataByPluginID); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java new file mode 100644 index 000000000..73b000c6c --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java @@ -0,0 +1,151 @@ +/* + * 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.extension.implementation.storage.queries; + +import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.db.access.QueryStatement; +import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.extension.FormatType; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.results.*; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Query Extension data of x most recent players on a server. + *

+ * Returns Map: Player UUID - ExtensionTabData (container for provider based data) + * + * @author Rsl1122 + */ +public class ExtensionServerPlayerDataTableQuery implements Query> { + + private final UUID serverUUID; + private final int xMostRecentPlayers; + + public ExtensionServerPlayerDataTableQuery(UUID serverUUID, int xMostRecentPlayers) { + this.serverUUID = serverUUID; + this.xMostRecentPlayers = xMostRecentPlayers; + } + + @Override + public Map executeQuery(SQLDB db) { + return db.query(fetchIncompletePlayerDataByPluginID()); + } + + private Query> fetchIncompletePlayerDataByPluginID() { + String selectLimitedNumberOfPlayerUUIDsByLastSeenDate = SELECT + + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + + ",MAX(" + SessionsTable.SESSION_END + ") as last_seen" + + FROM + SessionsTable.TABLE_NAME + + GROUP_BY + SessionsTable.USER_UUID + + ORDER_BY + SessionsTable.SESSION_END + " DESC LIMIT ?"; + + String sql = SELECT + + "v1." + ExtensionPlayerValueTable.USER_UUID + " as uuid," + + "v1." + ExtensionPlayerValueTable.DOUBLE_VALUE + " as double_value," + + "v1." + ExtensionPlayerValueTable.LONG_VALUE + " as long_value," + + "v1." + ExtensionPlayerValueTable.STRING_VALUE + " as string_value," + + "p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," + + "p1." + ExtensionProviderTable.TEXT + " as text," + + "p1." + ExtensionProviderTable.FORMAT_TYPE + " as format_type," + + "p1." + ExtensionProviderTable.IS_PLAYER_NAME + " as is_player_name," + + "i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," + + "i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" + + FROM + ExtensionPlayerValueTable.TABLE_NAME + " v1" + + INNER_JOIN + '(' + selectLimitedNumberOfPlayerUUIDsByLastSeenDate + ") as last_seen_q on last_seen_q.uuid=v1." + ExtensionPlayerValueTable.USER_UUID + + INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionPlayerValueTable.PROVIDER_ID + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID + + WHERE + "e1." + ExtensionPluginTable.SERVER_UUID + "=?" + + AND + " v1." + ExtensionPlayerValueTable.BOOLEAN_VALUE + IS_NULL + // Don't select Boolean value rows + AND + " v1." + ExtensionPlayerValueTable.PERCENTAGE_VALUE + IS_NULL + // Don't select Percentage value rows + AND + " p1." + ExtensionProviderTable.IS_PLAYER_NAME + "=?"; + + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, xMostRecentPlayers); // Limit to x most recently seen players + statement.setString(2, serverUUID.toString()); + statement.setBoolean(3, false); // Don't select player_name String values + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + return extractDataByPlayer(set); + } + }; + } + + private Map extractDataByPlayer(ResultSet set) throws SQLException { + Map dataByPlayer = new HashMap<>(); + + while (set.next()) { + UUID playerUUID = UUID.fromString(set.getString("uuid")); + ExtensionTabData.Factory data = dataByPlayer.getOrDefault(playerUUID, new ExtensionTabData.Factory(null)); + + ExtensionDescriptive extensionDescriptive = extractDescriptive(set); + extractAndPutDataTo(data, extensionDescriptive, set); + + dataByPlayer.put(playerUUID, data); + } + return dataByPlayer.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build())); + } + + private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { + double doubleValue = set.getDouble(ExtensionPlayerValueTable.DOUBLE_VALUE); + if (!set.wasNull()) { + extensionTab.putDoubleData(new ExtensionDoubleData(descriptive, doubleValue)); + return; + } + + long numberValue = set.getLong(ExtensionPlayerValueTable.LONG_VALUE); + if (!set.wasNull()) { + FormatType formatType = FormatType.getByName(set.getString(ExtensionProviderTable.FORMAT_TYPE)).orElse(FormatType.NONE); + extensionTab.putNumberData(new ExtensionNumberData(descriptive, formatType, numberValue)); + return; + } + + String stringValue = set.getString(ExtensionPlayerValueTable.STRING_VALUE); + if (stringValue != null) { + boolean isPlayerName = false; + extensionTab.putStringData(new ExtensionStringData(descriptive, isPlayerName, stringValue)); + } + } + + private ExtensionDescriptive extractDescriptive(ResultSet set) throws SQLException { + String name = set.getString("provider_name"); + String text = set.getString(ExtensionProviderTable.TEXT); + + String iconName = set.getString("provider_icon_name"); + Family family = Family.getByName(set.getString("provider_icon_family")).orElse(Family.SOLID); + Icon icon = new Icon(family, iconName, Color.NONE); + + return new ExtensionDescriptive(name, text, null, icon, 0); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java new file mode 100644 index 000000000..047b7b2be --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java @@ -0,0 +1,284 @@ +/* + * 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.extension.implementation.storage.queries; + +import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.db.access.QueryStatement; +import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; +import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.extension.table.TableAccessor; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Query server tables from tableprovider table. + *

+ * Returns Map: PluginID - ExtensionServerData.Factory. + *

+ * How it is done: + * - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory + * - Table values are queried and merged into the above multimap + * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data + * - (Tab Name can be empty.) + * - Multi-map is sorted into ExtensionServerData objects by PluginID, one per ID + *

+ * There are multiple data extraction methods to make extracting the value query easier. + * + * @author Rsl1122 + */ +public class ExtensionServerTablesQuery implements Query> { + + private final UUID serverUUID; + + public ExtensionServerTablesQuery(UUID serverUUID) { + this.serverUUID = serverUUID; + } + + @Override + public Map executeQuery(SQLDB db) { + Map> tablesByPluginIDAndTableID = db.query(queryTableProviders()); + Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID)); + + Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues); + return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + } + + /** + * @param tablesByPluginIDAndTableID {@code >} + * @return {@code } + */ + private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) { + Map> byPluginID = new HashMap<>(); + + for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) { + Integer pluginID = entry.getKey(); + Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>()); + for (Table.Factory table : entry.getValue().values()) { + // Extra Table information + String tableName = TableAccessor.getTableName(table); + Color tableColor = TableAccessor.getColor(table); + + // Extra tab information + String tabName = TableAccessor.getTabName(table); + int tabPriority = TableAccessor.getTabPriority(table); + ElementOrder[] tabOrder = TableAccessor.getTabOrder(table); + Icon tabIcon = TableAccessor.getTabIcon(table); + + ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority))); + tab.putTableData(new ExtensionTableData( + tableName, table.build(), tableColor + )); + byTabName.put(tabName, tab); + } + byPluginID.put(pluginID, byTabName); + } + + return byPluginID; + } + + // Map: > + private Query>> queryTableValues(Map> tables) { + String selectTableValues = SELECT + + ExtensionTableProviderTable.PLUGIN_ID + ',' + + ExtensionServerTableValueTable.TABLE_ID + ',' + + ExtensionServerTableValueTable.VALUE_1 + ',' + + ExtensionServerTableValueTable.VALUE_2 + ',' + + ExtensionServerTableValueTable.VALUE_3 + ',' + + ExtensionServerTableValueTable.VALUE_4 + ',' + + ExtensionServerTableValueTable.VALUE_5 + + FROM + ExtensionServerTableValueTable.TABLE_NAME + + INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionServerTableValueTable.TABLE_NAME + '.' + ExtensionServerTableValueTable.TABLE_ID + + WHERE + ExtensionServerTableValueTable.SERVER_UUID + "=?"; + + return new QueryStatement>>(selectTableValues, 10000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + while (set.next()) { + Table.Factory table = getTable(set); + if (table == null) { + continue; + } + + Object[] row = extractTableRow(set); + if (row.length > 0) { + table.addRow(row); + } + } + return tables; + } + + private Table.Factory getTable(ResultSet set) throws SQLException { + int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID); + Map byTableID = tables.get(pluginID); + if (byTableID == null) { + return null; + } + int tableID = set.getInt(ExtensionServerTableValueTable.TABLE_ID); + return byTableID.get(tableID); + } + }; + } + + private Object[] extractTableRow(ResultSet set) throws SQLException { + List row = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + String columnName = "col_" + i + "_value"; // See ExtensionServerTableValueTable.VALUE_1 + String value = set.getString(columnName); + if (value == null) { + return row.toArray(new Object[0]); + } + row.add(value); + } + return row.toArray(new Object[0]); + } + + // Map: > + private Query>> queryTableProviders() { + String selectTables = SELECT + + "p1." + ExtensionTableProviderTable.ID + " as table_id," + + "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," + + "p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," + + "p1." + ExtensionTableProviderTable.COLOR + " as table_color," + + ExtensionTableProviderTable.COL_1 + ',' + + ExtensionTableProviderTable.COL_2 + ',' + + ExtensionTableProviderTable.COL_3 + ',' + + ExtensionTableProviderTable.COL_4 + ',' + + ExtensionTableProviderTable.COL_5 + ',' + + "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," + + "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," + + "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," + + "i1." + ExtensionIconTable.ICON_NAME + " as i1_name," + + "i1." + ExtensionIconTable.FAMILY + " as i1_family," + + "i1." + ExtensionIconTable.COLOR + " as i1_color," + + "i2." + ExtensionIconTable.ICON_NAME + " as i2_name," + + "i2." + ExtensionIconTable.FAMILY + " as i2_family," + + "i2." + ExtensionIconTable.COLOR + " as i2_color," + + "i3." + ExtensionIconTable.ICON_NAME + " as i3_name," + + "i3." + ExtensionIconTable.FAMILY + " as i3_family," + + "i3." + ExtensionIconTable.COLOR + " as i3_color," + + "i4." + ExtensionIconTable.ICON_NAME + " as i4_name," + + "i4." + ExtensionIconTable.FAMILY + " as i4_family," + + "i4." + ExtensionIconTable.COLOR + " as i4_color," + + "i5." + ExtensionIconTable.ICON_NAME + " as i5_name," + + "i5." + ExtensionIconTable.FAMILY + " as i5_family," + + "i5." + ExtensionIconTable.COLOR + " as i5_color," + + "i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," + + "i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," + + "i6." + ExtensionIconTable.COLOR + " as tab_icon_color" + + FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" + + INNER_JOIN + ExtensionServerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionServerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID + + LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i5 on i5." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_5_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID + + WHERE + "v1." + ExtensionServerTableValueTable.SERVER_UUID + "=?"; + + return new QueryStatement>>(selectTables, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + Map> byPluginID = new HashMap<>(); + + while (set.next()) { + int pluginID = set.getInt("plugin_id"); + Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>()); + + int tableID = set.getInt("table_id"); + Table.Factory table = Table.builder(); + + extractColumns(set, table); + + TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); + TableAccessor.setTableName(table, set.getString("table_name")); + TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); + TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); + TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); + TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); + + byTableID.put(tableID, table); + byPluginID.put(pluginID, byTableID); + } + + return byPluginID; + } + }; + } + + private void extractColumns(ResultSet set, Table.Factory table) throws SQLException { + String col1 = set.getString(ExtensionTableProviderTable.COL_1); + if (col1 != null) { + table.columnOne(col1, extractIcon(set, "i1")); + } + String col2 = set.getString(ExtensionTableProviderTable.COL_2); + if (col2 != null) { + table.columnTwo(col2, extractIcon(set, "i2")); + } + String col3 = set.getString(ExtensionTableProviderTable.COL_3); + if (col3 != null) { + table.columnThree(col3, extractIcon(set, "i3")); + } + String col4 = set.getString(ExtensionTableProviderTable.COL_4); + if (col4 != null) { + table.columnFour(col4, extractIcon(set, "i4")); + } + String col5 = set.getString(ExtensionTableProviderTable.COL_5); + if (col5 != null) { + table.columnFive(col5, extractIcon(set, "i5")); + } + } + + private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException { + String iconName = set.getString(iconColumnName + "_name"); + if (iconName == null) { + return null; + } + return new Icon( + Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID), + iconName, + Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE) + ); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java index 0ad9c8509..a4c44b37d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java @@ -68,14 +68,14 @@ public class StoreTabInformationTransaction extends Transaction { " SET " + ExtensionTabTable.TAB_PRIORITY + "=?," + ExtensionTabTable.ELEMENT_ORDER + "=?," + - ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + + ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + WHERE + ExtensionTabTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + AND + ExtensionTabTable.TAB_NAME + "=?"; return new ExecStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setInt(1, tabInformation.getTabPriority()); - statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null)); + statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values()))); ExtensionIconTable.set3IconValuesToStatement(statement, 3, tabInformation.getTabIcon()); ExtensionPluginTable.set2PluginValuesToStatement(statement, 6, pluginName, serverUUID); statement.setString(8, tabInformation.getTabName()); @@ -88,14 +88,14 @@ public class StoreTabInformationTransaction extends Transaction { ExtensionTabTable.TAB_NAME + "," + ExtensionTabTable.ELEMENT_ORDER + "," + ExtensionTabTable.TAB_PRIORITY + "," + - ExtensionTabTable.ICON_ID + + ExtensionTabTable.ICON_ID + "," + ExtensionTabTable.PLUGIN_ID + ") VALUES (?,?,?," + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")"; return new ExecStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, tabInformation.getTabName()); - statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null)); + statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values()))); statement.setInt(3, tabInformation.getTabPriority()); ExtensionIconTable.set3IconValuesToStatement(statement, 4, tabInformation.getTabIcon()); ExtensionPluginTable.set2PluginValuesToStatement(statement, 7, pluginName, serverUUID); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java new file mode 100644 index 000000000..5b203abe9 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java @@ -0,0 +1,173 @@ +/* + * 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.extension.implementation.storage.transactions.providers; + +import com.djrapitops.plan.db.access.ExecStatement; +import com.djrapitops.plan.db.access.Executable; +import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.ProviderInformation; +import com.djrapitops.plan.extension.table.Table; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +import static com.djrapitops.plan.db.sql.parsing.Sql.AND; +import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable.*; + +/** + * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. + * + * @author Rsl1122 + */ +public class StoreTableProviderTransaction extends Transaction { + + private final UUID serverUUID; + private final ProviderInformation providerInformation; + private final Color tableColor; + private final Table table; + + public StoreTableProviderTransaction(UUID serverUUID, ProviderInformation providerInformation, Color tableColor, Table table) { + this.providerInformation = providerInformation; + this.tableColor = tableColor; + this.table = table; + this.serverUUID = serverUUID; + } + + @Override + protected void performOperations() { + execute(storeProvider()); + } + + private Executable storeProvider() { + return connection -> { + if (!updateProvider().execute(connection)) { + return insertProvider().execute(connection); + } + return false; + }; + } + + private Executable updateProvider() { + String[] columns = table.getColumns(); + Icon[] icons = table.getIcons(); + + String sql = "UPDATE " + TABLE_NAME + " SET " + + COLOR + "=?," + + COL_1 + "=?," + + COL_2 + "=?," + + COL_3 + "=?," + + COL_4 + "=?," + + COL_5 + "=?," + + CONDITION + "=?," + + TAB_ID + '=' + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' + + ICON_1_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ICON_2_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ICON_3_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ICON_4_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ICON_5_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + + WHERE + PROVIDER_NAME + "=?" + + AND + PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, tableColor.name()); + setStringOrNull(statement, 2, columns[0]); + setStringOrNull(statement, 3, columns[1]); + setStringOrNull(statement, 4, columns[2]); + setStringOrNull(statement, 5, columns[3]); + setStringOrNull(statement, 6, columns[4]); + setStringOrNull(statement, 7, providerInformation.getCondition().orElse(null)); + ExtensionTabTable.set3TabValuesToStatement(statement, 8, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID); + ExtensionIconTable.set3IconValuesToStatement(statement, 11, icons[0]); + ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[1]); + ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[2]); + ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[3]); + ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[4]); + statement.setString(26, providerInformation.getName()); + ExtensionPluginTable.set2PluginValuesToStatement(statement, 27, providerInformation.getPluginName(), serverUUID); + } + }; + } + + private Executable insertProvider() { + String[] columns = table.getColumns(); + Icon[] icons = table.getIcons(); + + String sql = "INSERT INTO " + TABLE_NAME + '(' + + PROVIDER_NAME + ',' + + COLOR + ',' + + COL_1 + ',' + + COL_2 + ',' + + COL_3 + ',' + + COL_4 + ',' + + COL_5 + ',' + + CONDITION + ',' + + TAB_ID + ',' + + PLUGIN_ID + ',' + + ICON_1_ID + ',' + + ICON_2_ID + ',' + + ICON_3_ID + ',' + + ICON_4_ID + ',' + + ICON_5_ID + + ") VALUES (?,?,?,?,?,?,?,?," + + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' + + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ',' + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ')'; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, providerInformation.getName()); + statement.setString(2, tableColor.name()); + setStringOrNull(statement, 3, columns[0]); + setStringOrNull(statement, 4, columns[1]); + setStringOrNull(statement, 5, columns[2]); + setStringOrNull(statement, 6, columns[3]); + setStringOrNull(statement, 7, columns[4]); + setStringOrNull(statement, 8, providerInformation.getCondition().orElse(null)); + ExtensionTabTable.set3TabValuesToStatement(statement, 9, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID); + ExtensionPluginTable.set2PluginValuesToStatement(statement, 12, providerInformation.getPluginName(), serverUUID); + ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[0]); + ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[1]); + ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[2]); + ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[3]); + ExtensionIconTable.set3IconValuesToStatement(statement, 26, icons[4]); + } + }; + } + + private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException { + if (value != null) { + statement.setString(index, value); + } else { + statement.setNull(index, Types.VARCHAR); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java index dc0aa5613..4b0913ff7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java @@ -19,9 +19,7 @@ package com.djrapitops.plan.extension.implementation.storage.transactions.result import com.djrapitops.plan.db.access.ExecStatement; import com.djrapitops.plan.db.access.Executable; import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.db.sql.tables.*; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -51,12 +49,17 @@ public class RemoveInvalidResultsTransaction extends Transaction { @Override protected void performOperations() { for (String invalidatedMethod : invalidatedMethods) { - execute(deleteInvalidMethodResults(invalidatedMethod)); + execute(deleteInvalidPlayerMethodResults(invalidatedMethod)); + execute(deleteInvalidServerMethodResults(invalidatedMethod)); execute(deleteInvalidMethodProvider(invalidatedMethod)); + + execute(deleteInvalidPlayerTableResults(invalidatedMethod)); + execute(deleteInvalidServerTableResults(invalidatedMethod)); + execute(deleteInvalidTableProvider(invalidatedMethod)); } } - private Executable deleteInvalidMethodResults(String invalidMethod) { + private Executable deleteInvalidPlayerMethodResults(String invalidMethod) { String sql = "DELETE FROM " + ExtensionPlayerValueTable.TABLE_NAME + WHERE + ExtensionPlayerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID; return new ExecStatement(sql) { @@ -67,10 +70,56 @@ public class RemoveInvalidResultsTransaction extends Transaction { }; } + private Executable deleteInvalidServerMethodResults(String invalidMethod) { + String sql = "DELETE FROM " + ExtensionServerValueTable.TABLE_NAME + + WHERE + ExtensionServerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + ExtensionProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID); + } + }; + } + + private Executable deleteInvalidPlayerTableResults(String invalidMethod) { + String sql = "DELETE FROM " + ExtensionPlayerTableValueTable.TABLE_NAME + + WHERE + ExtensionPlayerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID); + } + }; + } + + private Executable deleteInvalidServerTableResults(String invalidMethod) { + String sql = "DELETE FROM " + ExtensionServerTableValueTable.TABLE_NAME + + WHERE + ExtensionServerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID); + } + }; + } + private Executable deleteInvalidMethodProvider(String invalidMethod) { String sql = "DELETE FROM " + ExtensionProviderTable.TABLE_NAME + WHERE + ExtensionProviderTable.PROVIDER_NAME + "=?" + - AND + ExtensionProviderTable.PLUGIN_ID + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID; + AND + ExtensionProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, invalidMethod); + ExtensionPluginTable.set2PluginValuesToStatement(statement, 2, pluginName, serverUUID); + } + }; + } + + private Executable deleteInvalidTableProvider(String invalidMethod) { + String sql = "DELETE FROM " + ExtensionTableProviderTable.TABLE_NAME + + WHERE + ExtensionTableProviderTable.TABLE_NAME + "=?" + + AND + ExtensionTableProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID; return new ExecStatement(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java similarity index 62% rename from Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java index f1e62ad27..9f729a432 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java @@ -20,8 +20,10 @@ import com.djrapitops.plan.db.DBType; import com.djrapitops.plan.db.access.ExecStatement; import com.djrapitops.plan.db.access.Executable; import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable; import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable; import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -39,37 +41,53 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction { +public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transaction { + + private final String providerTable; + private final String playerValueTable; + private final String playerTableValueTable; + private final String tableTable; + + public RemoveUnsatisfiedConditionalPlayerResultsTransaction() { + providerTable = ExtensionProviderTable.TABLE_NAME; + playerValueTable = ExtensionPlayerValueTable.TABLE_NAME; + tableTable = ExtensionTableProviderTable.TABLE_NAME; + playerTableValueTable = ExtensionPlayerTableValueTable.TABLE_NAME; + } @Override protected void performOperations() { - execute(deleteUnsatisfied()); + String selectSatisfiedConditions = getSatisfiedConditionsSQL(); + + execute(deleteUnsatisfiedValues(selectSatisfiedConditions)); + execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions)); } - private Executable deleteUnsatisfied() { + private String getSatisfiedConditionsSQL() { String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')'; - String providerTable = ExtensionProviderTable.TABLE_NAME; - String playerValueTable = ExtensionPlayerValueTable.TABLE_NAME; - String selectSatisfiedPositiveConditions = SELECT + ExtensionProviderTable.PROVIDED_CONDITION + ',' + - ExtensionPlayerValueTable.USER_UUID + + ExtensionProviderTable.PLUGIN_ID + ',' + + ExtensionPlayerTableValueTable.USER_UUID + FROM + providerTable + INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID + WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" + AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL; String selectSatisfiedNegativeConditions = SELECT + reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' + - ExtensionPlayerValueTable.USER_UUID + + ExtensionProviderTable.PLUGIN_ID + ',' + + ExtensionPlayerTableValueTable.USER_UUID + FROM + providerTable + INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID + WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" + AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL; // Query contents: Set of provided_conditions - String selectSatisfiedConditions = '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1"; + return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1"; + } + private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) { // Query contents: // id | uuid | q1.uuid | condition | q1.provided_condition // -- | ---- | ------- | --------- | --------------------- @@ -87,6 +105,8 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction "=q1." + ExtensionPlayerValueTable.USER_UUID + AND + ExtensionProviderTable.CONDITION + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + + AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID + + "=q1." + ExtensionProviderTable.PLUGIN_ID + ')' + WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition @@ -105,4 +125,32 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction } }; } + + private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) { + String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID + + FROM + tableTable + + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled + " on (" + + tableTable + '.' + ExtensionTableProviderTable.CONDITION + + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + + AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID + + "=q1." + ExtensionProviderTable.PLUGIN_ID + + ')' + + WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query + AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition + + // Nested query here is required because MySQL limits update statements with nested queries: + // The nested query creates a temporary table that bypasses the same table query-update limit. + // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. + String deleteValuesSQL = "DELETE FROM " + playerTableValueTable + + WHERE + ExtensionPlayerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; + + return new ExecStatement(deleteValuesSQL) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setBoolean(1, true); // Select provided conditions with 'true' value + statement.setBoolean(2, false); // Select negated conditions with 'false' value + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java new file mode 100644 index 000000000..e03b36aaa --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java @@ -0,0 +1,150 @@ +/* + * 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.extension.implementation.storage.transactions.results; + +import com.djrapitops.plan.db.DBType; +import com.djrapitops.plan.db.access.ExecStatement; +import com.djrapitops.plan.db.access.Executable; +import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable; +import com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Transaction to remove older results that violate an updated condition value. + *

+ * How it works: + * - Select all fulfilled conditions for all servers (conditionName when true and not_conditionName when false) + * - Left join with server value & provider tables when plugin_ids match, and when condition matches a condition in the + * query above. (plugin_ids can be linked to servers) + * - Filter the join query for values where the condition did not match any provided condition in the join (Is null) + * - Delete all server values with IDs that are returned by the left join query after filtering + * + * @author Rsl1122 + */ +public class RemoveUnsatisfiedConditionalServerResultsTransaction extends Transaction { + + private final String providerTable; + private final String serverValueTable; + private final String serverTableValueTable; + private final String tableTable; + + public RemoveUnsatisfiedConditionalServerResultsTransaction() { + providerTable = ExtensionProviderTable.TABLE_NAME; + serverValueTable = ExtensionServerValueTable.TABLE_NAME; + tableTable = ExtensionTableProviderTable.TABLE_NAME; + serverTableValueTable = ExtensionServerTableValueTable.TABLE_NAME; + } + + @Override + protected void performOperations() { + String selectSatisfiedConditions = getSatisfiedConditionsSQL(); + execute(deleteUnsatisfiedValues(selectSatisfiedConditions)); + execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions)); + } + + private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) { + // Query contents: + // id | provider_id | q1.provider_id | condition | q1.provided_condition + // -- | ----------- | -------------- | --------- | --------------------- + // 1 | ... | ... | A | A Satisfied condition + // 2 | ... | ... | not_B | not_B Satisfied condition + // 3 | ... | ... | NULL | NULL Satisfied condition + // 4 | ... | ... | B | NULL Unsatisfied condition, filtered to these in WHERE clause. + // 5 | ... | ... | not_C | NULL Unsatisfied condition + String selectUnsatisfiedValueIDs = SELECT + serverValueTable + '.' + ExtensionServerValueTable.ID + + FROM + providerTable + + INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID + + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled + " on (" + + ExtensionProviderTable.CONDITION + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + + AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID + "=q1." + ExtensionProviderTable.PLUGIN_ID + + ')' + + WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query + AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition + + // Nested query here is required because MySQL limits update statements with nested queries: + // The nested query creates a temporary table that bypasses the same table query-update limit. + // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. + String sql = "DELETE FROM " + serverValueTable + + WHERE + ExtensionServerValueTable.ID + " IN (" + SELECT + ExtensionServerValueTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setBoolean(1, true); // Select provided conditions with 'true' value + statement.setBoolean(2, false); // Select negated conditions with 'false' value + } + }; + } + + private String getSatisfiedConditionsSQL() { + String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')'; + + String selectSatisfiedPositiveConditions = SELECT + + ExtensionProviderTable.PROVIDED_CONDITION + ',' + + ExtensionProviderTable.PLUGIN_ID + + FROM + providerTable + + INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID + + WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" + + AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL; + String selectSatisfiedNegativeConditions = SELECT + + reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' + + ExtensionProviderTable.PLUGIN_ID + + FROM + providerTable + + INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID + + WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" + + AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL; + + // Query contents: Set of provided_conditions + return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1"; + } + + private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) { + String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID + + FROM + tableTable + + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled + " on (" + + tableTable + '.' + ExtensionTableProviderTable.CONDITION + + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + + AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID + + "=q1." + ExtensionProviderTable.PLUGIN_ID + + ')' + + WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query + AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition + + // Nested query here is required because MySQL limits update statements with nested queries: + // The nested query creates a temporary table that bypasses the same table query-update limit. + // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. + String deleteValuesSQL = "DELETE FROM " + serverTableValueTable + + WHERE + ExtensionServerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; + + return new ExecStatement(deleteValuesSQL) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setBoolean(1, true); // Select provided conditions with 'true' value + statement.setBoolean(2, false); // Select negated conditions with 'false' value + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java new file mode 100644 index 000000000..73f0b6a53 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java @@ -0,0 +1,155 @@ +/* + * 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.extension.implementation.storage.transactions.results; + +import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.db.access.*; +import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.extension.table.Table; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable.*; + +/** + * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. + * + * @author Rsl1122 + */ +public class StorePlayerTableResultTransaction extends Transaction { + + private final String pluginName; + private final UUID serverUUID; + private final String providerName; + private final UUID playerUUID; + + private final Table table; + + public StorePlayerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, UUID playerUUID, Table table) { + this.pluginName = pluginName; + this.serverUUID = serverUUID; + this.providerName = providerName; + this.playerUUID = playerUUID; + this.table = table; + } + + @Override + protected void performOperations() { + execute(storeValue()); + } + + private Executable storeValue() { + return connection -> { + int maxColumnSize = table.getMaxColumnSize(); + if (maxColumnSize == 0) { + return false; + } + + Integer tableID = query(tableID()); + deleteOldValues(tableID).execute(connection); + insertNewValues(tableID).execute(connection); + return false; + }; + } + + private Executable deleteOldValues(int tableID) { + String sql = "DELETE FROM " + TABLE_NAME + + WHERE + TABLE_ID + "=?" + + AND + USER_UUID + "=?"; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, tableID); + statement.setString(2, playerUUID.toString()); + } + }; + } + + private Executable insertNewValues(int tableID) { + String sql = "INSERT INTO " + TABLE_NAME + '(' + + TABLE_ID + ',' + + USER_UUID + ',' + + VALUE_1 + ',' + + VALUE_2 + ',' + + VALUE_3 + ',' + + VALUE_4 + + ") VALUES (?,?,?,?,?,?)"; + + return new ExecBatchStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are. + + for (Object[] row : table.getRows()) { + statement.setInt(1, tableID); + statement.setString(2, playerUUID.toString()); + for (int i = 0; i < maxColumnSize; i++) { + Object value = row[i]; + setStringOrNull(statement, 3 + i, value != null ? value.toString() : null); + } + // Rest are set null if not 4 columns wide. + for (int i = maxColumnSize; i < 4; i++) { + statement.setNull(3 + i, Types.VARCHAR); + } + + statement.addBatch(); + } + } + }; + } + + private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException { + if (value != null) { + statement.setString(index, value); + } else { + statement.setNull(index, Types.VARCHAR); + } + } + + private Query tableID() { + String sql = SELECT + ExtensionTableProviderTable.ID + + FROM + ExtensionTableProviderTable.TABLE_NAME + + WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" + + AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + + " LIMIT 1"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + if (set.next()) { + int id = set.getInt(ExtensionTableProviderTable.ID); + if (!set.wasNull()) { + return id; + } + } + throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java new file mode 100644 index 000000000..ff5211097 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java @@ -0,0 +1,154 @@ +/* + * 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.extension.implementation.storage.transactions.results; + +import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.db.access.*; +import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.extension.table.Table; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable.*; + +/** + * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. + * + * @author Rsl1122 + */ +public class StoreServerTableResultTransaction extends Transaction { + + private final String pluginName; + private final UUID serverUUID; + private final String providerName; + + private final Table table; + + public StoreServerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, Table table) { + this.pluginName = pluginName; + this.serverUUID = serverUUID; + this.providerName = providerName; + this.table = table; + } + + @Override + protected void performOperations() { + execute(storeValue()); + } + + private Executable storeValue() { + return connection -> { + int maxColumnSize = table.getMaxColumnSize(); + if (maxColumnSize == 0) { + return false; + } + + Integer tableID = query(tableID()); + deleteOldValues(tableID).execute(connection); + insertNewValues(tableID).execute(connection); + return false; + }; + } + + private Executable deleteOldValues(int tableID) { + String sql = "DELETE FROM " + TABLE_NAME + + WHERE + TABLE_ID + "=?" + + AND + SERVER_UUID + "=?"; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, tableID); + statement.setString(2, serverUUID.toString()); + } + }; + } + + private Executable insertNewValues(int tableID) { + String sql = "INSERT INTO " + TABLE_NAME + '(' + + TABLE_ID + ',' + + SERVER_UUID + ',' + + VALUE_1 + ',' + + VALUE_2 + ',' + + VALUE_3 + ',' + + VALUE_4 + ',' + + VALUE_5 + + ") VALUES (?,?,?,?,?,?, ?)"; + + return new ExecBatchStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + int maxColumnSize = Math.min(table.getMaxColumnSize(), 5); // Limit to maximum 5 columns, or how many column names there are. + + for (Object[] row : table.getRows()) { + statement.setInt(1, tableID); + statement.setString(2, serverUUID.toString()); + for (int i = 0; i < maxColumnSize; i++) { + Object value = row[i]; + setStringOrNull(statement, 3 + i, value != null ? value.toString() : null); + } + // Rest are set null if not 5 columns wide. + for (int i = maxColumnSize; i < 5; i++) { + statement.setNull(3 + i, Types.VARCHAR); + } + + statement.addBatch(); + } + } + }; + } + + private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException { + if (value != null) { + statement.setString(index, value); + } else { + statement.setNull(index, Types.VARCHAR); + } + } + + private Query tableID() { + String sql = SELECT + ExtensionTableProviderTable.ID + + FROM + ExtensionTableProviderTable.TABLE_NAME + + WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" + + AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + + " LIMIT 1"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + if (set.next()) { + int id = set.getInt(ExtensionTableProviderTable.ID); + if (!set.wasNull()) { + return id; + } + } + throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java new file mode 100644 index 000000000..19fef428f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java @@ -0,0 +1,81 @@ +/* + * 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.extension.table; + +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Icon; + +/** + * Utility for accessing implementation variables inside Table.Factory object. + * + * @author Rsl1122 + */ +public class TableAccessor { + + private TableAccessor() { + /* Static method class */ + } + + public static Color getColor(Table.Factory factory) { + return factory.color; + } + + public static void setColor(Table.Factory factory, Color color) { + factory.color = color; + } + + public static String getTableName(Table.Factory factory) { + return factory.tableName; + } + + public static void setTableName(Table.Factory factory, String tableName) { + factory.tableName = tableName; + } + + public static String getTabName(Table.Factory factory) { + return factory.tabName; + } + + public static void setTabName(Table.Factory factory, String tabName) { + factory.tabName = tabName; + } + + public static int getTabPriority(Table.Factory factory) { + return factory.tabPriority; + } + + public static void setTabPriority(Table.Factory factory, int tabPriority) { + factory.tabPriority = tabPriority; + } + + public static ElementOrder[] getTabOrder(Table.Factory factory) { + return factory.tabOrder; + } + + public static void setTabOrder(Table.Factory factory, ElementOrder[] tabOrder) { + factory.tabOrder = tabOrder; + } + + public static Icon getTabIcon(Table.Factory factory) { + return factory.tabIcon; + } + + public static void setTabIcon(Table.Factory factory, Icon tabIcon) { + factory.tabIcon = tabIcon; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java index b4933f345..a1610fc04 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java @@ -126,11 +126,11 @@ public class PlanSystem implements SubSystem { localeSystem, versionCheckSystem, databaseSystem, - importSystem, - exportSystem, webServerSystem, processing, serverInfo, + importSystem, + exportSystem, infoSystem, cacheSystem, listenerSystem, diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java index 0d875e0ae..47aa56180 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java @@ -18,10 +18,10 @@ package com.djrapitops.plan.system.export; import com.djrapitops.plan.system.SubSystem; import com.djrapitops.plan.system.info.connection.ConnectionSystem; +import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plugin.api.Check; import javax.inject.Inject; import javax.inject.Singleton; @@ -35,6 +35,7 @@ import javax.inject.Singleton; public class ExportSystem implements SubSystem { private final PlanConfig config; + private final ServerInfo serverInfo; private final Processing processing; private final HtmlExport htmlExport; private final ConnectionSystem connectionSystem; @@ -42,11 +43,13 @@ public class ExportSystem implements SubSystem { @Inject public ExportSystem( PlanConfig config, + ServerInfo serverInfo, Processing processing, HtmlExport htmlExport, ConnectionSystem connectionSystem ) { this.config = config; + this.serverInfo = serverInfo; this.processing = processing; this.htmlExport = htmlExport; this.connectionSystem = connectionSystem; @@ -54,7 +57,7 @@ public class ExportSystem implements SubSystem { @Override public void enable() { - if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) { + if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { return; } if (config.isTrue(ExportSettings.JS_AND_CSS)) { @@ -69,7 +72,10 @@ public class ExportSystem implements SubSystem { processing.submitNonCritical(htmlExport::exportAvailablePlayers); } if (config.isTrue(ExportSettings.SERVER_PAGE)) { - processing.submitNonCritical(htmlExport::exportAvailableServerPages); + processing.submitNonCritical(() -> { + htmlExport.cacheNetworkPage(); + htmlExport.exportAvailableServerPages(); + }); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java index 1f6483a08..844955ecb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java @@ -16,7 +16,6 @@ */ package com.djrapitops.plan.system.export; -import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.exceptions.ParseException; import com.djrapitops.plan.api.exceptions.database.DBOpException; import com.djrapitops.plan.data.container.BaseUser; @@ -32,9 +31,13 @@ import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.ExportSettings; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.system.settings.theme.ThemeVal; +import com.djrapitops.plan.system.webserver.cache.PageId; +import com.djrapitops.plan.system.webserver.cache.ResponseCache; +import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; +import com.djrapitops.plan.system.webserver.response.pages.NetworkPageResponse; import com.djrapitops.plan.utilities.html.pages.InspectPage; +import com.djrapitops.plan.utilities.html.pages.NetworkPage; import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.utilities.Verify; @@ -55,7 +58,6 @@ import java.util.*; @Singleton public class HtmlExport extends SpecificExport { - private final PlanPlugin plugin; private final PlanConfig config; private final Theme theme; private final PlanFiles files; @@ -66,18 +68,17 @@ public class HtmlExport extends SpecificExport { @Inject public HtmlExport( - PlanPlugin plugin, PlanFiles files, PlanConfig config, Theme theme, DBSystem dbSystem, PageFactory pageFactory, + JSONFactory jsonFactory, ServerInfo serverInfo, ConnectionSystem connectionSystem, ErrorHandler errorHandler ) { - super(files, serverInfo); - this.plugin = plugin; + super(files, jsonFactory, serverInfo); this.config = config; this.theme = theme; this.files = files; @@ -93,7 +94,7 @@ public class HtmlExport extends SpecificExport { } public void exportServer(UUID serverUUID) { - if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) { + if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { return; } dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)) @@ -122,7 +123,7 @@ public class HtmlExport extends SpecificExport { } public void exportCachedPlayerPage(UUID playerUUID) { - if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) { + if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { return; } @@ -166,6 +167,35 @@ public class HtmlExport extends SpecificExport { } } + public void cacheNetworkPage() { + if (serverInfo.getServer().isNotProxy()) { + return; + } + + NetworkPage networkPage = pageFactory.networkPage(); + ResponseCache.cacheResponse(PageId.SERVER.of(serverInfo.getServerUUID()), () -> { + try { + return new NetworkPageResponse(networkPage); + } catch (ParseException e) { + errorHandler.log(L.WARN, this.getClass(), e); + return null; + } + }); + } + + public void exportNetworkPage() { + if (serverInfo.getServer().isNotProxy()) { + return; + } + + cacheNetworkPage(); + try { + exportAvailableServerPage(serverInfo.getServerUUID(), serverInfo.getServer().getName()); + } catch (IOException e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } + public void exportAvailableServerPages() { try { Map serverNames = dbSystem.getDatabase().query(ServerQueries.fetchServerNames()); @@ -231,15 +261,11 @@ public class HtmlExport extends SpecificExport { public void exportPlugins() { String[] resources = new String[]{ - "web/plugins/bootstrap/css/bootstrap.css", "web/plugins/node-waves/waves.css", "web/plugins/node-waves/waves.js", "web/plugins/animate-css/animate.css", "web/plugins/jquery-slimscroll/jquery.slimscroll.js", "web/plugins/jquery/jquery.min.js", - "web/plugins/bootstrap/js/bootstrap.js", - "web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js", - "web/plugins/jquery-datatable/jquery.dataTables.js", "web/plugins/fullcalendar/fullcalendar.min.js", "web/plugins/fullcalendar/fullcalendar.min.css", "web/plugins/momentjs/moment.js", diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java index 805512eac..04ddf947c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java @@ -24,6 +24,7 @@ import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.ExportSettings; +import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; import com.djrapitops.plan.system.webserver.response.ResponseFactory; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -55,10 +56,11 @@ public class JSONExport extends SpecificExport { PlanConfig config, DBSystem dbSystem, ServerInfo serverInfo, + JSONFactory jsonFactory, ResponseFactory responseFactory, ErrorHandler errorHandler ) { - super(files, serverInfo); + super(files, jsonFactory, serverInfo); this.config = config; this.dbSystem = dbSystem; this.responseFactory = responseFactory; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java index 226541042..15f450a77 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java @@ -20,8 +20,8 @@ import com.djrapitops.plan.system.file.PlanFiles; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; +import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plugin.api.Check; import java.io.File; import java.io.IOException; @@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -41,17 +42,16 @@ import java.util.UUID; public abstract class SpecificExport { private final PlanFiles files; - private final ServerInfo serverInfo; + private final JSONFactory jsonFactory; // Hacky, TODO export needs a rework + protected final ServerInfo serverInfo; - private final boolean usingProxy; - - protected SpecificExport( + SpecificExport( PlanFiles files, - ServerInfo serverInfo + JSONFactory jsonFactory, ServerInfo serverInfo ) { this.files = files; + this.jsonFactory = jsonFactory; this.serverInfo = serverInfo; - usingProxy = Check.isBungeeAvailable() || Check.isVelocityAvailable(); } protected File getFolder() { @@ -78,19 +78,19 @@ public abstract class SpecificExport { Files.write(to.toPath(), lines, StandardCharsets.UTF_8); } - protected File getServerFolder() { + File getServerFolder() { File server = new File(getFolder(), "server"); server.mkdirs(); return server; } - protected File getPlayerFolder() { + File getPlayerFolder() { File player = new File(getFolder(), "player"); player.mkdirs(); return player; } - protected void exportPlayerPage(String playerName, String html) throws IOException { + void exportPlayerPage(String playerName, String html) throws IOException { List lines = Arrays.asList(html.replace("../", "../../").split("\n")); File htmlLocation = new File(getPlayerFolder(), URLEncoder.encode(playerName, "UTF-8").replace(".", "%2E")); @@ -100,7 +100,7 @@ public abstract class SpecificExport { export(exportFile, lines); } - protected void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException { + void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException { Response response = ResponseCache.loadResponse(PageId.PLAYER.of(playerUUID)); if (response == null) { return; @@ -110,7 +110,7 @@ public abstract class SpecificExport { exportPlayerPage(name, html); } - protected void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException { + void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException { Response response = ResponseCache.loadResponse(PageId.SERVER.of(serverUUID)); if (response == null) { @@ -121,19 +121,23 @@ public abstract class SpecificExport { .replace("href=\"plugins/", "href=\"../plugins/") .replace("href=\"css/", "href=\"../css/") .replace("src=\"plugins/", "src=\"../plugins/") - .replace("src=\"js/", "src=\"../js/"); + .replace("src=\"js/", "src=\"../js/") + .replace("../json/players?serverName=" + serverName, "./players_table.json"); File htmlLocation; - if (usingProxy) { + if (serverInfo.getServer().isProxy()) { if (serverUUID.equals(serverInfo.getServerUUID())) { htmlLocation = new File(getFolder(), "network"); } else { htmlLocation = new File(getServerFolder(), URLEncoder.encode(serverName, "UTF-8").replace(".", "%2E")); html = html.replace("../", "../../"); + exportPlayersTableJSON(htmlLocation, serverUUID); } } else { htmlLocation = getServerFolder(); + exportPlayersTableJSON(htmlLocation, serverUUID); } + htmlLocation.mkdirs(); File exportFile = new File(htmlLocation, "index.html"); @@ -141,4 +145,9 @@ public abstract class SpecificExport { export(exportFile, lines); } + + private void exportPlayersTableJSON(File htmlLocation, UUID serverUUID) throws IOException { + File exportFile = new File(htmlLocation, "players_table.json"); + export(exportFile, Collections.singletonList(jsonFactory.serverPlayersTableJSON(serverUUID))); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java index 66b84b71b..a0a2fa502 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java @@ -29,7 +29,6 @@ import com.djrapitops.plan.system.info.request.InfoRequestFactory; import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.logging.console.PluginLogger; import dagger.Lazy; @@ -159,11 +158,8 @@ public abstract class InfoSystem implements SubSystem { * @throws WebException If fails. */ public void requestSetUp(String addressToRequestServer) throws WebException { - if (Check.isBungeeAvailable()) { - throw new BadRequestException("Method not available on Bungee."); - } - if (Check.isVelocityAvailable()) { - throw new BadRequestException("Method not available on Velocity."); + if (serverInfo.getServer().isProxy()) { + throw new BadRequestException("Method not available on a Proxy server."); } Server bungee = new Server(-1, null, "Bungee", addressToRequestServer, -1); String addressOfThisServer = webServer.get().getAccessAddress(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java index 65efe125d..7e5da8f27 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.info.request.InfoRequest; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.pages.PageHandler; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; @@ -30,7 +31,6 @@ import com.djrapitops.plugin.utilities.Verify; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; /** * PageHandler for /info/requestclassname pages. @@ -62,7 +62,7 @@ public class InfoRequestPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { int responseCode = 200; try { @@ -85,7 +85,7 @@ public class InfoRequestPageHandler implements PageHandler { responseCode = getResponseCodeFor(e); throw e; } finally { - connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTarget(), responseCode); + connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTargetString(), responseCode); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java index 2489ddef1..8ce23d583 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java @@ -105,7 +105,10 @@ public class CacheAnalysisPageRequest extends InfoRequestWithVariables implement } if (config.get(ExportSettings.SERVER_PAGE)) { - processing.submitNonCritical(() -> htmlExport.exportServer(serverUUID)); + processing.submitNonCritical(() -> { + htmlExport.exportNetworkPage(); + htmlExport.exportServer(serverUUID); + }); } if (config.get(ExportSettings.SERVER_JSON)) { processing.submitNonCritical(() -> jsonExport.exportServerJSON(serverUUID)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java index 79a22dfd2..2aa60a8b6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java @@ -22,9 +22,9 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.info.connection.ConnectionSystem; import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.webserver.response.DefaultResponses; import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.utilities.Verify; import java.util.Map; @@ -37,9 +37,11 @@ import java.util.UUID; */ public class CheckConnectionRequest extends InfoRequestWithVariables { + private final ServerInfo serverInfo; private final ConnectionSystem connectionSystem; - CheckConnectionRequest(String webServerAddress, ConnectionSystem connectionSystem) { + CheckConnectionRequest(String webServerAddress, ServerInfo serverInfo, ConnectionSystem connectionSystem) { + this.serverInfo = serverInfo; this.connectionSystem = connectionSystem; Verify.nullCheck(webServerAddress, () -> new IllegalArgumentException("webServerAddress can not be null.")); @@ -47,7 +49,8 @@ public class CheckConnectionRequest extends InfoRequestWithVariables { variables.put("continue", "yes"); } - CheckConnectionRequest(ConnectionSystem connectionSystem) { + CheckConnectionRequest(ServerInfo serverInfo, ConnectionSystem connectionSystem) { + this.serverInfo = serverInfo; this.connectionSystem = connectionSystem; } @@ -60,7 +63,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables { public Response handleRequest(Map variables) throws WebException { // Available variables: sender, address - if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) { + if (serverInfo.getServer().isProxy()) { attemptConnection(variables); } @@ -82,7 +85,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables { Server bukkit = new Server(-1, serverUUID, "", address, -1); try { - connectionSystem.sendInfoRequest(new CheckConnectionRequest(connectionSystem), bukkit); + connectionSystem.sendInfoRequest(new CheckConnectionRequest(serverInfo, connectionSystem), bukkit); } catch (ConnectionFailException e) { throw new GatewayException(e.getMessage()); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java index 654d5cf6d..ac6e9f3cd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java @@ -123,15 +123,15 @@ public class InfoRequestFactory { } public SaveDBSettingsRequest saveDBSettingsRequest() { - return new SaveDBSettingsRequest(plugin.get(), config.get(), logger.get(), runnableFactory.get()); + return new SaveDBSettingsRequest(plugin.get(), config.get(), serverInfo.get(), logger.get(), runnableFactory.get()); } public SetupRequest sendDBSettingsRequest(String addressOfThisServer) { - return new SendDBSettingsRequest(addressOfThisServer, this, connectionSystem.get()); + return new SendDBSettingsRequest(addressOfThisServer, serverInfo.get(), this, connectionSystem.get()); } public CheckConnectionRequest checkConnectionRequest(String webAddress) { - return new CheckConnectionRequest(webAddress, connectionSystem.get()); + return new CheckConnectionRequest(webAddress, serverInfo.get(), connectionSystem.get()); } @Singleton @@ -169,7 +169,7 @@ public class InfoRequestFactory { } CheckConnectionRequest checkConnectionRequest() { - return new CheckConnectionRequest(factory.connectionSystem.get()); + return new CheckConnectionRequest(factory.serverInfo.get(), factory.connectionSystem.get()); } GenerateRequest generateAnalysisPageRequest() { @@ -204,6 +204,7 @@ public class InfoRequestFactory { return new SaveDBSettingsRequest( factory.plugin.get(), factory.config.get(), + factory.serverInfo.get(), factory.logger.get(), factory.runnableFactory.get() ); @@ -211,6 +212,7 @@ public class InfoRequestFactory { SetupRequest sendDBSettingsRequest() { return new SendDBSettingsRequest( + factory.serverInfo.get(), factory, factory.connectionSystem.get() ); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java index 73c28af60..9c3f6716d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java @@ -20,13 +20,13 @@ import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.exceptions.connection.BadRequestException; import com.djrapitops.plan.api.exceptions.connection.InternalErrorException; import com.djrapitops.plan.api.exceptions.connection.WebException; +import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.DatabaseSettings; import com.djrapitops.plan.system.settings.paths.PluginSettings; import com.djrapitops.plan.system.webserver.response.DefaultResponses; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.task.AbsRunnable; @@ -46,17 +46,19 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S private final PlanPlugin plugin; private final PlanConfig config; + private final ServerInfo serverInfo; private final PluginLogger logger; private final RunnableFactory runnableFactory; SaveDBSettingsRequest( PlanPlugin plugin, PlanConfig config, - PluginLogger logger, + ServerInfo serverInfo, PluginLogger logger, RunnableFactory runnableFactory ) { this.plugin = plugin; this.config = config; + this.serverInfo = serverInfo; this.logger = logger; this.runnableFactory = runnableFactory; @@ -75,11 +77,8 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S @Override public Response handleRequest(Map variables) throws WebException { - if (Check.isBungeeAvailable()) { - return new BadRequestResponse("Not supposed to be called on a Bungee server"); - } - if (Check.isVelocityAvailable()) { - return new BadRequestResponse("Not supposed to be called on a Velocity server"); + if (serverInfo.getServer().isProxy()) { + return new BadRequestResponse("Not supposed to be called on a proxy server"); } if (config.isFalse(PluginSettings.BUNGEE_COPY_CONFIG)) { return new BadRequestResponse("Bungee config settings overridden on this server."); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java index 168398621..50cd628d2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java @@ -22,10 +22,10 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.info.connection.ConnectionSystem; import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.webserver.response.DefaultResponses; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.utilities.Verify; import java.net.SocketException; @@ -39,20 +39,23 @@ import java.util.UUID; */ public class SendDBSettingsRequest extends InfoRequestWithVariables implements SetupRequest { + private final ServerInfo serverInfo; private final InfoRequestFactory infoRequestFactory; private final ConnectionSystem connectionSystem; SendDBSettingsRequest( - InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem + ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem ) { + this.serverInfo = serverInfo; this.infoRequestFactory = infoRequestFactory; this.connectionSystem = connectionSystem; } SendDBSettingsRequest( String webServerAddress, - InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem + ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem ) { + this.serverInfo = serverInfo; this.infoRequestFactory = infoRequestFactory; this.connectionSystem = connectionSystem; @@ -68,11 +71,8 @@ public class SendDBSettingsRequest extends InfoRequestWithVariables implements S @Override public Response handleRequest(Map variables) throws WebException { // Available variables: sender, address - if (Check.isBukkitAvailable()) { - return new BadRequestResponse("Not supposed to be called on a Bukkit server"); - } - if (Check.isSpongeAvailable()) { - return new BadRequestResponse("Not supposed to be called on a Sponge server"); + if (serverInfo.getServer().isNotProxy()) { + return new BadRequestResponse("Not supposed to be called on a non proxy server"); } String address = variables.get("address"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java index df5a4c518..fc9855c00 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java @@ -27,7 +27,6 @@ import java.util.function.Supplier; */ public abstract class ServerProperties { - private final String id; private final String name; private final int port; private final String version; @@ -38,7 +37,6 @@ public abstract class ServerProperties { private final IntSupplier onlinePlayers; protected ServerProperties( - String id, String name, int port, String version, @@ -47,7 +45,6 @@ public abstract class ServerProperties { int maxPlayers, IntSupplier onlinePlayers ) { - this.id = id; this.name = name; this.port = port; this.version = version; @@ -86,10 +83,6 @@ public abstract class ServerProperties { return maxPlayers; } - public String getServerId() { - return id; - } - public int getOnlinePlayers() { return onlinePlayers.getAsInt(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java index 2a05ffa2b..fd766370d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java @@ -23,23 +23,21 @@ package com.djrapitops.plan.system.locale; */ public enum LangCode { - CUSTOM("Custom"), - EN("English"), - FI("Finnish"), - DE("Deutch"), - FR("French"), - GA("Irish (Gaeilge)"), - CS("Czech"), - PT("Portugese"), - NL("Dutch"), - NO("Norwegian"), - PL("Polish"), - IT("Italian"); + CUSTOM("Custom", ""), + EN("English", "Rsl1122"), + CN("Simplified Chinese", "f0rb1d (佛壁灯) & qsefthuopq"), + DE("Deutch", "Eyremba & fuzzlemann & Morsmorse"), + FI("Finnish", "Rsl1122"), + FR("French", "CyanTech & Aurelien"), + JA("Japanese", "yukieji"), + IT("Italian", "- (Outdated, using English)"); private final String name; + private final String authors; - LangCode(String name) { + LangCode(String name, String authors) { this.name = name; + this.authors = authors; } public static LangCode fromString(String code) { @@ -54,6 +52,10 @@ public enum LangCode { return name; } + public String getAuthors() { + return authors; + } + public String getFileName() { return "locale_" + name() + ".txt"; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java index bed27d08e..0a3c95763 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java @@ -26,7 +26,7 @@ import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; @@ -41,12 +41,26 @@ public class Locale extends HashMap { return forLangCode(LangCode.fromString(code), files); } + private LangCode langCode; + + public Locale() { + this(LangCode.EN); + } + + public Locale(LangCode langCode) { + this.langCode = langCode; + } + public static Locale forLangCode(LangCode code, PlanFiles files) throws IOException { - return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load(); + return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load(code); } public static Locale fromFile(File file) throws IOException { - return new LocaleFileReader(new FileResource(file.getName(), file)).load(); + return new LocaleFileReader(new FileResource(file.getName(), file)).load(LangCode.CUSTOM); + } + + public LangCode getLangCode() { + return langCode; } @Override @@ -75,8 +89,9 @@ public class Locale extends HashMap { return get(key).toArray(values); } - public void loadFromAnotherLocale(Map locale) { + public void loadFromAnotherLocale(Locale locale) { putAll(locale); + this.langCode = locale.langCode; } public String replaceMatchingLanguage(String from) { @@ -111,4 +126,18 @@ public class Locale extends HashMap { } return replaced; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Locale)) return false; + if (!super.equals(o)) return false; + Locale locale = (Locale) o; + return langCode == locale.langCode; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), langCode); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java index 82a0e394f..1738c9e96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java @@ -36,8 +36,8 @@ public class LocaleFileReader { lines = resource.asLines(); } - public Locale load() { - Locale locale = new Locale(); + public Locale load(LangCode code) { + Locale locale = new Locale(code); Map identifiers = LocaleSystem.getIdentifiers(); lines.forEach(line -> { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java index bf8c161b7..02a734c71 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java @@ -106,6 +106,9 @@ public class LocaleSystem implements SubSystem { loaded = loadSettingLocale(); } loaded.ifPresent(locale::loadFromAnotherLocale); + + LangCode langCode = locale.getLangCode(); + logger.info("Locale: '" + langCode.getName() + "' by " + langCode.getAuthors()); } private void writeNewDefaultLocale(File localeFile) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java index a5451a6cc..eb2270d64 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java @@ -27,7 +27,7 @@ public enum ErrorPageLang implements Lang { NOT_PLAYED_404("Player has not played on this server."), UNKNOWN_PAGE_404("Make sure you're accessing a link given by a command, Examples:

/player/PlayerName
/server/ServerName

"), UNAUTHORIZED_401("Unauthorized"), - AUTHENTICATION_FAIlED_401("Authentication Failed."), + AUTHENTICATION_FAILED_401("Authentication Failed."), AUTH_FAIL_TIPS_401("- Ensure you have registered a user with /plan register
- Check that the username and password are correct
- Username and password are case-sensitive

If you have forgotten your password, ask a staff member to delete your old user and re-register."), FORBIDDEN_403("Forbidden"), ACCESS_DENIED_403("Access Denied"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java index 8bc27a971..2c71e1beb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java @@ -35,7 +35,7 @@ public enum PluginLang implements Lang { ENABLE_FAIL_DB("Enable FAIL - Database", "${0}-Database Connection failed: ${1}"), ENABLE_FAIL_WRONG_DB("Enable FAIL - Wrong Database Type", "${0} is not a supported Database"), ENABLE_FAIL_DB_PATCH("Enable FAIL - Database Patch", "Database Patching failed, plugin has to be disabled. Please report this issue"), - ENABLE_FAIL_NO_WEB_SERVER_BUNGEE("Enable FAIL - WebServer (Bungee)", "WebServer did not initialize!"), + ENABLE_FAIL_NO_WEB_SERVER_PROXY("Enable FAIL - WebServer (Bungee)", "WebServer did not initialize!"), ENABLE_FAIL_GEODB_WRITE("Enable FAIL - GeoDB Write", "Something went wrong saving the downloaded GeoLite2 Geolocation database"), WEB_SERVER_FAIL_PORT_BIND("WebServer FAIL - Port Bind", "WebServer was not initialized successfully. Is the port (${0}) in use?"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java index 85e5dea18..ca08796ee 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java @@ -53,7 +53,7 @@ public class Processing implements SubSystem { criticalExecutor = createExecutor(2, "Plan Critical-pool-%d"); } - private ExecutorService createExecutor(int i, String s) { + protected ExecutorService createExecutor(int i, String s) { return Executors.newFixedThreadPool(i, new ThreadFactoryBuilder().setNameFormat(s).build()); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java index 3923d8fe0..32048a335 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java @@ -41,7 +41,7 @@ public interface ConfigChange { } @Override - public void apply(Config config) { + public synchronized void apply(Config config) { if (!config.moveChild(oldPath, newPath)) { throw new IllegalStateException("Failed to move config node from '" + oldPath + "' to '" + newPath + "'"); } @@ -63,7 +63,7 @@ public interface ConfigChange { } @Override - public void apply(Config config) { + public synchronized void apply(Config config) { config.getNode(oldPath).ifPresent(oldNode -> config.addNode(newPath).copyAll(oldNode)); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java index 2b84e08b6..ead1610bb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java @@ -76,6 +76,10 @@ public interface ConfigValueParser { */ String decompose(T ofValue); + static IllegalArgumentException nullInvalidException() { + return new IllegalArgumentException("Null value is not valid for saving"); + } + class StringParser implements ConfigValueParser { @Override public String compose(String fromValue) { @@ -89,7 +93,7 @@ public interface ConfigValueParser { @Override public String decompose(String value) { - Verify.nullCheck(value, () -> new IllegalArgumentException("Null value is not valid for saving")); + Verify.nullCheck(value, ConfigValueParser::nullInvalidException); boolean surroundedByQuotes = value.startsWith("'") || value.endsWith("'"); boolean surroundedByDoubleQuotes = value.startsWith("\"") || value.endsWith("\""); @@ -116,7 +120,7 @@ public interface ConfigValueParser { @Override public String decompose(Integer ofValue) { - Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving")); + Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException); return Integer.toString(ofValue); } } @@ -133,7 +137,7 @@ public interface ConfigValueParser { @Override public String decompose(Long ofValue) { - Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving")); + Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException); return Long.toString(ofValue); } } @@ -146,7 +150,7 @@ public interface ConfigValueParser { @Override public String decompose(Boolean ofValue) { - Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving")); + Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException); return Boolean.toString(ofValue); } } @@ -174,7 +178,7 @@ public interface ConfigValueParser { @Override public String decompose(List ofValue) { - Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving")); + Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException); StringBuilder decomposedString = new StringBuilder(); for (String value : ofValue) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java index 606b8a368..7e8879f2a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java @@ -27,6 +27,7 @@ import javax.inject.Named; import javax.inject.Singleton; import java.io.File; import java.util.List; +import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -134,4 +135,9 @@ public class PlanConfig extends Config { if (o == null) return false; return super.equals(o); } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java index a6ced136e..e5f851c61 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java @@ -27,6 +27,7 @@ import com.djrapitops.plan.system.settings.paths.key.Setting; public class DataGatheringSettings { public static final Setting GEOLOCATIONS = new BooleanSetting("Data_gathering.Geolocations"); + public static final Setting PING = new BooleanSetting("Data_gathering.Ping"); public static final Setting LOG_UNKNOWN_COMMANDS = new BooleanSetting("Data_gathering.Commands.Log_unknown"); public static final Setting COMBINE_COMMAND_ALIASES = new BooleanSetting("Data_gathering.Commands.Log_aliases_as_main_command"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java index 316600799..0eb48a5fc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java @@ -35,7 +35,9 @@ public class TimeSettings { public static final Setting AFK_THRESHOLD = new TimeSetting("Time.Thresholds.AFK_threshold"); public static final Setting ACTIVE_LOGIN_THRESHOLD = new IntegerSetting("Time.Thresholds.Activity_index.Login_threshold", Setting::timeValidator); public static final Setting ACTIVE_PLAY_THRESHOLD = new TimeSetting("Time.Thresholds.Activity_index.Playtime_threshold"); - public static final Setting KEEP_INACTIVE_PLAYERS = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after"); + public static final Setting DELETE_INACTIVE_PLAYERS_AFTER = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after"); + public static final Setting DELETE_TPS_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_time_series_data_after"); + public static final Setting DELETE_PING_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_ping_data_after"); public static final Setting ANALYSIS_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Analysis_refresh_every"); public static final Setting EXTENSION_DATA_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Extension_data_refresh_every"); public static final Setting CLEAN_CACHE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_caches_every"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java index 004be0f64..e0d7a2bd2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.system.webserver.auth.Authentication; import com.sun.net.httpserver.HttpExchange; import java.io.InputStream; +import java.net.URI; import java.util.Optional; /** @@ -32,7 +33,9 @@ import java.util.Optional; */ public class Request { private final String requestMethod; - private final String target; + + private URI requestURI; + private final HttpExchange exchange; private final String remoteAddress; private final Locale locale; @@ -40,7 +43,7 @@ public class Request { public Request(HttpExchange exchange, Locale locale) { this.requestMethod = exchange.getRequestMethod(); - this.target = exchange.getRequestURI().getPath(); + requestURI = exchange.getRequestURI(); remoteAddress = exchange.getRemoteAddress().getAddress().getHostAddress(); @@ -61,8 +64,12 @@ public class Request { return requestMethod; } - public String getTarget() { - return target; + public String getTargetString() { + return requestURI.getPath() + '?' + requestURI.getQuery(); + } + + public RequestTarget getTarget() { + return new RequestTarget(requestURI); } public InputStream getRequestBody() { @@ -71,7 +78,7 @@ public class Request { @Override public String toString() { - return "Request:" + requestMethod + " " + target; + return "Request:" + requestMethod + " " + requestURI.getPath(); } public String getRemoteAddress() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java index 3a69977b6..8d7e99cbc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java @@ -89,6 +89,7 @@ public class RequestHandler implements HttpHandler { public void handle(HttpExchange exchange) { Headers requestHeaders = exchange.getRequestHeaders(); Headers responseHeaders = exchange.getResponseHeaders(); + Request request = new Request(exchange, locale); request.setAuth(getAuthorization(requestHeaders)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java new file mode 100644 index 000000000..e4a5f359a --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java @@ -0,0 +1,88 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.system.webserver; + +import java.net.URI; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Represents URI of a requested resource. + * + * @author Rsl1122 + */ +public class RequestTarget { + + private final String resourceString; + private final List resource; + private final Map parameters; + + public RequestTarget(URI targetURI) { + resourceString = targetURI.getPath(); + resource = Arrays.stream(resourceString.split("/")).filter(part -> !part.isEmpty()).collect(Collectors.toList()); + + parameters = new TreeMap<>(); + parseParameters(targetURI.getQuery()); + } + + private void parseParameters(String parameterString) { + if (parameterString == null || parameterString.isEmpty()) { + return; + } + + String[] keysAndValues = parameterString.split("&"); + for (String kv : keysAndValues) { + if (kv.isEmpty()) { + continue; + } + String[] keyAndValue = kv.split("=", 2); + if (keyAndValue.length >= 2) { + parameters.put(keyAndValue[0], keyAndValue[1]); + } + } + } + + public boolean isEmpty() { + return resource.isEmpty(); + } + + public int size() { + return resource.size(); + } + + public String get(int index) { + return resource.get(index); + } + + public void removeFirst() { + if (!isEmpty()) { + resource.remove(0); + } + } + + public boolean endsWith(String suffix) { + return resourceString.endsWith(suffix); + } + + public Optional getParameter(String key) { + return Optional.ofNullable(parameters.get(key)); + } + + public String getResourceString() { + return resourceString; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java index 9814a9f6e..f36d6ba45 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java @@ -23,6 +23,7 @@ import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; import com.djrapitops.plan.system.webserver.pages.*; +import com.djrapitops.plan.system.webserver.pages.json.RootJSONHandler; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; @@ -32,9 +33,6 @@ import dagger.Lazy; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Optional; /** @@ -50,6 +48,7 @@ public class ResponseHandler extends TreePageHandler { private final PlayerPageHandler playerPageHandler; private final ServerPageHandler serverPageHandler; private final InfoRequestPageHandler infoRequestPageHandler; + private final RootJSONHandler rootJSONHandler; private final ErrorHandler errorHandler; private Lazy webServer; @@ -64,6 +63,7 @@ public class ResponseHandler extends TreePageHandler { PlayerPageHandler playerPageHandler, ServerPageHandler serverPageHandler, InfoRequestPageHandler infoRequestPageHandler, + RootJSONHandler rootJSONHandler, ErrorHandler errorHandler ) { @@ -74,6 +74,7 @@ public class ResponseHandler extends TreePageHandler { this.playerPageHandler = playerPageHandler; this.serverPageHandler = serverPageHandler; this.infoRequestPageHandler = infoRequestPageHandler; + this.rootJSONHandler = rootJSONHandler; this.errorHandler = errorHandler; } @@ -92,16 +93,12 @@ public class ResponseHandler extends TreePageHandler { } registerPage("info", infoRequestPageHandler); + registerPage("json", rootJSONHandler); } public Response getResponse(Request request) { - String targetString = request.getTarget(); - List target = new ArrayList<>(Arrays.asList(targetString.split("/"))); - if (!target.isEmpty()) { - target.remove(0); - } try { - return getResponse(request, targetString, target); + return tryToGetResponse(request); } catch (NoServersException | NotFoundException e) { return responseFactory.notFound404(e.getMessage()); } catch (WebUserAuthException e) { @@ -109,45 +106,44 @@ public class ResponseHandler extends TreePageHandler { } catch (ForbiddenException e) { return responseFactory.forbidden403(e.getMessage()); } catch (BadRequestException e) { - return new BadRequestResponse(e.getMessage()); + return new BadRequestResponse(e.getMessage() + " (when requesting '" + request.getTargetString() + "')"); } catch (UnauthorizedServerException e) { return responseFactory.unauthorizedServer(e.getMessage()); } catch (GatewayException e) { return responseFactory.gatewayError504(e.getMessage()); } catch (InternalErrorException e) { if (e.getCause() != null) { - return responseFactory.internalErrorResponse(e.getCause(), request.getTarget()); + return responseFactory.internalErrorResponse(e.getCause(), request.getTargetString()); } else { - return responseFactory.internalErrorResponse(e, request.getTarget()); + return responseFactory.internalErrorResponse(e, request.getTargetString()); } } catch (Exception e) { errorHandler.log(L.ERROR, this.getClass(), e); - return responseFactory.internalErrorResponse(e, request.getTarget()); + return responseFactory.internalErrorResponse(e, request.getTargetString()); } } - private Response getResponse(Request request, String targetString, List target) throws WebException { - Optional authentication = Optional.empty(); + private Response tryToGetResponse(Request request) throws WebException { + Optional authentication = request.getAuth(); + RequestTarget target = request.getTarget(); + String resource = target.getResourceString(); - if (targetString.endsWith(".css")) { - return ResponseCache.loadResponse(PageId.CSS.of(targetString), () -> responseFactory.cssResponse(targetString)); + if (target.endsWith(".css")) { + return ResponseCache.loadResponse(PageId.CSS.of(resource), () -> responseFactory.cssResponse(resource)); } - if (targetString.endsWith(".js")) { - return ResponseCache.loadResponse(PageId.JS.of(targetString), () -> responseFactory.javaScriptResponse(targetString)); + if (target.endsWith(".js")) { + return ResponseCache.loadResponse(PageId.JS.of(resource), () -> responseFactory.javaScriptResponse(resource)); } - if (targetString.endsWith("favicon.ico")) { + if (target.endsWith("favicon.ico")) { return ResponseCache.loadResponse(PageId.FAVICON.id(), responseFactory::faviconResponse); } boolean isNotInfoRequest = target.isEmpty() || !target.get(0).equals("info"); boolean isAuthRequired = webServer.get().isAuthRequired() && isNotInfoRequest; - if (isAuthRequired) { - authentication = request.getAuth(); - if (!authentication.isPresent()) { - if (webServer.get().isUsingHTTPS()) { - return responseFactory.basicAuth(); - } else { - return responseFactory.forbidden403(); - } + if (isAuthRequired && !authentication.isPresent()) { + if (webServer.get().isUsingHTTPS()) { + return responseFactory.basicAuth(); + } else { + return responseFactory.forbidden403(); } } PageHandler pageHandler = getPageHandler(target); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java index 649fbe9ce..7a66c17a5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.system.webserver; import com.djrapitops.plan.api.exceptions.EnableException; import com.djrapitops.plan.system.SubSystem; import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.info.server.properties.ServerProperties; import com.djrapitops.plan.system.locale.Locale; import com.djrapitops.plan.system.locale.lang.PluginLang; @@ -76,7 +77,7 @@ public class WebServer implements SubSystem { Locale locale, PlanFiles files, PlanConfig config, - ServerProperties serverProperties, + ServerInfo serverInfo, PluginLogger logger, ErrorHandler errorHandler, RequestHandler requestHandler @@ -84,7 +85,7 @@ public class WebServer implements SubSystem { this.locale = locale; this.files = files; this.config = config; - this.serverProperties = serverProperties; + this.serverProperties = serverInfo.getServerProperties(); this.requestHandler = requestHandler; @@ -100,7 +101,7 @@ public class WebServer implements SubSystem { if (!isEnabled()) { if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) { - throw new EnableException(locale.getString(PluginLang.ENABLE_FAIL_NO_WEB_SERVER_BUNGEE)); + throw new EnableException(locale.getString(PluginLang.ENABLE_FAIL_NO_WEB_SERVER_PROXY)); } if (config.isTrue(WebserverSettings.DISABLED)) { logger.warn(locale.getString(PluginLang.ENABLE_NOTIFY_WEB_SERVER_DISABLED)); @@ -116,8 +117,8 @@ public class WebServer implements SubSystem { * Starts up the WebServer in a new Thread Pool. */ private void initServer() { - if (!(Check.isBungeeAvailable() || Check.isVelocityAvailable()) && config.isTrue(WebserverSettings.DISABLED)) { - // Bukkit WebServer has been disabled. + if ((Check.isBukkitAvailable() || Check.isSpongeAvailable()) && config.isTrue(WebserverSettings.DISABLED)) { + // Bukkit/Sponge WebServer has been disabled. return; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java index b1b36cb85..a48c51d68 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java @@ -82,7 +82,9 @@ public class ResponseCache { */ public static void cacheResponse(String identifier, Supplier loader) { Response response = loader.get(); - cache.put(identifier, response); + if (response != null) { + cache.put(identifier, response); + } } /** diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java index 456da2367..181a38a20 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java @@ -19,13 +19,13 @@ package com.djrapitops.plan.system.webserver.pages; import com.djrapitops.plan.api.exceptions.WebUserAuthException; import com.djrapitops.plan.data.WebUser; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; /** * PageHandler for /debug page. @@ -43,12 +43,12 @@ public class DebugPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) { + public Response getResponse(Request request, RequestTarget target) { return responseFactory.debugPageResponse(); } @Override - public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { WebUser webUser = auth.getWebUser(); return webUser.getPermLevel() <= 0; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java index 4c74b4feb..f76cf881a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java @@ -19,11 +19,10 @@ package com.djrapitops.plan.system.webserver.pages; import com.djrapitops.plan.api.exceptions.WebUserAuthException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.response.Response; -import java.util.List; - /** * PageHandlers are used for easier Response management and authorization checking. * @@ -38,9 +37,9 @@ public interface PageHandler { * @param target Rest of the target coordinates after this page has been solved. * @return Response appropriate to the PageHandler. */ - Response getResponse(Request request, List target) throws WebException; + Response getResponse(Request request, RequestTarget target) throws WebException; - default boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + default boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { return true; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java index e5f10e2f7..0d449836e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java @@ -26,6 +26,7 @@ import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.info.InfoSystem; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; @@ -36,7 +37,6 @@ import com.djrapitops.plan.utilities.uuid.UUIDUtility; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; import java.util.UUID; /** @@ -66,7 +66,7 @@ public class PlayerPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { if (target.isEmpty()) { return responseFactory.pageNotFound404(); } @@ -105,11 +105,11 @@ public class PlayerPageHandler implements PageHandler { infoSystem.generateAndCachePlayerPage(uuid); response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid)); } - return response != null ? response : responseFactory.serverNotFound404(); + return response != null ? response : responseFactory.playerNotFound404(); } @Override - public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { WebUser webUser = auth.getWebUser(); return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1)); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java index 64c7acc4c..d1be11287 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java @@ -22,6 +22,7 @@ import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; @@ -30,7 +31,6 @@ import com.djrapitops.plan.system.webserver.response.ResponseFactory; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; /** * PageHandler for /players page. @@ -53,7 +53,7 @@ public class PlayersPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { Database.State dbState = dbSystem.getDatabase().getState(); if (dbState != Database.State.OPEN) { throw new ForbiddenException("Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info"); @@ -62,7 +62,7 @@ public class PlayersPageHandler implements PageHandler { } @Override - public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { return auth.getWebUser().getPermLevel() <= 1; } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java index 0391d2c72..47052fa27 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java @@ -19,12 +19,12 @@ package com.djrapitops.plan.system.webserver.pages; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.data.WebUser; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.response.RedirectResponse; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import java.util.List; import java.util.Optional; /** @@ -43,7 +43,7 @@ public class RootPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { Optional auth = request.getAuth(); if (!auth.isPresent()) { return responseFactory.basicAuth(); @@ -65,7 +65,7 @@ public class RootPageHandler implements PageHandler { } @Override - public boolean isAuthorized(Authentication auth, List target) { + public boolean isAuthorized(Authentication auth, RequestTarget target) { return true; } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java index 5bc1d0045..b947bdbf1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java @@ -29,16 +29,15 @@ import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.processing.Processing; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plugin.api.Check; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -72,7 +71,7 @@ public class ServerPageHandler implements PageHandler { } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { UUID serverUUID = getServerUUID(target); boolean raw = target.size() >= 2 && target.get(1).equalsIgnoreCase("raw"); @@ -87,7 +86,7 @@ public class ServerPageHandler implements PageHandler { return response; } else { checkDBState(); - if ((Check.isBungeeAvailable() || Check.isVelocityAvailable()) && serverInfo.getServerUUID().equals(serverUUID)) { + if (serverInfo.getServer().isProxy() && serverInfo.getServerUUID().equals(serverUUID)) { return ResponseCache.loadResponse(PageId.SERVER.of(serverUUID), responseFactory::networkPageResponse); } return refreshNow(serverUUID); @@ -115,7 +114,7 @@ public class ServerPageHandler implements PageHandler { return responseFactory.refreshingAnalysisResponse(); } - private UUID getServerUUID(List target) { + private UUID getServerUUID(RequestTarget target) { // Default to current server's page UUID serverUUID = serverInfo.getServerUUID(); @@ -136,7 +135,7 @@ public class ServerPageHandler implements PageHandler { } @Override - public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { return auth.getWebUser().getPermLevel() <= 0; } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java index 3f2bd655a..f082456ba 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java @@ -19,12 +19,12 @@ package com.djrapitops.plan.system.webserver.pages; import com.djrapitops.plan.api.exceptions.WebUserAuthException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; import com.djrapitops.plan.system.webserver.response.Response; import com.djrapitops.plan.system.webserver.response.ResponseFactory; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -50,31 +50,31 @@ public abstract class TreePageHandler implements PageHandler { public void registerPage(String targetPage, Response response, int requiredPerm) { pages.put(targetPage, new PageHandler() { @Override - public Response getResponse(Request request, List target) { + public Response getResponse(Request request, RequestTarget target) { return response; } @Override - public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { return auth.getWebUser().getPermLevel() <= requiredPerm; } }); } @Override - public Response getResponse(Request request, List target) throws WebException { + public Response getResponse(Request request, RequestTarget target) throws WebException { PageHandler pageHandler = getPageHandler(target); return pageHandler != null ? pageHandler.getResponse(request, target) : responseFactory.pageNotFound404(); } - public PageHandler getPageHandler(List target) { + public PageHandler getPageHandler(RequestTarget target) { if (target.isEmpty()) { return pages.get(""); } String targetPage = target.get(0); - target.remove(0); + target.removeFirst(); return pages.get(targetPage); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java new file mode 100644 index 000000000..f42363709 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java @@ -0,0 +1,71 @@ +/* + * 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.system.webserver.pages.json; + +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.queries.containers.ServerPlayersTableContainersQuery; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerPlayerDataTableQuery; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.DisplaySettings; +import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +/** + * Factory with different JSON parsing placed to a single class. + * + * @author Rsl1122 + */ +@Singleton +public class JSONFactory { + + private final PlanConfig config; + private final DBSystem dbSystem; + private final Formatters formatters; + + @Inject + public JSONFactory( + PlanConfig config, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + this.formatters = formatters; + } + + public String serverPlayersTableJSON(UUID serverUUID) { + Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE); + Integer loginThreshold = config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + Boolean openPlayerLinksInNewTab = config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB); + + Database database = dbSystem.getDatabase(); + + return new PlayersTableJSONParser( + database.query(new ServerPlayersTableContainersQuery(serverUUID)), + database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)), + xMostRecentPlayers, playtimeThreshold, loginThreshold, openPlayerLinksInNewTab, + formatters + ).toJSONString(); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java new file mode 100644 index 000000000..c426605db --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java @@ -0,0 +1,93 @@ +/* + * 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.system.webserver.pages.json; + +import com.djrapitops.plan.api.exceptions.WebUserAuthException; +import com.djrapitops.plan.api.exceptions.connection.BadRequestException; +import com.djrapitops.plan.api.exceptions.connection.WebException; +import com.djrapitops.plan.db.access.queries.objects.ServerQueries; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; +import com.djrapitops.plan.system.webserver.auth.Authentication; +import com.djrapitops.plan.system.webserver.pages.PageHandler; +import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.data.JSONResponse; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; +import java.util.UUID; + +/** + * JSON handler for different Player table JSON requests. + * + * @author Rsl1122 + * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For JSON parsing of /server players table. + */ +@Singleton +public class PlayersTableJSONHandler implements PageHandler { + + private final DBSystem dbSystem; + private final JSONFactory jsonFactory; + + @Inject + public PlayersTableJSONHandler( + DBSystem dbSystem, + JSONFactory jsonFactory + ) { + this.jsonFactory = jsonFactory; + this.dbSystem = dbSystem; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID serverUUID = getServerUUID(target); // Can throw BadRequestException + return new JSONResponse(jsonFactory.serverPlayersTableJSON(serverUUID)); + } + + private UUID getServerUUID(RequestTarget target) throws BadRequestException { + Optional serverUUID = target.getParameter("serverUUID"); + if (serverUUID.isPresent()) { + return getServerUUIDDirectly(serverUUID.get()); + } else { + return getServerUUIDFromName(target); // Preferred + } + } + + private UUID getServerUUIDFromName(RequestTarget target) throws BadRequestException { + String serverName = target.getParameter("serverName") + .orElseThrow(() -> new BadRequestException("'serverName' parameter was not defined.")); + return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverName)) + .map(Server::getUuid) + .orElseThrow(() -> new BadRequestException("'serverName' was not found in the database.: '" + serverName + "'")); + } + + private UUID getServerUUIDDirectly(String serverUUIDString) throws BadRequestException { + try { + return UUID.fromString(serverUUIDString); + } catch (IllegalArgumentException malformedUUIDException) { + throw new BadRequestException("'serverName' was not a valid UUID: " + malformedUUIDException.getMessage()); + } + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java similarity index 50% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java rename to Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java index 562baebcd..b5690a138 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java @@ -14,38 +14,37 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.pluginbridge.plan.mcmmo; +package com.djrapitops.plan.system.webserver.pages.json; -import com.djrapitops.plan.data.plugin.HookHandler; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.pluginbridge.plan.Hook; +import com.djrapitops.plan.api.exceptions.WebUserAuthException; +import com.djrapitops.plan.system.webserver.RequestTarget; +import com.djrapitops.plan.system.webserver.auth.Authentication; +import com.djrapitops.plan.system.webserver.pages.TreePageHandler; +import com.djrapitops.plan.system.webserver.response.ResponseFactory; import javax.inject.Inject; import javax.inject.Singleton; /** - * A Class responsible for hooking to MCMMO and registering data sources. + * Root handler for different JSON end points. * * @author Rsl1122 - */ @Singleton -public class McmmoHook extends Hook { - - private final Formatter decimalFormatter; +public class RootJSONHandler extends TreePageHandler { @Inject - public McmmoHook( - Formatters formatters + public RootJSONHandler( + ResponseFactory responseFactory, + PlayersTableJSONHandler playersTableJSONHandler ) { - super("com.gmail.nossr50.mcMMO"); - decimalFormatter = formatters.decimals(); + super(responseFactory); + + registerPage("players", playersTableJSONHandler); } - public void hook(HookHandler handler) throws NoClassDefFoundError { - if (enabled) { - handler.addPluginDataSource(new McMmoData(decimalFormatter)); - } + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return true; } -} +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java new file mode 100644 index 000000000..b114caa83 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.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.system.webserver.response.data; + +import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.ResponseType; + +/** + * Generic JSON response implemented using Gson. + *

+ * Returns a JSON version of the given object. + * + * @author Rsl1122 + */ +public class JSONResponse extends Response { + + public JSONResponse(T object) { + super(ResponseType.JSON); + super.setHeader("HTTP/1.1 200 OK"); + + try { + Class gsonClass = Class.forName("com.google.gson.Gson"); + Object gson = gsonClass.getConstructor().newInstance(); + Object json = gsonClass.getMethod("toJson", Object.class).invoke(gson, object); + + super.setContent(json.toString()); + } catch (ReflectiveOperationException e) { + super.setContent("{\"error\":\"Gson for json responses not available on this server: " + e.toString() + "\"}"); + } + } + + public JSONResponse(String jsonString) { + super(ResponseType.JSON); + super.setHeader("HTTP/1.1 200 OK"); + super.setContent(jsonString); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java index c4ea76d41..a1dd8a097 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java @@ -17,8 +17,7 @@ package com.djrapitops.plan.system.webserver.response.pages; import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseType; +import com.djrapitops.plan.system.webserver.response.data.JSONResponse; import java.util.HashMap; import java.util.List; @@ -33,27 +32,13 @@ import java.util.stream.Collectors; * * @author Rsl1122 */ -public class RawDataResponse extends Response { +public class RawDataResponse extends JSONResponse> { public RawDataResponse(DataContainer dataContainer) { - super(ResponseType.JSON); - - Map values = mapToNormalMap(dataContainer); - - super.setHeader("HTTP/1.1 200 OK"); - - try { - Class gsonClass = Class.forName("com.google.gson.Gson"); - Object gson = gsonClass.getConstructor().newInstance(); - Object json = gsonClass.getMethod("toJson", Object.class).invoke(gson, values); - - super.setContent(json.toString()); - } catch (ReflectiveOperationException e) { - super.setContent("{\"error\":\"Gson for raw json responses not available on this server: " + e.toString() + "\"}"); - } + super(mapToNormalMap(dataContainer)); } - private Map mapToNormalMap(DataContainer player) { + private static Map mapToNormalMap(DataContainer player) { Map values = new HashMap<>(); player.getMap().forEach((key, value) -> { @@ -72,14 +57,14 @@ public class RawDataResponse extends Response { return values; } - private List handleList(List list) { + private static List handleList(List list) { if (list.stream().findAny().orElse(null) instanceof DataContainer) { - return (List) list.stream().map((obj) -> mapToNormalMap((DataContainer) obj)).collect(Collectors.toList()); + return (List) list.stream().map(obj -> mapToNormalMap((DataContainer) obj)).collect(Collectors.toList()); } return list; } - private Map handleMap(Map map) { + private static Map handleMap(Map map) { if (map.values().stream().findAny().orElse(null) instanceof DataContainer) { Map newMap = new HashMap<>(); map.forEach((key, value) -> newMap.put(key, mapToNormalMap((DataContainer) value))); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SHA256Hash.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/SHA256Hash.java deleted file mode 100644 index 0542f8176..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SHA256Hash.java +++ /dev/null @@ -1,37 +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.utilities; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; - -public class SHA256Hash { - - private final String original; - - public SHA256Hash(String original) { - this.original = original; - } - - public String create() throws NoSuchAlgorithmException { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - digest.update(original.getBytes(StandardCharsets.UTF_8)); - return Base64.getEncoder().encodeToString(digest.digest()); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java index 4000e0fe5..30954b31a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java @@ -39,7 +39,7 @@ public class StackGraph implements HighChart { int length = this.labels.length; int i = 0; for (String label : this.labels) { - labelBuilder.append("'").append(label).append("'"); + labelBuilder.append('"').append(label).append('"'); if (i < length - 1) { labelBuilder.append(","); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Color.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Color.java index 71fbbe1be..d52dced08 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Color.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Color.java @@ -58,6 +58,9 @@ public enum Color { } public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } try { return Optional.of(valueOf(name)); } catch (IllegalArgumentException e) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Family.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Family.java index e2ebab3ee..418575c0a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Family.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Family.java @@ -37,6 +37,9 @@ public enum Family { } public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } try { return Optional.of(valueOf(name)); } catch (IllegalArgumentException e) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icon.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icon.java index d36b75fb1..20e4bc4bf 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icon.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icon.java @@ -30,6 +30,9 @@ public class Icon { } public static Icon fromExtensionIcon(com.djrapitops.plan.extension.icon.Icon icon) { + if (icon == null) { + return Icon.called("question").build(); + } return new Icon( Family.getByName(icon.getFamily().name()).orElse(Family.SOLID), icon.getName(), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java index 4c039edb0..c4a62549d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java @@ -16,11 +16,13 @@ */ package com.djrapitops.plan.utilities.html.pages; +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.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; import com.djrapitops.plan.utilities.formatting.Formatter; import com.djrapitops.plan.utilities.formatting.Formatters; @@ -50,6 +52,8 @@ public class AnalysisPluginTabs { private String nav; private String tab; + private boolean hasWideTable; + public AnalysisPluginTabs(String nav, String tab) { this.nav = nav; this.tab = tab; @@ -70,6 +74,8 @@ public class AnalysisPluginTabs { this.decimalFormatter = formatters.decimals(); this.percentageFormatter = formatters.percentage(); + hasWideTable = false; + generate(); } @@ -106,7 +112,7 @@ public class AnalysisPluginTabs { String tabsElement; if (onlyGeneric) { ExtensionTabData genericTabData = datum.getTabs().get(0); - tabsElement = Html.BODY.parse(parseDataHtml(genericTabData)); + tabsElement = parseContentHtml(genericTabData); } else { tabsElement = new TabsElement( datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) @@ -125,11 +131,53 @@ public class AnalysisPluginTabs { private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { TabInformation tabInformation = tabData.getTabInformation(); + String tabContentHtml = parseContentHtml(tabData); - return new TabsElement.Tab(tabInformation.getTabName(), parseDataHtml(tabData)); + String tabName = tabInformation.getTabName(); + return new TabsElement.Tab(tabName.isEmpty() + ? Icon.called("info-circle").build().toHtml() + " General" + : Icon.fromExtensionIcon(tabInformation.getTabIcon()).toHtml() + ' ' + tabName, + tabContentHtml); } - private String parseDataHtml(ExtensionTabData tabData) { + private String parseContentHtml(ExtensionTabData tabData) { + TabInformation tabInformation = tabData.getTabInformation(); + + ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values()); + String values = parseValuesHtml(tabData); + String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values); + String tablesHtml = parseTablesHtml(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 parseTablesHtml(ExtensionTabData tabData) { + StringBuilder builder = new StringBuilder(); + for (ExtensionTableData tableData : tabData.getTableData()) { + if (tableData.isWideTable()) { + hasWideTable = true; + } + builder.append(tableData.getHtmlTable().parseHtml()); + } + return builder.toString(); + } + + private String parseValuesHtml(ExtensionTabData tabData) { StringBuilder builder = new StringBuilder(); for (String key : tabData.getValueOrder()) { tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescriptive(), data.getFormattedValue())); @@ -153,7 +201,8 @@ public class AnalysisPluginTabs { } private String wrapInContainer(ExtensionInformation information, String tabsElement) { - return "

" + + String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4"; + return "
" + "
" + "

" + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "

" + "
" + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPage.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPage.java index 9248d88b8..5e07099c6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPage.java @@ -48,7 +48,6 @@ import com.djrapitops.plan.utilities.html.structure.Accordions; import com.djrapitops.plan.utilities.html.structure.ServerAccordion; import com.djrapitops.plan.utilities.html.structure.SessionAccordion; import com.djrapitops.plan.utilities.html.tables.HtmlTables; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.benchmarking.Timings; @@ -259,7 +258,7 @@ public class InspectPage implements Page { : serverName ); - if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) { + if (serverInfo.getServer().isProxy()) { replacer.put("backButton", "
  • arrow_backcloud
  • "); } else { replacer.put("backButton", "
  • arrow_backstorage
  • "); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java index 976ee469b..f25e9b219 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java @@ -16,11 +16,13 @@ */ package com.djrapitops.plan.utilities.html.pages; +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.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData; import com.djrapitops.plan.utilities.formatting.Formatter; import com.djrapitops.plan.utilities.formatting.Formatters; @@ -48,6 +50,8 @@ public class InspectPluginTab implements Comparable { private String nav; private String tab; + private boolean hasWideTable; + public InspectPluginTab(String nav, String tab) { this.nav = nav; this.tab = tab; @@ -70,6 +74,8 @@ public class InspectPluginTab implements Comparable { this.decimalFormatter = formatters.decimals(); this.percentageFormatter = formatters.percentage(); + hasWideTable = false; + generate(); } @@ -106,7 +112,7 @@ public class InspectPluginTab implements Comparable { String tabsElement; if (onlyGeneric) { ExtensionTabData genericTabData = datum.getTabs().get(0); - tabsElement = Html.BODY.parse(parseDataHtml(genericTabData)); + tabsElement = parseContentHtml(genericTabData); } else { tabsElement = new TabsElement( datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) @@ -125,11 +131,53 @@ public class InspectPluginTab implements Comparable { private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { TabInformation tabInformation = tabData.getTabInformation(); + String tabContentHtml = parseContentHtml(tabData); - return new TabsElement.Tab(tabInformation.getTabName(), parseDataHtml(tabData)); + String tabName = tabInformation.getTabName(); + return new TabsElement.Tab(tabName.isEmpty() + ? Icon.called("info-circle").build().toHtml() + " General" + : Icon.fromExtensionIcon(tabInformation.getTabIcon()).toHtml() + ' ' + tabName, + tabContentHtml); } - private String parseDataHtml(ExtensionTabData tabData) { + private String parseContentHtml(ExtensionTabData tabData) { + TabInformation tabInformation = tabData.getTabInformation(); + + ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values()); + String values = parseValuesHtml(tabData); + String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values); + String tablesHtml = parseTablesHtml(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 parseTablesHtml(ExtensionTabData tabData) { + StringBuilder builder = new StringBuilder(); + for (ExtensionTableData tableData : tabData.getTableData()) { + if (tableData.isWideTable()) { + hasWideTable = true; + } + builder.append(tableData.getHtmlTable().parseHtml()); + } + return builder.toString(); + } + + private String parseValuesHtml(ExtensionTabData tabData) { StringBuilder builder = new StringBuilder(); for (String key : tabData.getValueOrder()) { tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescriptive(), data.getFormattedValue())); @@ -153,7 +201,8 @@ public class InspectPluginTab implements Comparable { } private String wrapInContainer(ExtensionInformation information, String tabsElement) { - return "
    " + + String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4"; + return "
    " + "
    " + "

    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "

    " + "
    " + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PlayersPage.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PlayersPage.java index 2fdacd054..1c0a003aa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PlayersPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PlayersPage.java @@ -28,7 +28,6 @@ import com.djrapitops.plan.system.settings.paths.ProxySettings; import com.djrapitops.plan.system.update.VersionCheckSystem; import com.djrapitops.plan.utilities.formatting.PlaceholderReplacer; import com.djrapitops.plan.utilities.html.tables.HtmlTables; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.benchmarking.Timings; import java.util.List; @@ -75,10 +74,10 @@ public class PlayersPage implements Page { placeholderReplacer.put("version", versionCheckSystem.getCurrentVersion()); placeholderReplacer.put("update", versionCheckSystem.getUpdateHtml().orElse("")); - if (Check.isBukkitAvailable()) { - placeholderReplacer.put("networkName", config.get(PluginSettings.SERVER_NAME)); - } else { + if (serverInfo.getServer().isProxy()) { placeholderReplacer.put("networkName", config.get(ProxySettings.NETWORK_NAME)); + } else { + placeholderReplacer.put("networkName", config.get(PluginSettings.SERVER_NAME)); } timings.start("Players page players table parsing"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java index 74838aa0a..a197ed755 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java @@ -75,7 +75,7 @@ class PlayerSessionTable extends TableContainer { String world = worldAliasSettings.getLongestWorldPlayed(session); String toolTip = "Session ID: " + session.getValue(SessionKeys.DB_ID) - .map(id -> Integer.toString(id)) + .map(Object::toString) .orElse("Not Saved."); addRow(Html.LINK_TOOLTIP.parse(inspectUrl, playerName, toolTip), start, length, world); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java new file mode 100644 index 000000000..55c449dd0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java @@ -0,0 +1,230 @@ +/* + * 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.utilities.html.tables; + +import com.djrapitops.plan.api.PlanAPI; +import com.djrapitops.plan.data.container.GeoInfo; +import com.djrapitops.plan.data.store.containers.PlayerContainer; +import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.data.store.mutators.ActivityIndex; +import com.djrapitops.plan.data.store.mutators.GeoInfoMutator; +import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.extension.FormatType; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.implementation.results.*; +import com.djrapitops.plan.utilities.comparators.PlayerContainerLastPlayedComparator; +import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.utilities.html.Html; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Parsing utility for creating jQuery Datatables JSON for a Players Table. + *

    + * See https://www.datatables.net/manual/data/orthogonal-data#HTML-5 for sort kinds + * + * @author Rsl1122 + */ +public class PlayersTableJSONParser { + + private final List players; + private final List extensionDescriptives; + private final Map extensionData; + + private final int maxPlayers; + private final long activeMsThreshold; + private final int activeLoginThreshold; + private final boolean openPlayerPageInNewTab; + + private Map> numberFormatters; + + private Formatter decimalFormatter; + + public PlayersTableJSONParser( + // Data + List players, + Map extensionData, + // Settings + int maxPlayers, long activeMsThreshold, int activeLoginThreshold, boolean openPlayerPageInNewTab, + // Formatters + Formatters formatters + ) { + // Data + this.players = players; + this.extensionData = extensionData; + extensionDescriptives = extensionData.values().stream() + .map(ExtensionTabData::getDescriptives) + .flatMap(Collection::stream) + .distinct().sorted((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName())) + .collect(Collectors.toList()); + // Settings + this.maxPlayers = maxPlayers; + this.activeMsThreshold = activeMsThreshold; + this.activeLoginThreshold = activeLoginThreshold; + this.openPlayerPageInNewTab = openPlayerPageInNewTab; + // Formatters + 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(); + } + + public String toJSONString() { + String data = parseData(); + String columnHeaders = parseColumnHeaders(); + return "{\"columns\":" + columnHeaders + ",\"data\":" + data + '}'; + } + + private String parseData() { + StringBuilder dataJSON = new StringBuilder("["); + + PlanAPI planAPI = PlanAPI.getInstance(); + long now = System.currentTimeMillis(); + players.sort(new PlayerContainerLastPlayedComparator()); + + int currentPlayerNumber = 0; + for (PlayerContainer player : players) { + if (currentPlayerNumber >= maxPlayers) { + break; + } + UUID playerUUID = player.getValue(PlayerKeys.UUID).orElse(null); + if (playerUUID == null) { + continue; + } + + if (currentPlayerNumber > 0) { + dataJSON.append(','); // Previous item + } + dataJSON.append('{'); // Start new item + + appendPlayerData(dataJSON, planAPI, now, player); + appendExtensionData(dataJSON, extensionData.getOrDefault(playerUUID, new ExtensionTabData.Factory(null).build())); + + dataJSON.append('}'); // Close new item + + currentPlayerNumber++; + } + return dataJSON.append(']').toString(); + } + + private void appendPlayerData(StringBuilder dataJSON, PlanAPI planAPI, long now, PlayerContainer player) { + String name = player.getValue(PlayerKeys.NAME).orElse("Unknown"); + String url = planAPI.getPlayerInspectPageLink(name); + + SessionsMutator sessionsMutator = SessionsMutator.forContainer(player); + int loginTimes = sessionsMutator.count(); + long playtime = sessionsMutator.toPlaytime(); + long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L); + long lastSeen = sessionsMutator.toLastSeen(); + + ActivityIndex activityIndex = player.getActivityIndex(now, activeMsThreshold, activeLoginThreshold); + boolean isBanned = player.getValue(PlayerKeys.BANNED).orElse(false); + String activityString = activityIndex.getFormattedValue(decimalFormatter) + + (isBanned ? " (Banned)" : " (" + activityIndex.getGroup() + ")"); + + String geolocation = GeoInfoMutator.forContainer(player).mostRecent().map(GeoInfo::getGeolocation).orElse("-"); + + Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK; + + dataJSON + .append(makeDataEntry(link.parse(url, name), "name")).append(',') + .append(makeDataEntry(activityIndex.getValue(), activityString, "index")).append(',') + .append(makeDataEntry(playtime, numberFormatters.get(FormatType.TIME_MILLISECONDS).apply(playtime), "playtime")).append(',') + .append(makeDataEntry(loginTimes, "sessions")).append(',') + .append(makeDataEntry(registered, numberFormatters.get(FormatType.DATE_YEAR).apply(registered), "registered")).append(',') + .append(makeDataEntry(lastSeen, numberFormatters.get(FormatType.DATE_YEAR).apply(lastSeen), "seen")).append(',') + .append(makeDataEntry(geolocation, "geolocation")) + ; + } + + private String makeDataEntry(Object data, String dataName) { + return "\"" + dataName + "\":\"" + data.toString().replace('"', '\'') + "\""; + } + + private String makeDataEntry(Object data, String formatted, String dataName) { + return "\"" + dataName + "\":{\"v\":\"" + data.toString().replace('"', '\'') + "\", \"d\":\"" + formatted.replace('"', '\'') + "\"}"; + } + + private void appendExtensionData(StringBuilder dataJSON, ExtensionTabData tabData) { + for (ExtensionDescriptive descriptive : extensionDescriptives) { + dataJSON.append(','); + String key = descriptive.getName(); + + // If it's a double, append a double + Optional doubleValue = tabData.getDouble(key); + + if (doubleValue.isPresent()) { + dataJSON.append(makeDataEntry(doubleValue.get().getRawValue(), doubleValue.get().getFormattedValue(decimalFormatter), key)); + continue; + } + + Optional numberValue = tabData.getNumber(key); + if (numberValue.isPresent()) { + ExtensionNumberData numberData = numberValue.get(); + FormatType formatType = numberData.getFormatType(); + dataJSON.append(makeDataEntry(numberData.getRawValue(), numberData.getFormattedValue(numberFormatters.get(formatType)), key)); + continue; + } + + // If it's a String append a String, otherwise the player has no value for this extension provider. + String stringValue = tabData.getString(key).map(ExtensionStringData::getFormattedValue).orElse("-"); + dataJSON.append(makeDataEntry(stringValue, stringValue, key)); + } + } + + private String parseColumnHeaders() { + StringBuilder columnHeaders = new StringBuilder("["); + + // Is the data for the column formatted + + columnHeaders + .append(makeColumnHeader(Icon.called("user") + " Name", "name")).append(',') + .append(makeFColumnHeader(Icon.called("check") + " Activity Index", "index")).append(',') + .append(makeFColumnHeader(Icon.called("clock").of(Family.REGULAR) + " Playtime", "playtime")).append(',') + .append(makeColumnHeader(Icon.called("calendar-plus").of(Family.REGULAR) + " Sessions", "sessions")).append(',') + .append(makeFColumnHeader(Icon.called("user-plus") + " Registered", "registered")).append(',') + .append(makeFColumnHeader(Icon.called("calendar-check").of(Family.REGULAR) + " Last Seen", "seen")).append(',') + .append(makeColumnHeader(Icon.called("globe") + " Geolocation", "geolocation")); + + appendExtensionHeaders(columnHeaders); + + return columnHeaders.append(']').toString(); + } + + private String makeColumnHeader(String title, String dataProperty) { + return "{\"title\": \"" + title.replace('"', '\'') + "\",\"data\":\"" + dataProperty + "\"}"; + } + + private String makeFColumnHeader(String title, String dataProperty) { + return "{\"title\": \"" + title.replace('"', '\'') + "\",\"data\":{\"_\":\"" + dataProperty + ".v\",\"display\":\"" + dataProperty + ".d\"}}"; + } + + private void appendExtensionHeaders(StringBuilder columnHeaders) { + for (ExtensionDescriptive provider : extensionDescriptives) { + columnHeaders.append(','); + String headerText = Icon.fromExtensionIcon(provider.getIcon().setColor(Color.NONE)).toHtml().replace('"', '\'') + ' ' + provider.getText(); + columnHeaders.append(makeFColumnHeader(headerText, provider.getName())); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java index de04fb2b8..a5b0f25cb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java @@ -76,7 +76,7 @@ class ServerSessionTable extends TableContainer { String world = worldAliasSettings.getLongestWorldPlayed(session); String toolTip = "Session ID: " + session.getValue(SessionKeys.DB_ID) - .map(id -> Integer.toString(id)) + .map(Object::toString) .orElse("Not Saved."); String playerName = playerNames.getOrDefault(session.getValue(SessionKeys.UUID).orElse(null), "Unknown"); diff --git a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml index 7567f6e8e..f63dc3d63 100644 --- a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml +++ b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml @@ -59,6 +59,7 @@ Webserver: # ----------------------------------------------------- Data_gathering: Geolocations: true + Ping: true # ----------------------------------------------------- # Supported time units: MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS # ----------------------------------------------------- @@ -85,6 +86,11 @@ Time: Unit: MINUTES Remove_inactive_player_data_after: 180 Unit: DAYS + # Includes players online, tps and performance time series + Remove_time_series_data_after: 90 + Unit: DAYS + Remove_ping_data_after: 14 + Unit: DAYS Periodic_tasks: Extension_data_refresh_every: 1 Unit: HOURS diff --git a/Plan/common/src/main/resources/assets/plan/config.yml b/Plan/common/src/main/resources/assets/plan/config.yml index 30514c56a..70eb39c40 100644 --- a/Plan/common/src/main/resources/assets/plan/config.yml +++ b/Plan/common/src/main/resources/assets/plan/config.yml @@ -25,7 +25,7 @@ Plugin: # Supported databases: SQLite, H2, MySQL # ----------------------------------------------------- Database: - Type: SQLite + Type: H2 MySQL: Host: localhost Port: 3306 @@ -61,6 +61,7 @@ Webserver: # ----------------------------------------------------- Data_gathering: Geolocations: true + Ping: true Commands: Log_unknown: false Log_aliases_as_main_command: true @@ -90,6 +91,11 @@ Time: Unit: MINUTES Remove_inactive_player_data_after: 180 Unit: DAYS + # Includes players online, tps and performance time series + Remove_time_series_data_after: 90 + Unit: DAYS + Remove_ping_data_after: 14 + Unit: DAYS Periodic_tasks: Analysis_refresh_every: 60 Unit: MINUTES diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt index 92fdfc178..1bab9873b 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt @@ -1,9 +1,9 @@ -Cmd - Click Me || 点击此处 +Cmd - Click Me || 请点击此处 Cmd - Link || §7 •§2 链接:§f -Cmd Disable - Disabled || §a现已禁用计划系统。阁下仍可使用 /planbungee reload 重启插件。 +Cmd Disable - Disabled || §a现已禁用计划系统。您仍可使用 /planbungee reload 重启插件。 Cmd FAIL - Invalid Username || §c[计划] 此玩家不存在。 -Cmd FAIL - No Feature || §e请定义要禁用的功能!(当前支持 ${0}) -Cmd FAIL - No Permission || §c[计划] 阁下没有所需权限。 +Cmd FAIL - No Feature || §e请设置要禁用的功能!(当前支持 ${0}) +Cmd FAIL - No Permission || §c[计划] 您没有所需权限。 Cmd FAIL - Require only one Argument || §c[计划] 命令需要一个参数。 Cmd FAIL - Requires Arguments || §c[计划] 命令缺少参数。${0} Cmd FAIL - Unknown Username || §c[计划] 未在数据库中找到此玩家。 @@ -18,16 +18,16 @@ Cmd Header - Search || §f»§2 玩家统计 - 搜 Cmd Header - Servers || > §2服务器 Cmd Header - Web Users || > §2${0} 个网页用户 Cmd Info - Bungee Connection || §2已连接至 Bungee:§f${0} -Cmd Info - Database || §2活跃数据库:§f${0} +Cmd Info - Database || §2可用数据库:§f${0} Cmd Info - Reload Complete || §a重新载入完毕 Cmd Info - Reload Failed || §c重载插件时发生错误,建议重新启动服务器。 Cmd Info - Update || §2更新可用:§f${0} Cmd Info - Version || §2版本:§f${0} -Cmd Notify - No WebUser || 阁下可能还没有网页账户,请输入 /plan register <密码> +Cmd Notify - No WebUser || 您可能还没有网页账户,请输入 /plan register <密码> Cmd Notify - WebUser register || 已注册新用户:'${0}' 权限级别:${1} -Cmd Qinspect - Activity Index || §2活动索引:§f${0} | ${1} +Cmd Qinspect - Activity Index || §2活跃指数:§f${0} | ${1} Cmd Qinspect - Deaths || §2死亡数:§f${0} -Cmd Qinspect - Geolocation || §2从 §f${0} 处登录 +Cmd Qinspect - Geolocation || §2从 §f${0} 登录 Cmd Qinspect - Last Seen || §2最后在线:§f${0} Cmd Qinspect - Longest Session || §2最长在线时间:§f${0} Cmd Qinspect - Mob Kills || §2怪物击杀数:§f${0} @@ -44,7 +44,7 @@ Cmd Setup - Generic Fail || §e连接失败:${0} Cmd Setup - Internal Error || §e连接成功。${0},请检查接收服务器调试页面的错误日志。 Cmd Setup - Success || §a连接成功,计划可能将在几秒内重启··· Cmd Setup - Unauthorized || §e连接成功,但接收服务器并未授权此服务器。请在 Discord 上联系以寻求帮助 -Cmd Setup - Url mistake || §c请确保阁下所输入的是完整地址(以 http:// 或 https:// 开头)- 请检查 Bungee 启用日志获取完整地址。 +Cmd Setup - Url mistake || §c请确保您所输入的是完整地址(以 http:// 或 https:// 开头)- 请检查 Bungee 启用日志获取完整地址。 Cmd Setup - WebServer not Enabled || §c未在此服务器上启用网页服务器!请确保其在开机时启用! Cmd SUCCESS - Feature disabled || §a已在下次插件重载前暂时禁用 '${0}'。 Cmd SUCCESS - WebUser register || §a已成功添加新用户(${0})! @@ -53,11 +53,11 @@ Cmd Update - Cancelled || §c已取消更新。 Cmd Update - Change log || 更新日志 版本${0}: Cmd Update - Fail Cacnel || §c某个服务器更新失败,正在取消所有服务器的更新··· Cmd Update - Fail Force Notify || §e${0} 无法更新,但使用了 -force,正在继续更新。 -Cmd Update - Fail Not Online || §c并非所有服务器均在线或可访问,阁下仍可通过使用 /plan update -u -force 的方式强制更新。 -Cmd Update - Notify Cancel || §a阁下可以使用 /plan update cancel 以取消尚未重启的服务器上的更新。 +Cmd Update - Fail Not Online || §c并非所有服务器均在线或可访问,您仍可通过使用 /plan update -u -force 的方式强制更新。 +Cmd Update - Notify Cancel || §a您可以使用 /plan update cancel 以取消尚未重启的服务器上的更新。 Cmd Update - Online Check || 正在检查是否所有服务器均在线··· Cmd Update - Scheduled || §a已为 ${0} 计划了更新。 -Cmd Update - Url mismatch || §c版本下载网址不以 ${0} 开头故可能不受信任。阁下可手动下载此版本(直接下载): +Cmd Update - Url mismatch || §c版本下载网址不以 ${0} 开头故可能不受信任。您可手动下载此版本(直接下载): Cmd Web - Permission Levels || >\§70:访问所有页面\§71:访问 '/players' 及所有玩家页\§72:访问用户名与网页用户名一致的玩家页\§73+:无权限 Command Help - /plan analyze || 查看服务器分析 Command Help - /plan dev || 开发模式命令 @@ -97,16 +97,16 @@ Database - Patches Applied Already || 已应用所有数据库补 Database MySQL - Launch Options Error || 启动参数出错,正使用默认参数(${0}) Database Notify - Clean || 移除了 ${0} 位用户的数据。 Database Notify - SQLite No WAL || 此服务器版本不支持 SQLite WAL 模式,正使用默认模式。这可能会影响性能。 -Disable || 已禁用玩家分析。 +Disable || 已禁用插件。 Disable - Processing || 正在处理未处理的关键任务。(${0}) Disable - Processing Complete || 处理完毕。 Disable - WebServer || 正在关闭网页服务器··· -Enable || 已启用玩家分析。 +Enable || 已启用插件。 Enable - Database || ${0}-已建立数据库连接。 Enable - Notify Address Confirmation || 确保此地址指向此服务器:${0} -Enable - Notify Empty IP || server.properties 中的 IP 为空且未使用替代 IP。这将给出错误地址! -Enable - Notify Geolocations disabled || 地理位置收集不活跃。(Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || 计划需要在首次运行时访问互联网以下载 GeoLite2 地理位置数据库。 +Enable - Notify Empty IP || server.properties 中的 IP 为空且未使用替代 IP。这将会导致地址出错! +Enable - Notify Geolocations disabled || 已关闭地理位置收集。(Data.Geolocations: false) +Enable - Notify Geolocations Internet Required || 插件需要在首次运行时访问互联网以下载 GeoLite2 地理位置数据库。 Enable - Notify Webserver disabled || 未初始化网页服务器。(WebServer.DisableWebServer: true) Enable - WebServer || 正在初始化网页服务器··· Enable FAIL - Database || ${0}-连接数据库失败:${1} @@ -114,7 +114,7 @@ Enable FAIL - Database Patch || 数据库补丁失败,插 Enable FAIL - GeoDB Write || 保存已下载的 GeoLite2 地理位置数据库时发生问题 Enable FAIL - WebServer (Bungee) || 网页服务器未初始化 Enable FAIL - Wrong Database Type || ${0} 此数据类型不存在。 -HTML - ACTIVITY_INDEX || 活动索引 +HTML - ACTIVITY_INDEX || 活跃指数 HTML - ALL || 全部 HTML - ALL_TIME_PEAK || 所有时间峰值 HTML - AVERAGE_PING || 平均延迟 @@ -125,7 +125,7 @@ HTML - CALENDAR || 日历 HTML - CALENDAR_TEXT || 日历 HTML - CHUNKS || 区块 HTML - COMMAND || 命令 -HTML - COMMNAND_USAGE || 命令用法 +HTML - COMMNAND_USAGE || 使用命令 HTML - CONNECTION_INFORMATION || 连接信息 HTML - COUNTRY || 国家 HTML - CURRENT_PLAYERBASE || 当前玩家 @@ -138,8 +138,8 @@ HTML - GEOLOCATION_TEXT || 地理位置 HTML - HEALTH_ESTIMATE || 预计健康度 HTML - INDEX_ACTIVE || 活跃 HTML - INDEX_INACTIVE || 不活跃 -HTML - INDEX_IRREGULAR || 偶尔 -HTML - INDEX_REGULAR || 经常 +HTML - INDEX_IRREGULAR || 偶尔上线 +HTML - INDEX_REGULAR || 经常上线 HTML - INDEX_VERY_ACTIVE || 非常活跃 HTML - IP_ADDRESS || IP 地址 HTML - KILLED || 被击杀 @@ -160,25 +160,25 @@ HTML - LOW_TPS_SPIKES || 最低 TPS 值 HTML - MOB_CAUSED_DEATHS || 被怪物击杀数 HTML - MOB_KDR || 怪物击杀比 HTML - MOB_KILLS || 怪物击杀数 -HTML - MOST_RECENT_SESSIONS || 最近会话 +HTML - MOST_RECENT_SESSIONS || 最近时域 HTML - NAME || 名称 -HTML - NAV_COMMAND_USAGE || 指令用法 +HTML - NAV_COMMAND_USAGE || 使用指令 HTML - NAV_GEOLOCATIONS || 地理位置 HTML - NAV_INFORMATION || 信息 HTML - NAV_NETWORK_PLAYERS || 网络玩家 -HTML - NAV_ONLINE_ACTIVITY || 在线活动 +HTML - NAV_ONLINE_ACTIVITY || 在线玩家 HTML - NAV_OVERVIEW || 预览 HTML - NAV_PERFORMANCE || 性能 HTML - NAV_PLAYERS || 玩家 HTML - NAV_PLUGINS || 插件 -HTML - NAV_SESSIONS || 会话 +HTML - NAV_SESSIONS || 时域 HTML - NAV_SEVER_HEALTH || 服务器健康度 -HTML - NETWORK || 网络 -HTML - NETWORK_INFORMATION || 网络信息 +HTML - NETWORK || 服务器网络 +HTML - NETWORK_INFORMATION || 服务器网络信息 HTML - NEW || 新建 HTML - NEW_CALENDAR || 新: HTML - NEW_PLAYERS_TEXT || 新玩家 -HTML - NEW_RETENTION || 新玩家保留 +HTML - NEW_RETENTION || 新玩家留坑率 HTML - NEW_TEXT || 新建 HTML - NICKNAME || 昵称 HTML - NO_KILLS || 无击杀数 @@ -186,7 +186,7 @@ HTML - NO_PLAYER_CAUSED_DEATHS || 无被玩家击杀数 HTML - OFFLINE || 离线 HTML - ONLINE || 在线 HTML - ONLINE_ACTIVITY || 在线活动 -HTML - OPERATOR || 操作员 +HTML - OPERATOR || 管理员 HTML - OVERVIEW || 预览 HTML - PER_DAY || / 天 HTML - PLAYER_CAUSED_DEATHS || 被玩家击杀数 @@ -199,39 +199,39 @@ HTML - PLAYERS_ONLINE_TEXT || 在线玩家 HTML - PLAYERS_TEXT || 玩家 HTML - PLAYTIME || 游玩时间 HTML - PLEASE_WAIT || 请稍候··· -HTML - PREDICETED_RETENTION || 预计保留 +HTML - PREDICETED_RETENTION || 预计留坑率 HTML - PUNCH_CARD || 打卡签到 HTML - PUNCHCARD || 打卡签到 HTML - RECENT_LOGINS || 近期登陆 HTML - REGISTERED || 已注册 HTML - REGISTERED_TEXT || 已注册 HTML - REGULAR || 经常 -HTML - SEEN_NICKNAMES || 可见昵称 +HTML - SEEN_NICKNAMES || 可视昵称 HTML - SERVER || 服务器 HTML - SERVER_ANALYSIS || 服务器分析 HTML - SERVER_HEALTH_ESTIMATE || 服务器健康估计 HTML - SERVER_INFORMATION || 服务器信息 HTML - SERVER_PREFERENCE || 服务器偏好设置 HTML - SERVERS || 服务器 -HTML - SESSION || 会话 -HTML - SESSION_ENDED || 会话已结束 -HTML - SESSION_LENGTH || 会话长度 -HTML - SESSION_MEDIAN || 会话中位数 -HTML - SESSIONS || 会话 +HTML - SESSION || 时域 +HTML - SESSION_ENDED || 时域之末 +HTML - SESSION_LENGTH || 时域长度 +HTML - SESSION_MEDIAN || 平均时域 +HTML - SESSIONS || 时域 # Not sure if this is the time or count of numbers. HTML - TIME || 次数 HTML - TIMES_KICKED || 被踢次数 -HTML - TIMES_USED || 使用次数 +HTML - TIMES_USED || 占用 HTML - TOTAL_ACTIVE_TEXT || 总活跃玩家 HTML - TOTAL_AFK || 总挂机玩家 HTML - TOTAL_PLAYERS || 总玩家 HTML - TOTAL_PLAYTIME || 总在线时长 -HTML - UNIQUE || 唯一 -HTML - UNIQUE_CALENDAR || 唯一: -HTML - UNIQUE_PLAYERS || 唯一玩家 -HTML - UNIQUE_PLAYERS_TEXT || 唯一玩家 -HTML - UNIQUE_TEXT || 唯一 -HTML - USAGE || 用法 +HTML - UNIQUE || 普通 +HTML - UNIQUE_CALENDAR || 普通玩家: +HTML - UNIQUE_PLAYERS || 普通玩家 +HTML - UNIQUE_PLAYERS_TEXT || 普通玩家 +HTML - UNIQUE_TEXT || 普通玩家 +HTML - USAGE || 占用 HTML - USED_COMMANDS || 使用的命令 HTML - USER_AND_PASS_NOT_SPECIFIED || 未指定用户名与密码 HTML - USER_DOES_NOT_EXIST || 用户不存在 @@ -239,21 +239,21 @@ HTML - USER_INFORMATION || 用户信息 HTML - USER_PASS_MISMATCH || 用户名与密码不匹配 HTML - WITH ||

    ').appendTo(this); - } - oSettings.nTBody = tbody[0]; - - var tfoot = $this.children('tfoot'); - if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) - { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo(this); - } - - if ( tfoot.length === 0 || tfoot.children().length === 0 ) { - $this.addClass( oClasses.sNoFooter ); - } - else if ( tfoot.length > 0 ) { - oSettings.nTFoot = tfoot[0]; - _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); - } - - /* Check if there is data passing into the constructor */ - if ( oInit.aaData ) - { - for ( i=0 ; i/g; - var _re_date_start = /^[\w\+\-]/; - var _re_date_end = /[\w\+\-]$/; - - // Escape regular expression special characters - var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); - - // http://en.wikipedia.org/wiki/Foreign_exchange_market - // - \u20BD - Russian ruble. - // - \u20a9 - South Korean Won - // - \u20BA - Turkish Lira - // - \u20B9 - Indian Rupee - // - R - Brazil (R$) and South Africa - // - fr - Swiss Franc - // - kr - Swedish krona, Norwegian krone and Danish krone - // - \u2009 is thin space and \u202F is narrow no-break space, both used in many - // standards as thousands separators. - var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi; - - - var _empty = function ( d ) { - return !d || d === true || d === '-' ? true : false; - }; - - - var _intVal = function ( s ) { - var integer = parseInt( s, 10 ); - return !isNaN(integer) && isFinite(s) ? integer : null; - }; - - // Convert from a formatted number with characters other than `.` as the - // decimal place, to a Javascript number - var _numToDecimal = function ( num, decimalPoint ) { - // Cache created regular expressions for speed as this function is called often - if ( ! _re_dic[ decimalPoint ] ) { - _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); - } - return typeof num === 'string' && decimalPoint !== '.' ? - num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : - num; - }; - - - var _isNumber = function ( d, decimalPoint, formatted ) { - var strType = typeof d === 'string'; - - // If empty return immediately so there must be a number if it is a - // formatted string (this stops the string "k", or "kr", etc being detected - // as a formatted number for currency - if ( _empty( d ) ) { - return true; - } - - if ( decimalPoint && strType ) { - d = _numToDecimal( d, decimalPoint ); - } - - if ( formatted && strType ) { - d = d.replace( _re_formatted_numeric, '' ); - } - - return !isNaN( parseFloat(d) ) && isFinite( d ); - }; - - - // A string without HTML in it can be considered to be HTML still - var _isHtml = function ( d ) { - return _empty( d ) || typeof d === 'string'; - }; - - - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { - return true; - } - - var html = _isHtml( d ); - return ! html ? - null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? - true : - null; - }; - - - var _pluck = function ( a, prop, prop2 ) { - var out = []; - var i=0, ien=a.length; - - // Could have the test in the loop for slightly smaller code, but speed - // is essential here - if ( prop2 !== undefined ) { - for ( ; i') - .css( { - position: 'fixed', - top: 0, - left: 0, - height: 1, - width: 1, - overflow: 'hidden' - } ) - .append( - $('
    ') - .css( { - position: 'absolute', - top: 1, - left: 1, - width: 100, - overflow: 'scroll' - } ) - .append( - $('
    ') - .css( { - width: '100%', - height: 10 - } ) - ) - ) - .appendTo( 'body' ); - - var outer = n.children(); - var inner = outer.children(); - - // Numbers below, in order, are: - // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth - // - // IE6 XP: 100 100 100 83 - // IE7 Vista: 100 100 100 83 - // IE 8+ Windows: 83 83 100 83 - // Evergreen Windows: 83 83 100 83 - // Evergreen Mac with scrollbars: 85 85 100 85 - // Evergreen Mac without scrollbars: 100 100 100 100 - - // Get scrollbar width - browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; - - // IE6/7 will oversize a width 100% element inside a scrolling element, to - // include the width of the scrollbar, while other browsers ensure the inner - // element is contained without forcing scrolling - browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; - - // In rtl text layout, some browsers (most, but not all) will place the - // scrollbar on the left, rather than the right. - browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; - - // IE8- don't provide height and width for getBoundingClientRect - browser.bBounding = n[0].getBoundingClientRect().width ? true : false; - - n.remove(); - } - - $.extend( settings.oBrowser, DataTable.__browser ); - settings.oScroll.iBarWidth = DataTable.__browser.barWidth; - } - - - /** - * Array.prototype reduce[Right] method, used for browsers which don't support - * JS 1.6. Done this way to reduce code size, since we iterate either way - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnReduce ( that, fn, init, start, end, inc ) - { - var - i = start, - value, - isSet = false; - - if ( init !== undefined ) { - value = init; - isSet = true; - } - - while ( i !== end ) { - if ( ! that.hasOwnProperty(i) ) { - continue; - } - - value = isSet ? - fn( value, that[i], i, that ) : - that[i]; - - isSet = true; - i += inc; - } - - return value; - } - - /** - * Add a column to the list used for the table with default values - * @param {object} oSettings dataTables settings object - * @param {node} nTh The th element for this column - * @memberof DataTable#oApi - */ - function _fnAddColumn( oSettings, nTh ) - { - // Add column to aoColumns array - var oDefaults = DataTable.defaults.column; - var iCol = oSettings.aoColumns.length; - var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { - "nTh": nTh ? nTh : document.createElement('th'), - "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', - "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mData": oDefaults.mData ? oDefaults.mData : iCol, - idx: iCol - } ); - oSettings.aoColumns.push( oCol ); - - // Add search object for column specific search. Note that the `searchCols[ iCol ]` - // passed into extend can be undefined. This allows the user to give a default - // with only some of the parameters defined, and also not give a default - var searchCols = oSettings.aoPreSearchCols; - searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); - - // Use the default column options function to initialise classes etc - _fnColumnOptions( oSettings, iCol, $(nTh).data() ); - } - - - /** - * Apply options for a column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable etc - * @memberof DataTable#oApi - */ - function _fnColumnOptions( oSettings, iCol, oOptions ) - { - var oCol = oSettings.aoColumns[ iCol ]; - var oClasses = oSettings.oClasses; - var th = $(oCol.nTh); - - // Try to get width information from the DOM. We can't get it from CSS - // as we'd need to parse the CSS stylesheet. `width` option can override - if ( ! oCol.sWidthOrig ) { - // Width attribute - oCol.sWidthOrig = th.attr('width') || null; - - // Style attribute - var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); - if ( t ) { - oCol.sWidthOrig = t[1]; - } - } - - /* User specified column options */ - if ( oOptions !== undefined && oOptions !== null ) - { - // Backwards compatibility - _fnCompatCols( oOptions ); - - // Map camel case parameters to their Hungarian counterparts - _fnCamelToHungarian( DataTable.defaults.column, oOptions ); - - /* Backwards compatibility for mDataProp */ - if ( oOptions.mDataProp !== undefined && !oOptions.mData ) - { - oOptions.mData = oOptions.mDataProp; - } - - if ( oOptions.sType ) - { - oCol._sManualType = oOptions.sType; - } - - // `class` is a reserved word in Javascript, so we need to provide - // the ability to use a valid name for the camel case input - if ( oOptions.className && ! oOptions.sClass ) - { - oOptions.sClass = oOptions.className; - } - - $.extend( oCol, oOptions ); - _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); - - /* iDataSort to be applied (backwards compatibility), but aDataSort will take - * priority if defined - */ - if ( oOptions.iDataSort !== undefined ) - { - oCol.aDataSort = [ oOptions.iDataSort ]; - } - _fnMap( oCol, oOptions, "aDataSort" ); - } - - /* Cache the data get and set functions for speed */ - var mDataSrc = oCol.mData; - var mData = _fnGetObjectDataFn( mDataSrc ); - var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; - - var attrTest = function( src ) { - return typeof src === 'string' && src.indexOf('@') !== -1; - }; - oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( - attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) - ); - oCol._setter = null; - - oCol.fnGetData = function (rowData, type, meta) { - var innerData = mData( rowData, type, undefined, meta ); - - return mRender && type ? - mRender( innerData, type, rowData, meta ) : - innerData; - }; - oCol.fnSetData = function ( rowData, val, meta ) { - return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); - }; - - // Indicate if DataTables should read DOM data as an object or array - // Used in _fnGetRowElements - if ( typeof mDataSrc !== 'number' ) { - oSettings._rowReadObject = true; - } - - /* Feature sorting overrides column specific when off */ - if ( !oSettings.oFeatures.bSort ) - { - oCol.bSortable = false; - th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called - } - - /* Check that the class assignment is correct for sorting */ - var bAsc = $.inArray('asc', oCol.asSorting) !== -1; - var bDesc = $.inArray('desc', oCol.asSorting) !== -1; - if ( !oCol.bSortable || (!bAsc && !bDesc) ) - { - oCol.sSortingClass = oClasses.sSortableNone; - oCol.sSortingClassJUI = ""; - } - else if ( bAsc && !bDesc ) - { - oCol.sSortingClass = oClasses.sSortableAsc; - oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; - } - else if ( !bAsc && bDesc ) - { - oCol.sSortingClass = oClasses.sSortableDesc; - oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; - } - else - { - oCol.sSortingClass = oClasses.sSortable; - oCol.sSortingClassJUI = oClasses.sSortJUI; - } - } - - - /** - * Adjust the table column widths for new data. Note: you would probably want to - * do a redraw after calling this function! - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAdjustColumnSizing ( settings ) - { - /* Not interested in doing column width calculation if auto-width is disabled */ - if ( settings.oFeatures.bAutoWidth !== false ) - { - var columns = settings.aoColumns; - - _fnCalculateColumnWidths( settings ); - for ( var i=0 , iLen=columns.length ; i
    ').addClass( k ); - $('td', created) - .addClass( k ) - .html( r ) - [0].colSpan = _fnVisbleColumns( ctx ); - - rows.push( created[0] ); - } - }; - - addRow( data, klass ); - - if ( row._details ) { - row._details.remove(); - } - - row._details = $(rows); - - // If the children were already shown, that state should be retained - if ( row._detailsShow ) { - row._details.insertAfter( row.nTr ); - } - }; - - - var __details_remove = function ( api, idx ) - { - var ctx = api.context; - - if ( ctx.length ) { - var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; - - if ( row && row._details ) { - row._details.remove(); - - row._detailsShow = undefined; - row._details = undefined; - } - } - }; - - - var __details_display = function ( api, show ) { - var ctx = api.context; - - if ( ctx.length && api.length ) { - var row = ctx[0].aoData[ api[0] ]; - - if ( row._details ) { - row._detailsShow = show; - - if ( show ) { - row._details.insertAfter( row.nTr ); - } - else { - row._details.detach(); - } - - __details_events( ctx[0] ); - } - } - }; - - - var __details_events = function ( settings ) - { - var api = new _Api( settings ); - var namespace = '.dt.DT_details'; - var drawEvent = 'draw'+namespace; - var colvisEvent = 'column-visibility'+namespace; - var destroyEvent = 'destroy'+namespace; - var data = settings.aoData; - - api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); - - if ( _pluck( data, '_details' ).length > 0 ) { - // On each draw, insert the required elements into the document - api.on( drawEvent, function ( e, ctx ) { - if ( settings !== ctx ) { - return; - } - - api.rows( {page:'current'} ).eq(0).each( function (idx) { - // Internal data grab - var row = data[ idx ]; - - if ( row._detailsShow ) { - row._details.insertAfter( row.nTr ); - } - } ); - } ); - - // Column visibility change - update the colspan - api.on( colvisEvent, function ( e, ctx, idx, vis ) { - if ( settings !== ctx ) { - return; - } - - // Update the colspan for the details rows (note, only if it already has - // a colspan) - var row, visible = _fnVisbleColumns( ctx ); - - for ( var i=0, ien=data.length ; i=0 count from left, <0 count from right) - * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) - * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) - * "{string}:name" - column name - * "{string}" - jQuery selector on column header nodes - * - */ - - // can be an array of these items, comma separated list, or an array of comma - // separated lists - - var __re_column_selector = /^(.+):(name|visIdx|visible)$/; - - - // r1 and r2 are redundant - but it means that the parameters match for the - // iterator callback in columns().data() - var __columnData = function ( settings, column, r1, r2, rows ) { - var a = []; - for ( var row=0, ien=rows.length ; row= 0 ? - selInt : // Count from left - columns.length + selInt // Count from right (+ because its a negative value) - ]; - } - - // Selector = function - if ( typeof s === 'function' ) { - var rows = _selector_row_indexes( settings, opts ); - - return $.map( columns, function (col, idx) { - return s( - idx, - __columnData( settings, idx, 0, 0, rows ), - nodes[ idx ] - ) ? idx : null; - } ); - } - - // jQuery or string selector - var match = typeof s === 'string' ? - s.match( __re_column_selector ) : - ''; - - if ( match ) { - switch( match[2] ) { - case 'visIdx': - case 'visible': - var idx = parseInt( match[1], 10 ); - // Visible index given, convert to column index - if ( idx < 0 ) { - // Counting from the right - var visColumns = $.map( columns, function (col,i) { - return col.bVisible ? i : null; - } ); - return [ visColumns[ visColumns.length + idx ] ]; - } - // Counting from the left - return [ _fnVisibleToColumnIndex( settings, idx ) ]; - - case 'name': - // match by name. `names` is column index complete and in order - return $.map( names, function (name, i) { - return name === match[1] ? i : null; - } ); - - default: - return []; - } - } - - // Cell in the table body - if ( s.nodeName && s._DT_CellIndex ) { - return [ s._DT_CellIndex.column ]; - } - - // jQuery selector on the TH elements for the columns - var jqResult = $( nodes ) - .filter( s ) - .map( function () { - return $.inArray( this, nodes ); // `nodes` is column index complete and in order - } ) - .toArray(); - - if ( jqResult.length || ! s.nodeName ) { - return jqResult; - } - - // Otherwise a node which might have a `dt-column` data attribute, or be - // a child or such an element - var host = $(s).closest('*[data-dt-column]'); - return host.length ? - [ host.data('dt-column') ] : - []; - }; - - return _selector_run( 'column', selector, run, settings, opts ); - }; - - - var __setColumnVis = function ( settings, column, vis ) { - var - cols = settings.aoColumns, - col = cols[ column ], - data = settings.aoData, - row, cells, i, ien, tr; - - // Get - if ( vis === undefined ) { - return col.bVisible; - } - - // Set - // No change - if ( col.bVisible === vis ) { - return; - } - - if ( vis ) { - // Insert column - // Need to decide if we should use appendChild or insertBefore - var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 ); - - for ( i=0, ien=data.length ; i iThat; - } - - return true; - }; - - - /** - * Check if a `
    与 HTML - WORLD || 世界 -HTML - WORLD_LOAD || 世界载入 +HTML - WORLD_LOAD || 世界加载 HTML - WORLD_PLAYTIME || 世界游玩时间 HTML - WORST_PING || 最高延迟 HTML ERRORS - ACCESS_DENIED_403 || 拒绝访问 HTML ERRORS - ANALYSIS_REFRESH || 正在刷新分析··· HTML ERRORS - ANALYSIS_REFRESH_LONG || 正在执行分析,请在几秒后刷新页面··· -HTML ERRORS - AUTH_FAIL_TIPS_401 || - 确保阁下已使用 /plan register 注册用户
    - 检查用户名与密码是否正确
    - 用户名与密码区分大小写

    若阁下忘记了密码,请让工作人员删除阁下的旧密码并重新注册。 -HTML ERRORS - AUTHENTICATION_FAIlED_401 || 认证失败。 +HTML ERRORS - AUTH_FAIL_TIPS_401 || - 确保您已使用 /plan register 注册用户
    - 检查用户名与密码是否正确
    - 用户名与密码区分大小写

    若您忘记了密码,请让工作人员删除您的旧密码并重新注册。 +HTML ERRORS - AUTHENTICATION_FAILED_401 || 认证失败。 HTML ERRORS - FORBIDDEN_403 || 禁止访问 HTML ERRORS - NO_SERVERS_404 || 无可执行此请求的在线服务器。 HTML ERRORS - NOT_FOUND_404 || 未找到 HTML ERRORS - NOT_PLAYED_404 || 玩家从未在此服务器上游玩过。 HTML ERRORS - PAGE_NOT_FOUND_404 || 页面不存在。 HTML ERRORS - UNAUTHORIZED_401 || 未认证 -HTML ERRORS - UNKNOWN_PAGE_404 || 请确保阁下正通过命令所给出的链接访问,示例:

    /player/玩家名
    /server/服务器名

    +HTML ERRORS - UNKNOWN_PAGE_404 || 请确保您正通过命令所给出的链接访问,示例:

    /player/玩家名
    /server/服务器名

    HTML ERRORS - UUID_404 || 未在数据库中找到玩家的 UUID。 In Depth Help - /plan ? || §2/plan - 主命令\§f 用于访问所有子命令及帮助\§7 /plan - 列出子命令\§7 /plan <子命令> ? - 详细帮助 In Depth Help - /plan analyze ? || §2分析命令\§f 用于刷新分析缓存及访问结果页面\§7 /plan status 可用于在网页在线时检查分析状态fen.\§7 别名:analyze, analyse, analysis, a @@ -266,7 +266,7 @@ In Depth Help - /plan manage disable ? || > §2禁用命令\ 用于在 In Depth Help - /plan manage import ? || §2管理导入命令\§f 用于从其它来源导入数据\§7 在导入过程中将禁用数据分析。 In Depth Help - /plan manage move ? || > §2移动指令\ 将数据从 SQLite 移动至 MySQL,反之亦然。\ 目标数据库将在转移前被清除。 In Depth Help - /plan manage remove ? || §2管理移除命令\§f 用于从活跃数据库中移除用户数据。 -In Depth Help - /plan manage restore ? || > §2恢复命令\ 从先前备份的 SQLite 数据库中恢复数据(.db 文件)\ 阁下也可以从其他服务器中恢复 database.db 到 MySQL。\ 目标数据库将在传输前被清除。 +In Depth Help - /plan manage restore ? || > §2恢复命令\ 从先前备份的 SQLite 数据库中恢复数据(.db 文件)\ 您也可以从其他服务器中恢复 database.db 到 MySQL。\ 目标数据库将在传输前被清除。 In Depth Help - /plan manage setup ? || > §2安装命令\ 设置 Bungee 与此服务器之间的网络连接。\ Bungee 地址可在计划于 Bungee 上启动时的控制台日志中找到。 In Depth Help - /plan network ? || > §2网络命令\ 显示到网络页的链接。\ 若不在群组网络上,此页面将显示为服务器页面。 In Depth Help - /plan players ? || > §2玩家命令\ 显示到玩家页的链接。 @@ -289,10 +289,10 @@ Manage - Fail Same Database || > §c[计划] 无法移动至 Manage - Fail, Confirmation || > §c[计划] 请添加 -a 以确认执行!${0} Manage - Fail, Connection Exception || §e失败原因: Manage - Fail, No Servers || §c未在数据库中找到服务器。 -Manage - Fail, Old version || §e失败原因:接收服务器正运行计划的老版本。 -Manage - Fail, Unauthorized || §e失败原因:未授权。服务器可能使用不同的数据库。 +Manage - Fail, Old version || §e失败原因:接收服务器使用了旧版本的插件。 +Manage - Fail, Unauthorized || §e失败原因:未获得授权。服务器可能使用不同的数据库。 Manage - Fail, Unexpected Exception || §e异常例外:${0} -Manage - List Importers || 导入器: +Manage - List Importers || 导入器: Manage - Notify External Url || §e非本地地址,请检查端口是否开放 Manage - Remind HotSwap || §e[计划] 请记住要切换至新数据库并重新载入插件(/plan m hotswap ${0}) Manage - Start || »§7 正在处理数据··· @@ -301,11 +301,11 @@ Negative || 否 Positive || 是 Unknown || 未知 Version - DEV || 这是开发者版本。 -Version - Latest || 阁下正使用最新版本。 +Version - Latest || 您正使用最新版本。 Version - New || 新版本(${0})可用 ${1} Version - New (old) || 新版本在 ${0} 可用 Version FAIL - Read info (old) || 无法检查最新版本号 -Version FAIL - Read versions.txt || 无法从 Github/versions.txt 处载入版本信息 +Version FAIL - Read versions.txt || 无法从 Github/versions.txt 载入版本信息 Web User Listing || §2${0} §7:§f${1} WebServer - Notify HTTP || 网页服务器:无证书 -> 正使用 HTTP 服务器提供可视化效果。 WebServer - Notify HTTP User Auth || 网页服务器:已禁用用户认证!(HTTP 方式不安全) @@ -313,25 +313,25 @@ WebServer - Notify no Cert file || 网页服务器:未找到 WebServer FAIL - Port Bind || 未成功初始化网页服务器。端口(${0})是否被占用? WebServer FAIL - SSL Context || 网页服务器:SSL 环境初始化失败。 WebServer FAIL - Store Load || 网页服务器:SSL 证书载入失败。 -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file +Yesterday || '昨天' +Today || '今天' +Health - Active Playtime Comparison Decrease || 玩家可能闲得没事干了 (活跃玩家数 ${0} vs ${1}, 最近2周 vs 2-4周) +Health - Active Playtime Comparison Increase || 玩家可能有很多事要做 (活跃玩家数 ${0} vs ${1}, 最近2周 vs 2-4周) +Health - Downtime || 服务器停机时间为 (无数据传输的时间) ${0} +Health - New Player Join Players, No || 新玩家里也许很少有人继续玩下去 (平均为 ${0} ) +Health - New Player Join Players, Yes || 新玩家里有人玩了下去 (平均为 ${0} ) +Health - New Player Stickiness || ${0} 的新玩家留了下来 (${1}/${2}) +Health - No Servers Inaccuracy || 没有可收集数据的 Bukkit/Sponge 服务器 - 这些数据不准确. +Health - Player Play on Network || 在这个服务器网络内游玩的玩家: +Health - Player Register Server || 平均每日每服务器注册玩家数. +Health - Player Visit Server || 平均每日每服务器登录服务器玩家数. +Health - Regular Activity Change || 普通玩家数 +Health - Regular Activity Change Decrease || 减少了 (${0}) +Health - Regular Activity Change Increase || 增加了 (+${0}) +Health - Regular Activity Change Zero || 保持不变 (+${0}) +Health - Regular Activity Remain || ${0} 普通玩家仍保持活跃 (${1}/${2}) +Health - Single Servers Inaccuracy || 收集单个 Bukkit/Sponge 服务器的时域数据. +Health - TPS Above Low Threshold || ${0} 的平均TPS超过了最低门槛 +Health - TPS Low Dips || 平均TPS低于最低门槛 (${0}) ${1} 此 +HTML - FREE_DISK_SPACE || 剩余磁盘空间 +HTML - DISK_SPACE || 磁盘空间 \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_CZ.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_CZ.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_CZ.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt index 7d410e229..571043643 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt @@ -245,7 +245,7 @@ HTML ERRORS - ACCESS_DENIED_403 || Zugriff verweigert HTML ERRORS - ANALYSIS_REFRESH || Plan wird aktualisiert... HTML ERRORS - ANALYSIS_REFRESH_LONG || Plan wird ausgeführt. Es wird in ein paar Sekunden neu geladen. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Stelle sicher, dass du einen Account mit /plan register hinzugefügt hast.
    - Überprüfe, ob Passwort und Benutzername korrekt sind
    - Bei Benutzername und Passwort auf Groß- und Kleinschreibung achten!

    - Wenn du dein Passwort vergessen hast, bitte ein Teammitglied deinen Account zu löschen und neu zu erstellen. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentifizierung fehlgeschlagen. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentifizierung fehlgeschlagen. HTML ERRORS - FORBIDDEN_403 || Verboten HTML ERRORS - NO_SERVERS_404 || Keine Server online, die die Anfrage ausführen können. HTML ERRORS - NOT_FOUND_404 || Nicht gefunden. diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt index 2d9750a68..f3308126b 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt @@ -245,7 +245,7 @@ HTML ERRORS - ACCESS_DENIED_403 || Access Denied HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentication Failed. HTML ERRORS - FORBIDDEN_403 || Forbidden HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. HTML ERRORS - NOT_FOUND_404 || Not Found diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt index b237353a5..ef57fff47 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt @@ -244,7 +244,7 @@ HTML ERRORS - ACCESS_DENIED_403 || Pääsy Kielletty HTML ERRORS - ANALYSIS_REFRESH || Analyysiä suoritetaan.. HTML ERRORS - ANALYSIS_REFRESH_LONG || Analyysiä suoritetaan, sivu päivittyy hetken kuluttua.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Varmista että olet rekisteröinyt käyttäjän komennolla /plan register
    - Tarkista että käyttäjänimi ja salaasana ovat oikein
    - Nimi ja salasana ovat CASE SENSITIVE

    Jos unohdit salasanasi, pyydä valvojia poistamaan käyttäjäsi ja uudelleenrekisteröidy. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Autentikaatio ei onnistunut. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Autentikaatio ei onnistunut. HTML ERRORS - FORBIDDEN_403 || Kielletty HTML ERRORS - NO_SERVERS_404 || Ei palvelimia jolla toiminto voitaisiin suorittaa. HTML ERRORS - NOT_FOUND_404 || Ei löytynyt diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt index 2d9750a68..63abee222 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt @@ -1,336 +1,336 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths +Cmd - Click Me || Cliquez ici +Cmd - Link || §2Lien : §f +Cmd Disable - Disabled || §aLes systèmes de Plan sont maintenant désactivés. Vous pouvez toujours exécuter la commande '/planbungee reload' pour les redémarrer. +Cmd FAIL - Invalid Username || §cCet utilisateur ne possède pas d'UUID. +Cmd FAIL - No Feature || §eDéfinir une fonctionnalité à désactiver ! (supporte actuellement ${0}) +Cmd FAIL - No Permission || §cVous ne possédez pas la permission requise. +Cmd FAIL - Require only one Argument || §cUn argument est requis ${1} +Cmd FAIL - Requires Arguments || §cDes arguments sont requis (${0}) ${1} +Cmd FAIL - Unknown Username || §cCet utilisateur ne s'est jamais connecté sur ce serveur. +Cmd FAIL - WebUser does not exists || §cCet utilisateur n'existe pas ! +Cmd FAIL - WebUser exists || §cCet utilisateur existe déjà ! +Cmd Header - Analysis || > §2Résultats de l'analyse +Cmd Header - Info || > §2Analyse du joueur +Cmd Header - Inspect || > §2Joueur : §f${0} +Cmd Header - Network || > §2Page du réseau +Cmd Header - Players || > §2Joueurs +Cmd Header - Search || > §2${0} Résultats pour §f${1}§2: +Cmd Header - Servers || > §2Serveurs +Cmd Header - Web Users || > §2${0} Utilisateurs Web +Cmd Info - Bungee Connection || §2Connecté à Bungee : §f${0} +Cmd Info - Database || §2Base de données active : §f${0} +Cmd Info - Reload Complete || §aRechargement terminé. +Cmd Info - Reload Failed || §cUne erreur s'est produite lors du rechargement du plugin, un redémarrage total est recommandé. +Cmd Info - Update || §2Mise à jour disponible : §f${0} +Cmd Info - Version || §2Version : §f${0} +Cmd Notify - No WebUser || Vous n'avez peut-être pas d'utilisateur Web, essayez d'exécuter '/plan register ' afin d'y remédier. +Cmd Notify - WebUser register || Nouvel utilisateur enregistré : '${0}' Niveau de permission : ${1}. +Cmd Qinspect - Activity Index || §2Indice d'activité : §f${0} | ${1} +Cmd Qinspect - Deaths || §2Morts : §f${0} +Cmd Qinspect - Geolocation || §2Connecté depuis : §f${0} +Cmd Qinspect - Last Seen || §2Vu pour la dernière fois : §f${0} +Cmd Qinspect - Longest Session || §2Session la plus longue : §f${0} +Cmd Qinspect - Mob Kills || §2Kills de Mobs : §f${0} +Cmd Qinspect - Player Kills || §2Kills de Joueurs : §f${0} +Cmd Qinspect - Playtime || §2Temps de jeu : §f${0} +Cmd Qinspect - Registered || §2Incrit depuis : §f${0} +Cmd Qinspect - Times Kicked || §2Nombre d'éjections : §f${0} +Cmd Setup - Allowed || §aLa configuration est maintenant autorisée. +Cmd Setup - Bad Request || §eLa connexion a réussi, mais le serveur de réception n'était pas un serveur Bungee. Utilisez l'adresse du Bungee à la place. +Cmd Setup - Disallowed || §cLa configuration est maintenant interdite. +Cmd Setup - Forbidden || §eConnexion réussie, mais le mode de configuration de Bungee est désactivé. Exécutez '/plan bungee setup' pour l'activer. +Cmd Setup - Gateway Error || §eConnexion réussie, mais Bungee n'a pas réussi à se connecter à ce serveur (Le serveur Web actuel a-t-il redémarré ?). Exécutez '/plan m con' et '/planbungee con' pour déboguer. +Cmd Setup - Generic Fail || §eConnexion échouée : ${0} +Cmd Setup - Internal Error || §eConnexion échouée : ${0}, Vérifiez des possibles erreurs sur la page de débogage du serveur de réception. +Cmd Setup - Success || §aConnexion réussie, Plan peut redémarrer dans quelques secondes. +Cmd Setup - Unauthorized || §eConnexion réussie, mais le serveur de réception n'a pas autorisé ce serveur. Contactez-nous sur Discord pour obtenir de l'aide. +Cmd Setup - Url mistake || §cAssurez-vous d'utiliser l'adresse complète (commençant par http:// ou https://). Consultez les logs du Bungee pour la connaître. +Cmd Setup - WebServer not Enabled || §cLe serveur Web n'est pas activé sur ce serveur ! Assurez-vous qu'il se soit démarré lors du lancement du serveur ! +Cmd SUCCESS - Feature disabled || §aFontionnalité '${0}' temporairement désactivée jusqu'au prochain rechargement du plugin. +Cmd SUCCESS - WebUser register || §aAjout d'un nouvel utilisateur (${0}) avec succès ! +Cmd Update - Cancel Success || §aAnnulation de l'opération... +Cmd Update - Cancelled || §cMise à jour annulée. +Cmd Update - Change log || Changements v${0}: +Cmd Update - Fail Cacnel || §cLa mise à jour a échoué sur un serveur, annulant la mise à jour sur tous les serveurs. +Cmd Update - Fail Force Notify || §e${0} échec de la mise à jour : force spécifiée, continuation. +Cmd Update - Fail Not Online || §cTous les serveurs n'étant pas en ligne ou accessibles, vous pouvez toujours mettre à jour les serveurs disponibles en exécutant '/plan update -u -force'. +Cmd Update - Notify Cancel || §aVous pouvez annuler la mise à jour sur des serveurs n'ayant pas encore redémarré en exécutant '/plan update cancel'. +Cmd Update - Online Check || Vérification que tous les serveurs soient en ligne... +Cmd Update - Scheduled || §a${0} prévu pour la mise à jour. +Cmd Update - Url mismatch || §cL'URL de téléchargement de la version ne commence pas par ${0} et n'est peut-être pas fiable. Vous pouvez télécharger cette version manuellement ici (téléchargement direct) : +Cmd Web - Permission Levels || >\§70: Accéder à toutes les pages.\§71: Accéder au '/players' et à toutes les pages des joueurs.\§72: Accéder à la page du joueur avec le même nom d'utilisateur que l'utilisateur Web.\§73+: Pas de permission. +Command Help - /plan analyze || Voir la page du serveur. +Command Help - /plan dev || Commande du mode de développement. +Command Help - /plan help || Afficher la liste des commandes. +Command Help - /plan info || Vérifier la version de Plan. +Command Help - /plan inspect || Visionner la page d'un joueur. +Command Help - /plan manage || Gérer la base de données de Plan. +Command Help - /plan manage backup || Sauvegarder une base de données. +Command Help - /plan manage clear || Effacer une base de données. +Command Help - /plan manage con || Déboguer la connexion Serveur -> Bungee. +Command Help - /plan manage disable || Désactiver temporairement une fonctionnalité. +Command Help - /plan manage hotswap || Changer rapidement de base de données. +Command Help - /plan manage import || Importer des données externes. +Command Help - /plan manage move || Déplacer des données entre des bases de données. +Command Help - /plan manage remove || Supprimer les données d'un joueur. +Command Help - /plan manage restore || Restaurer une sauvegarde précédente. +Command Help - /plan manage setup || Configuration de la connexion Serveur -> Bungee. +Command Help - /plan network || Visionner la page du réseau. +Command Help - /plan players || Visionner la page des joueurs. +Command Help - /plan qinspect || Visionner les informations d'un joueur (en jeu). +Command Help - /plan register || Enregistrer un utilisateur Web. +Command Help - /plan reload || Recharger Plan. +Command Help - /plan search || Rechercher un joueur. +Command Help - /plan servers || Obtenir la liste des serveurs dans la base de données. +Command Help - /plan update || Obtenir le lien des modifications ou du plugin mis à jour. +Command Help - /plan web check || Inspecter un utilisateur Web. +Command Help - /plan web delete || Supprimer un utilisateur Web. +Command Help - /plan web level || Informations sur les niveaux de permission. +Command Help - /plan web list || Liste des utilisateurs Web. +Command Help - /plan webuser || Gérer les utilisateurs Web. +Command Help - /planbungee con || Déboguer la connexion Bungee -> Serveur. +Command Help - /planbungee disable || Désactiver temporairement Plan. +Command Help - /planbungee setup || Basculer le mode de configuration (on/off). +Database - Apply Patch || Application de correctifs : ${0}... +Database - Patches Applied || Tous les correctifs pour la base de données ont été appliqués avec succès. +Database - Patches Applied Already || Tous les correctifs pour la base de données ont déjà été appliqués. +Database MySQL - Launch Options Error || Configurations défectueuses, utilisation de celles par défaut (${0}) +Database Notify - Clean || Suppression des données de ${0} joueurs. +Database Notify - SQLite No WAL || Le mode WAL de SQLite n'est pas pris en charge sur cette version du serveur, en utilisant le mode par défaut. Cela peut possiblement affecter les performances. +Disable || Plan a été désactivé. +Disable - Processing || Traitement des tâches critiques non traitées. (${0}) +Disable - Processing Complete || Traitement complété. +Disable - WebServer || Le serveur Web a été désactivé. +Enable || Plan a été activé. +Enable - Database || Connexion à la base de données établie. (${0}) +Enable - Notify Address Confirmation || Assurez-vous que cette adresse pointe vers ce serveur : ${0}. +Enable - Notify Empty IP || L'adresse IP située dans le fichier 'server.properties' est vide et l'option 'AlternativeIP' n'est pas utilisée. Attention, des liens incorrects seront donnés ! +Enable - Notify Geolocations disabled || La géolocalisation n'est pas active. (Data.Geolocations: false) +Enable - Notify Geolocations Internet Required || Plan nécessite un accès à Internet lors de sa première utilisation pour télécharger la base de données 'GeoLite2 Geolocation'. +Enable - Notify Webserver disabled || Le serveur Web n'a pas été initialisé. (WebServer.DisableWebServer: true) +Enable - WebServer || Le serveur Web communique à travers le port ${0} (${1}). +Enable FAIL - Database || La connexion à la base de données a échoué. (${0}) : ${1} +Enable FAIL - Database Patch || L'application de correctifs pour la base de données a échoué, le plugin doit être désactivé. Merci de signaler ce problème. +Enable FAIL - GeoDB Write || Une erreur s'est produite lors de l'enregistrement de la base de données 'GeoLite2 Geolocation' téléchargée précédemment. +Enable FAIL - WebServer (Bungee) || Le serveur Web n'a pas été initialisé ! +Enable FAIL - Wrong Database Type || ${0} n'est pas une base de données prise en charge. +HTML - ACTIVITY_INDEX || Indice d'activité +HTML - ALL || TOUT +HTML - ALL_TIME_PEAK || Pic maximal de joueurs connectés +HTML - AVERAGE_PING || Latence moyenne +HTML - AVG || MOYEN.NE +HTML - BANNED || Banni +HTML - BEST_PING || Meilleure latence +HTML - CALENDAR || CALENDRIER +HTML - CALENDAR_TEXT || Calendrier +HTML - CHUNKS || Tronçons +HTML - COMMAND || Commande +HTML - COMMNAND_USAGE || Utilisation des commandes +HTML - CONNECTION_INFORMATION || Informations de connexion +HTML - COUNTRY || Pays +HTML - CURRENT_PLAYERBASE || Base de joueurs actuelle +HTML - DEATHS || Morts +HTML - ENTITIES || Entités +HTML - ERROR || Authentification échouée à cause d'une erreur +HTML - FAVORITE_SERVER || Serveur favori +HTML - GEOLOCATION || Géolocalisation +HTML - GEOLOCATION_TEXT || Géolocalisation +HTML - HEALTH_ESTIMATE || Estimation de la santé du serveur +HTML - INDEX_ACTIVE || Actif +HTML - INDEX_INACTIVE || Inactif +HTML - INDEX_IRREGULAR || Irrégulier +HTML - INDEX_REGULAR || Régulier +HTML - INDEX_VERY_ACTIVE || Très actif +HTML - IP_ADDRESS || Adresse IP +HTML - KILLED || Tué +HTML - KILLED_BY || Tué par +HTML - LAST_24_HOURS || 24 DERNIÈRES HEURES +HTML - LAST_30_DAYS || 30 DERNIERS JOURS +HTML - LAST_30_DAYS_TEXT || 30 derniers jours +HTML - LAST_7_DAYS || 7 DERNIERS JOURS +HTML - LAST_CONNECTED || Dernier connecté +HTML - LAST_PEAK || Dernier pic de joueurs connectés +HTML - LAST_SEEN || DERNIÈRE CONNEXION +HTML - LAST_SEEN_TEXT || Dernière connexion +HTML - LOADED_CHUNKS || Tronçons chargés +HTML - LOADED_ENTITIES || Entités chargés +HTML - LOCAL_MACHINE || Machine locale +HTML - LONGEST || Plus long.ue +HTML - LOW_TPS_SPIKES || Pointes basses des TPS +HTML - MOB_CAUSED_DEATHS || Morts causées par un Mob HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview +HTML - MOB_KILLS || Kills de Mobs +HTML - MOST_RECENT_SESSIONS || Sessions récentes +HTML - NAME || Nom +HTML - NAV_COMMAND_USAGE || Utilisation des commandes +HTML - NAV_GEOLOCATIONS || Géolocalisation +HTML - NAV_INFORMATION || Informations +HTML - NAV_NETWORK_PLAYERS || Joueurs du réseau +HTML - NAV_ONLINE_ACTIVITY || Activité en ligne +HTML - NAV_OVERVIEW || Vue d'ensemble HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players +HTML - NAV_PLAYERS || Joueurs HTML - NAV_PLUGINS || Plugins HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers +HTML - NAV_SEVER_HEALTH || Santé du serveur +HTML - NETWORK || Réseau +HTML - NETWORK_INFORMATION || INFORMATIONS SUR LE RÉSEAU +HTML - NEW || NOUVEAU(X) +HTML - NEW_CALENDAR || Nouveau : +HTML - NEW_PLAYERS_TEXT || Nouveaux joueurs +HTML - NEW_RETENTION || Rétention des nouveaux joueurs +HTML - NEW_TEXT || Nouveau +HTML - NICKNAME || Surnom +HTML - NO_KILLS || Pas de Kills +HTML - NO_PLAYER_CAUSED_DEATHS || Aucun joueur n'a causé de mort +HTML - OFFLINE || Hors-ligne +HTML - ONLINE || En ligne +HTML - ONLINE_ACTIVITY || ACTIVITÉ EN LIGNE +HTML - OPERATOR || Opérateur +HTML - OVERVIEW || Vue d'ensemble +HTML - PER_DAY || / Jour +HTML - PLAYER_CAUSED_DEATHS || Décès causés par le joueur +HTML - PLAYER_KILLS || Kills de joueurs +HTML - PLAYER_LIST || Liste des joueurs +HTML - PLAYERBASE_DEVELOPMENT || Évolution de la base de joueurs +HTML - PLAYERS || JOUEURS +HTML - PLAYERS_ONLINE || JOUEURS EN LIGNE +HTML - PLAYERS_ONLINE_TEXT || Joueurs en ligne +HTML - PLAYERS_TEXT || Joueurs +HTML - PLAYTIME || Temps de jeu +HTML - PLEASE_WAIT || Merci de patienter... +HTML - PREDICETED_RETENTION || Rétention prévue +HTML - PUNCH_CARD || Carte perforée +HTML - PUNCHCARD || CARTE PERFORÉE +HTML - RECENT_LOGINS || CONNEXIONS RÉCENTES +HTML - REGISTERED || INSCRIPTION +HTML - REGISTERED_TEXT || Inscription +HTML - REGULAR || RÉGULIER(S) +HTML - SEEN_NICKNAMES || Surnoms Vus +HTML - SERVER || Serveur +HTML - SERVER_ANALYSIS || Analyse du serveur +HTML - SERVER_HEALTH_ESTIMATE || Estimation de la santé du serveur +HTML - SERVER_INFORMATION || INFORMATIONS SUR LE SERVEUR +HTML - SERVER_PREFERENCE || Préférence de serveur +HTML - SERVERS || Serveurs HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median +HTML - SESSION_ENDED || Session terminée +HTML - SESSION_LENGTH || Durée de la session +HTML - SESSION_MEDIAN || Session médiane HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime +HTML - TIME || Temps +HTML - TIMES_KICKED || Nombre d'éjections +HTML - TIMES_USED || Nombre d'utilisations +HTML - TOTAL_ACTIVE_TEXT || Temps actif total +HTML - TOTAL_AFK || Temps inactif total +HTML - TOTAL_PLAYERS || Joueurs totaux +HTML - TOTAL_PLAYTIME || Temps de jeu total HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt +HTML - UNIQUE_CALENDAR || Unique : +HTML - UNIQUE_PLAYERS || JOUEURS UNIQUES +HTML - UNIQUE_PLAYERS_TEXT || Joueurs uniques +HTML - UNIQUE_TEXT || Unique(s) +HTML - USAGE || Utilisation +HTML - USED_COMMANDS || Commandes utilisées +HTML - USER_AND_PASS_NOT_SPECIFIED || Utilisateur et mot de passe non spécifiés +HTML - USER_DOES_NOT_EXIST || Cet utilisateur n'existe pas +HTML - USER_INFORMATION || INFORMATIONS SUR L'UTILISATEUR +HTML - USER_PASS_MISMATCH || L'utilisateur et le mot de passe ne correspondent pas +HTML - WITH ||
    Avec +HTML - WORLD || Monde +HTML - WORLD_LOAD || CHARGE DU MONDE +HTML - WORLD_PLAYTIME || Temps de jeu par monde +HTML - WORST_PING || Pire latence +HTML ERRORS - ACCESS_DENIED_403 || Accès refusé. +HTML ERRORS - ANALYSIS_REFRESH || L'analyseur est en cours de rafraîchissement... +HTML ERRORS - ANALYSIS_REFRESH_LONG || L'analyse est en cours d'exécution, actualisez la page après quelques secondes.... +HTML ERRORS - AUTH_FAIL_TIPS_401 || - Assurez-vous d'avoir enregistré un utilisateur avec :'/plan register'.
    - Vérifiez que le nom d'utilisateur et le mot de passe soient corrects.
    - Le nom d'utilisateur et le mot de passe sont sensibles au format majuscule/minuscule.

    Si vous avez oublié votre mot de passe, demandez à un membre du staff de supprimer votre ancien utilisateur puis de vous réinscrire. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentification échouée. +HTML ERRORS - FORBIDDEN_403 || Accès interdit. +HTML ERRORS - NO_SERVERS_404 || Aucun serveur en ligne pour exécuter la demande. +HTML ERRORS - NOT_FOUND_404 || Non trouvé. +HTML ERRORS - NOT_PLAYED_404 || Cet utilisateur ne s'est jamais connecté sur ce serveur. +HTML ERRORS - PAGE_NOT_FOUND_404 || Cette page n'existe pas. +HTML ERRORS - UNAUTHORIZED_401 || Non autorisé. +HTML ERRORS - UNKNOWN_PAGE_404 || Assurez-vous que vous accédez à un lien donné par une commande. Exemples :

    /player/PlayerName
    /server/ServerName

    +HTML ERRORS - UUID_404 || L'UUID de cet utilisateur n'a pas été trouvé dans la base de données. +In Depth Help - /plan ? || > §2Commande principale\ Accès aux sous-commandes et à l'aide\ §2/plan §fListe des sous-commandes\ §2/plan ? §fAide profonde +In Depth Help - /plan analyze ? || > §2Commande d'analyse\ Actualise la page du serveur et affiche un lien vers la page Web. +In Depth Help - /plan inspect ? || > §2Commande d'inspection\ Actualise la page du joueur et affiche un lien vers la page Web. +In Depth Help - /plan manage ? || > §2Commande de gestion\ Gestion des bases de données MySQL et SQLite de Plan.\ §2/plan m §fListe des sous-commandes\ §2/plan m ? §fAide en profondeur +In Depth Help - /plan manage backup ? || > §2Sous-commande de sauvegarde\ Crée une nouvelle base de données SQLite (fichier .db) avec le contenu de la base de données actuellement active dans la configuration de Plan. +In Depth Help - /plan manage clear ? || > §2Sous-commande d'effacement\ Supprime l'intégralité de la base de données active. À utiliser avec précaution. +In Depth Help - /plan manage con ? || > §2Sous-commande de débogage de connexion\ Utilisée pour déboguer les connexions du réseau.\ Envoie une demande à chaque serveur présent dans la base de données. +In Depth Help - /plan manage disable ? || > §2Sous-commande de désactivation\ Peut désactiver des parties du plugin jusqu'au prochain rechargement.\ Arguments acceptés :\§2kickcount §fDésactive le comptage d'éjections dans le cas où tous les joueurs ont été expulsés par un arrêt. +In Depth Help - /plan manage import ? || > §2Sous-commande d'importation\ Importe des données depuis d'autres sources.\ Arguments acceptés :\ §2offline §fDonnées du joueur Bukkit, uniquement la date d'enregistrement et le nom. +In Depth Help - /plan manage move ? || > §2Sous-commande de déplacement\ Déplace les données de SQLite vers MySQL ou autre.\ La base de données cible est effacée avant le transfert. +In Depth Help - /plan manage remove ? || > §2Sous-commande de suppression\ Supprime les données d'un joueur de la base de données active. +In Depth Help - /plan manage restore ? || > §2Sous-commande de restauration\ Restaure une sauvegarde de base de données SQLite précédente (fichier .db)\ Vous pouvez également restaurer le fichier database.db d'un autre serveur vers MySQL.\ La base de données cible est effacée avant le transfert. +In Depth Help - /plan manage setup ? || > §2Sous-commande de configuration\ Configure une connexion entre Bungee et ce serveur pour la fonctionnalité réseau.\ Vous pouvez trouver l'adresse du Bungee dans les logs de la la console lorsque Plan est activé sur Bungee. +In Depth Help - /plan network ? || > §2Sous-commande du réseau\ Affiche le lien vers la page du réseau.\ S'il ne s'agit pas d'un réseau, elle affiche la page du serveur. +In Depth Help - /plan players ? || > §2Commande des joueurs\ Affiche un lien vers la page des joueurs. +In Depth Help - /plan qinspect ? || > §2Quick Inspect CommandCommande d'inspection rapide\ Affiche des informations sur un joueur en jeu. +In Depth Help - /plan reload ? || > §2Commande de rechargement\ Redémarre le plugin en utilisant onDisable et onEnable.\ §bNe supporte pas l'échange de .jar à la volée (sans redémarrage complet) +In Depth Help - /plan search ? || > §2Commande de recherches\ Obtient une liste des joueurs correspondant à l'argument donné.\§7 Exemple: /plan search 123 - Recherche tous les utilisateurs dont le nom comporte '123'. +In Depth Help - /plan servers ? || > §2Commande des serveurs\ Affiche la liste des serveurs de Plan présents dans la base de données.\ Peut être utilisé pour résoudre les problèmes d'enregistrement de base de données sur un réseau. +In Depth Help - /plan update ? || > §2Commande de mise à jour\ Utilisé pour mettre à jour le plugin lors du prochain arrêt\ /plan update - Lien des modifications\ /plan update -u - Planifie la mise à jour sur tous les serveurs de réseau en ligne, au prochain redémarrage.\ /plan update cancel - Annule la mise à jour programmée sur les serveurs qui n'ont pas encore redémarré. +In Depth Help - /plan web ? || < §2Commande de la gestion d'utilisateurs Web\ §2/plan web §fListe des sous-commandes\ §2/plan web ? §fAide en profondeure +In Depth Help - /plan web register ? || > §2Commande d'enregistrement\ Enregistre un nouvel utilisateur Web.\ L'enregistrement d'un utilisateur pour un autre joueur nécessite l'autorisation 'plan.webmanage'.\ Les mots de passe sont hachés avec PBKDF2 (64000 itérations de SHA1) en utilisant 'un sel cryptographiquement aléatoire'. +In Depth Help - /planbungee disable ? || > §2Commande de désactivation\ Lance onDisable pour PlanBungee.\ Le plugin peut être activé par la suite en exécutant la commande /planbungee reload.\ §bNe supporte pas l'échange de .jar à la volée (sans redémarrage complet) +In Depth Help - /planbungee setup ? || > §2Commande de bascule de configuration\ Bascule le mode de configuration sur Bungee.\ Protégez-vous contre le 'fouinage' non autorisée de MySQL avec un autre serveur. +Manage - Confirm Overwrite || Les données ${0} seront écrasées ! +Manage - Confirm Removal || Les données ${0} seront supprimées ! +Manage - Fail || > §cUne erreur est survenue : ${0}. +Manage - Fail File not found || > §cAucun fichier trouvé depuis ${0}. +Manage - Fail Incorrect Database || > §c'${0}' n'est pas une base de données prise en charge. +Manage - Fail No Importer || §eL'importateur '${0}' n'existe pas. +Manage - Fail Same Database || > §cNe peut pas opérer sur et depuis la même base de données ! +Manage - Fail, Confirmation || > §cAjoutez l'argmenter '-a' afin de confirmer l'éxécution : ${0} +Manage - Fail, Connection Exception || §eRaison de l'échec : +Manage - Fail, No Servers || §cAucun serveur trouvé dans la base de données. +Manage - Fail, Old version || §eRaison de l'échec : Le serveur de réception utilise une ancienne version de Plan. +Manage - Fail, Unauthorized || §eRaison de l'échec : Non autorisé. Le serveur utilise peut-être une base de données différente. +Manage - Fail, Unexpected Exception || §eErreur : ${0}. +Manage - List Importers || Importateurs : +Manage - Notify External Url || §eAdresse non locale, vérifiez que le port soit ouvert. +Manage - Remind HotSwap || §eN'oubliez pas de passer à la nouvelle base de données (/plan m hotswap ${0}) & de recharger Plan. +Manage - Start || > §2Traitement des données... +Manage - Success || > §aSuccès ! +Negative || Non +Positive || Oui +Unknown || Inconnu +Version - DEV || Ceci est une version de développement. +Version - Latest || Vous utilisez la dernière version. +Version - New || Une nouvelle version (${0}) est disponible ${1} +Version - New (old) || Une nouvelle version est disponible depuis ${0} +Version FAIL - Read info (old) || Impossible de vérifier le dernier numéro de la version +Version FAIL - Read versions.txt || Les informations de la version n'ont pas pu être chargées depuis Github/versions.txt Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file +WebServer - Notify HTTP || Serveur Web : Aucun certificat -> Utilisation du serveur HTTP pour la visualisation. +WebServer - Notify HTTP User Auth || Serveur Web : Authentification utilisateur désactivée ! (Non sécurisée avec HTTP) +WebServer - Notify no Cert file || Serveur Web : Fichier KeyStore du certificat introuvable : ${0} +WebServer FAIL - Port Bind || Le Serveur Web n'a pas été initialisé avec succès. Le port (${0}) est-il actuellement utilisé ? +WebServer FAIL - SSL Context || Serveur Web : Échec d'initialisation du contexte SSL. +WebServer FAIL - Store Load || Serveur Web : Échec du chargement du certificat SSL. +Yesterday || 'Hier' +Today || 'Aujourd''hui' +Health - Active Playtime Comparison Decrease || Les joueurs actifs risquent de manquer de choses à faire (Joué ${0} contre ${1}, les deux dernières semaines par rapport aux semaines 2 à 4). +Health - Active Playtime Comparison Increase || Les joueurs actifs semblent avoir des choses à faire (Joué ${0} contre ${1}, les deux dernières semaines par rapport aux semaines 2 à 4). +Health - Downtime || Le temps total d'arrêt du serveur (absence de données) était de ${0}. +Health - New Player Join Players, No || Les nouveaux joueurs peuvent ne pas avoir de joueurs avec lesquels jouer (${0} en moyenne). +Health - New Player Join Players, Yes || Les nouveaux joueurs ont des joueurs avec lesquels jouer (${0} en moyenne). +Health - New Player Stickiness || ${0} des nouveaux joueurs sont restés (${1}/${2}). +Health - No Servers Inaccuracy || Aucun serveur Bukkit/Sponge pour collecter des données de session - Ces mesures sont inexactes. +Health - Player Play on Network || joueurs jouent sur le réseau : +Health - Player Register Server || joueurs s'enregistrent surle serveur par jour/serveur en moyenne. +Health - Player Visit Server || joueurs visitent le serveur par jour/serveur en moyenne. +Health - Regular Activity Change || Le nombre de joueurs réguliers a +Health - Regular Activity Change Decrease || diminué (${0}). +Health - Regular Activity Change Increase || augmenté (+${0}). +Health - Regular Activity Change Zero || stagné (+/-${0}). +Health - Regular Activity Remain || ${0} des joueurs réguliers sont restés actifs (${1}/${2}). +Health - Single Servers Inaccuracy || Un seul serveur Bukkit/Sponge pour collecter les données de session. +Health - TPS Above Low Threshold || Les TPS moyens étaient supérieurs au seuil bas ${0} du temps. +Health - TPS Low Dips || Les TPS moyens sont descendus sous le seuil bas (${0}) ${1} fois. +HTML - FREE_DISK_SPACE || Espace disque disponible +HTML - DISK_SPACE || ESPACE DISQUE diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_GA.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_GA.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_GA.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_IT.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_IT.txt index 2d9750a68..f3308126b 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_IT.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_IT.txt @@ -245,7 +245,7 @@ HTML ERRORS - ACCESS_DENIED_403 || Access Denied HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentication Failed. HTML ERRORS - FORBIDDEN_403 || Forbidden HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. HTML ERRORS - NOT_FOUND_404 || Not Found diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt new file mode 100644 index 000000000..112047d37 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt @@ -0,0 +1,340 @@ +Cmd - Click Me || ここをクリック +Cmd - Link || §2リンク: §f +Cmd Disable - Disabled || §a「Plan」は無効になりました。「/planbungee reload」コマンドを使ってプラグインを再起動できます +Cmd FAIL - Database not open || §cデータベースは${0}です - しばらくしてからもう一度お試し下さい +Cmd FAIL - Invalid Username || §cこのユーザーはUUIDを所持していません +Cmd FAIL - No Feature || §eこの機能は現在使用されていません! (現在、「${0}」をサポートしています) +Cmd FAIL - No Permission || §cあなたには実行する権限がありません +Cmd FAIL - Require only one Argument || §c一つの引数「${1}」が必要です +Cmd FAIL - Requires Arguments || §c引数「(${0}) ${1}」が必要です +Cmd FAIL - Unknown Username || §c入力されたユーザーはBukkit/Spigotサーバー上にいません +Cmd FAIL - WebUser does not exists || §c入力されたユーザーは存在しません! +Cmd FAIL - WebUser exists || §c入力されたユーザー名は既に使われています! +Cmd Header - Analysis || > §2分析結果 +Cmd Header - Info || > §2プレイヤーの分析結果 +Cmd Header - Inspect || > §2プレイヤー: §f${0} +Cmd Header - Network || > §2ネットワークページ +Cmd Header - Players || > §2プレイヤー +Cmd Header - Search || > §2${0} §f${1}§2 の結果: +Cmd Header - Servers || > §2サーバー +Cmd Header - Web Users || > §2${0} ウェブユーザー +Cmd Info - Bungee Connection || §2BungeeCordに接続済み: §f${0} +Cmd Info - Database || §2動作中のデータベース: §f${0} +Cmd Info - Reload Complete || §aリロードが完了しました +Cmd Info - Reload Failed || §cプラグインのリロード中に何らかの問題が発生しました、Bukkit/SpigotサーバーかBungeeCordの再起動をお勧めします +Cmd Info - Update || §2利用可能なアップデート: §f${0} +Cmd Info - Version || §2バージョン: §f${0} +Cmd Notify - No WebUser || ウェブユーザーの情報がない可能性があります、「/plan register 」を使用して登録して下さい +Cmd Notify - WebUser register || 登録が完了しました: '${0}' 権限レベル: ${1} +Cmd Qinspect - Activity Index || §2活動指数: §f${0} | ${1} +Cmd Qinspect - Deaths || §2死亡回数: §f${0} +Cmd Qinspect - Geolocation || §2出身: §f${0} +Cmd Qinspect - Last Seen || §2最終ログイン日: §f${0} +Cmd Qinspect - Longest Session || §2最長ログイン時間: §f${0} +Cmd Qinspect - Mob Kills || §2キルカウント(モブ): §f${0} +Cmd Qinspect - Player Kills || §2キルカウント(プレイヤー): §f${0} +Cmd Qinspect - Playtime || §2プレイ時間: §f${0} +Cmd Qinspect - Registered || §2登録日: §f${0} +Cmd Qinspect - Times Kicked || §2キックされた時間: §f${0} +Cmd Setup - Allowed || §aセットアップモードが有効になりました +Cmd Setup - Bad Request || §e接続は成功しましたが、受信サーバーはBungeeCordのサーバーではありませんでした。代わりにBungeeCordのアドレスを使用して下さい +Cmd Setup - Disallowed || §cセットアップモードが無効になりました +Cmd Setup - Forbidden || §e接続は成功しましたが、BungeeCordの「Plan」のセットアップモードが無効になっています。「/planbungee setup」コマンドを使用して有効にして下さい +Cmd Setup - Gateway Error || §e接続は成功しましたが、BungeeCordの「Plan」のウェブサーバーに接続できませんでした。(ウェブサーバーが再起動中かもしれません)。「/plan m con 」と「/planbungee con」でデバック情報を表示できます +Cmd Setup - Generic Fail || §e以下の理由で接続に失敗しました: ${0} +Cmd Setup - Internal Error || §e接続は成功しました。 ${0}、受信サーバーのデバッグページでエラーログを確認できるようになりました +Cmd Setup - Success || §a接続は成功しました。「Plan」は数秒以内に再起動します。 +Cmd Setup - Unauthorized || §e接続は成功しましたが、このBukkit/Spigotサーバーを受信サーバーが拒否しました。Discordのサポートに連絡して下さい +Cmd Setup - Url mistake || §cウェブアドレスの入力を確かめて下さい(「http://」もしくは「https://」で始まります )。アドレスについてはBungeeCordの「Plan」を有効化した時のログを確認して下さい。 +Cmd Setup - WebServer not Enabled || §cこのBukkit/Spigotサーバーでウェブサーバーは有効になっていません。Bukkit/Spigotサーバー起動時にウェブサーバーが有効になっていることを確認して下さい! +Cmd SUCCESS - Feature disabled || §a次にプラグインがリロードされるまで一時的に「${0}」を無効にしました。 +Cmd SUCCESS - WebUser register || §a新規ユーザー「(${0})」の登録に成功しました! +Cmd WARN - Database not open || §eデータベースは${0}です - 予想以上に時間がかかるかもしれません +Cmd Web - Permission Levels || >\§70: 全てのページにアクセスできます\§71:「/players」と全てのプレイヤーページにアクセスできます\§72: ウェブユーザーと同じユーザー名でプレイヤーページにアクセスできます\§73+:権限を保持していません +Command Help - /plan analyze || サーバーページのURLを表示します +Command Help - /plan dev || 開発モードコマンド +Command Help - /plan help || コマンドリストを表示します +Command Help - /plan info || 「Plan」のバージョンを表示します +Command Help - /plan inspect || 「プレイヤー」のURLを表示します +Command Help - /plan manage || 「Plan」のデータベースを管理します +Command Help - /plan manage backup || データベースをバックアップします +Command Help - /plan manage clear || データベースを消去します +Command Help - /plan manage con || BungeeCordとBukkit/Spigotサーバーとの接続をデバックします +Command Help - /plan manage disable || 機能を一時的に無効にします +Command Help - /plan manage export || 手動でデータのエクスポートを行います +Command Help - /plan manage hotswap || データベースを高速で変更します +Command Help - /plan manage import || 他の場所からデータをインポートします +Command Help - /plan manage move || データベース間でデータを移動します +Command Help - /plan manage raw || プレイヤーデータのJSONファイルを直接表示ます +Command Help - /plan manage remove || プレイヤーのデータを削除します +Command Help - /plan manage restore || 以前のバックアップから復元します +Command Help - /plan manage setup || BungeeCordとBukkit/Spigotサーバーとの接続をセットアップします +Command Help - /plan manage uninstalled || 指定されたサーバーをデータベースからアンインストールします +Command Help - /plan network || 「ネットワーク」のページのURLを表示します +Command Help - /plan players || 「プレイヤー」のページのURLを表示します +Command Help - /plan qinspect || プレイヤー情報をゲーム内で表示します +Command Help - /plan register || ウェブユーザーを登録します +Command Help - /plan reload || 「Plan」を再起動します +Command Help - /plan search || プレイヤー名を検索します +Command Help - /plan servers || データベース内のBukkit/Spigotサーバー一覧を表示します +Command Help - /plan web check || ウェブユーザーの点検を行います +Command Help - /plan web delete || ウェブユーザーの削除を行います +Command Help - /plan web level || 権限レベルに関する情報を表示します +Command Help - /plan web list || ウェブユーザーの一覧を表示します +Command Help - /plan webuser || ウェブユーザーを管理します +Command Help - /planbungee con || BungeeとBukkit/Spigotサーバーとの接続をデバックします +Command Help - /planbungee disable || プラグインを一時的に無効にします +Command Help - /planbungee setup || セットアップモードを切り替えます +Database - Apply Patch || 次のパッチを適用しています: ${0}.. +Database - Patches Applied || 全てのパッチは正常にデータベースに適用されました +Database - Patches Applied Already || 全てのパッチは既にデータベースへ適用されています +Database MySQL - Launch Options Error || 起動オプションに問題があります,デフォルトのオプションを使用して下さい (${0}) +Database Notify - Clean || ${0} のプレイヤーデータを削除しています +Database Notify - SQLite No WAL || SQLiteのWALモードはこのサーバのバージョンではサポートされていないため、デフォルトを使用します。これはサーバーのパフォーマンスに影響を与える可能性があります +Disable || プレイヤー分析が無効になりました +Disable - Processing || 未実行の重要な処理があります (${0}) +Disable - Processing Complete || 処理が完了しました +Disable - Unsaved Session Save || 未保存のセッションを保存しています・・ +Disable - WebServer || ウェブサーバーが無効になりました +Enable || プレイヤー分析が有効になりました +Enable - Database || ${0}のデータベースの接続が確立しました +Enable - Notify Address Confirmation || 次に表示されるアドレスがこのサーバーをである事を確認して下さい: ${0} +Enable - Notify Empty IP || server.propertiesの設定で、IPの項目が設定されておらずAlternative IPが使用されていません。そのため誤ったリンクが表示されます! +Enable - Notify Geolocations disabled || 位置情報サービスが有効ではありません。 (Data.Geolocations: false) +Enable - Notify Geolocations Internet Required || 「Plan」は初回起動時、「GeoLite2」の位置情報データベースをダウンロードするためインターネットアクセスが必要です +Enable - Notify Webserver disabled || ウェブサーバーの初期化に失敗しました (WebServer.DisableWebServer: true) +Enable - WebServer || ウェブサーバーは次のポートで実行されています: ${0} (${1}) +Enable FAIL - Database || ${0}のデータベースの接続に失敗しました: ${1} +Enable FAIL - Database Patch || データベースのパッチ適用に失敗しました、プラグインを無効にする必要があります。バグ報告をお願いします +Enable FAIL - GeoDB Write || ダウンロードした「GeoLite2」の位置情報データベースを保存中に何らかのエラーが発生しました +Enable FAIL - WebServer (Bungee) || ウェブサーバーの初期化に失敗しました! +Enable FAIL - Wrong Database Type || ${0}はサポートされていないデータベースです +Health - Active Playtime Comparison Decrease || よくログインしているプレイヤーのやることが少なくなっている可能性があります (直近2週間のアクティブ時間が${0}に対し、1ヶ月~2週間のアクティブ時間が${1}です) +Health - Active Playtime Comparison Increase || よくログインしているプレイヤーのやるべきことがある可能性があります (直近2週間のアクティブ時間が${0}に対し、1ヶ月~2週間のアクティブ時間が${1}です) +Health - Downtime || サーバーの合計停止時間(データが存在しない)が${0}です +Health - New Player Join Players, No || 新規プレイヤーが参加時、一緒にプレイするプレイヤーがいないかもしれません。(平均${0}人のプレイヤーがオンラインです) +Health - New Player Join Players, Yes || 新規プレイヤーが参加時、一緒にプレイするプレイヤーがいます。(平均${0}人のプレイヤーがオンラインです) +Health - New Player Stickiness || ${0}人の新規プレイヤーが行き詰詰まっています。(${1}/${2}) +Health - No Servers Inaccuracy || セッションデータを収集するBukkit/Spongeサーバーが接続されていません - 不正確なデータが表示されます +Health - Player Play on Network || 人のプレイヤーがネットワーク上でプレイしています: +Health - Player Register Server || 人のプレイヤーが1日あたり登録されています。以下サーバーごとの1日の登録数: +Health - Player Visit Server || 人のプレイヤーが1日あたりこのサーバーに接続しています。以下サーバーごとの1日の接続数: +Health - Regular Activity Change || しばしばログインしているプレイヤー数は +Health - Regular Activity Change Decrease || ${0}人減少しました (-${0}人) +Health - Regular Activity Change Increase || ${0}人増加しました (+${0}人) +Health - Regular Activity Change Zero || 変化していません (+${0}人) +Health - Regular Activity Remain || ${0}人のしばしばログインしているプレイヤーはよくログインするようになりました(${1}/${2}) +Health - Single Servers Inaccuracy || セッションデータを収集するためのBukkit/Spongeサーバーが1つしか存在しません +Health - TPS Above Low Threshold || 平均TPSの${0}が平均TPSの下限しきい値より高い値を示しています +Health - TPS Low Dips || 平均TPSの下限しきい値を下回った回数:${1}回(以前の値:${0}回) +HTML - ACTIVITY_INDEX || 活動指数 +HTML - ALL || 全て +HTML - ALL_TIME_PEAK || 全体のピークタイム +HTML - AVERAGE_PING || 平均応答時間 +HTML - AVG || 平均 +HTML - BANNED || BAN履歴 +HTML - BEST_PING || 最低応答時間 +HTML - CALENDAR || カレンダー +HTML - CALENDAR_TEXT || カレンダー +HTML - CHUNKS || チャンク +HTML - COMMAND || コマンド +HTML - COMMNAND_USAGE || コマンド使用履歴 +HTML - CONNECTION_INFORMATION || 接続情報 +HTML - COUNTRY || 国/地域 +HTML - CURRENT_PLAYERBASE || ログインプレイヤー +HTML - DATABASE_NOT_OPEN || データベースを開くことができませんでした。「/plan info」コマンドを実行して状況を確認して下さい +HTML - DEATHS || 死亡回数 +HTML - DISK_SPACE || ドライブの容量 +HTML - ENTITIES || エンティティ数 +HTML - ERROR || エラーが発生したため認証に失敗しました +HTML - FAVORITE_SERVER || お気に入りのサーバー +HTML - FREE_DISK_SPACE || ドライブの空き容量 +HTML - GEOLOCATION || 地域 +HTML - GEOLOCATION_TEXT || 地域 +HTML - HEALTH_ESTIMATE || ネットワークの健康状態 +HTML - INDEX_ACTIVE || よくログインしている +HTML - INDEX_INACTIVE || 休止中 +HTML - INDEX_IRREGULAR || たまにログインしている +HTML - INDEX_REGULAR || しばしばログインしている +HTML - INDEX_VERY_ACTIVE || とてもログインしている +HTML - IP_ADDRESS || IPアドレス +HTML - KILLED || 殺した人 +HTML - KILLED_BY || 殺された人 +HTML - LAST_24_HOURS || 直近24時間以内 +HTML - LAST_30_DAYS || 直近30日以内 +HTML - LAST_30_DAYS_TEXT || 直近30日以内の出来事 +HTML - LAST_7_DAYS || 一週間以内 +HTML - LAST_CONNECTED || 直近の接続 +HTML - LAST_PEAK || 直近のピークタイム +HTML - LAST_SEEN || 直近のオンライン +HTML - LAST_SEEN_TEXT || 直近のオンライン +HTML - LOADED_CHUNKS || ロードされたチャンク数 +HTML - LOADED_ENTITIES || ロードされたエンティ数 +HTML - LOCAL_MACHINE || ローカルマシン +HTML - LONGEST || 最長 +HTML - LOW_TPS_SPIKES || 最低TPS値 +HTML - MOB_CAUSED_DEATHS || Mobによって殺された回数 +HTML - MOB_KDR || Mobに対してのKDR +HTML - MOB_KILLS || Mobを殺した回数 +HTML - MOST_RECENT_SESSIONS || 最近のログイン +HTML - NAME || 名前 +HTML - NAV_COMMAND_USAGE || コマンド使用履歴 +HTML - NAV_GEOLOCATIONS || 地域 +HTML - NAV_INFORMATION || 概要 +HTML - NAV_NETWORK_PLAYERS || ネットワーク内のプレイヤー数 +HTML - NAV_ONLINE_ACTIVITY || 活動履歴 +HTML - NAV_OVERVIEW || 概要 +HTML - NAV_PERFORMANCE || パフォーマンス +HTML - NAV_PLAYERS || プレイヤー +HTML - NAV_PLUGINS || プラグイン +HTML - NAV_SESSIONS || 接続履歴 +HTML - NAV_SEVER_HEALTH || サーバーの健康状態 +HTML - NETWORK || ネットワーク +HTML - NETWORK_INFORMATION || ネットワーク内の情報 +HTML - NEW || 新規 +HTML - NEW_CALENDAR || New: +HTML - NEW_PLAYERS_TEXT || 新規プレイヤー +HTML - NEW_RETENTION || 新規プレイヤーの継続率 +HTML - NEW_TEXT || 新規 +HTML - NICKNAME || ニックネーム +HTML - NO_KILLS || プレイヤーキルなし +HTML - NO_PLAYER_CAUSED_DEATHS || プレイヤーによるキルなし +HTML - OFFLINE || オフライン +HTML - ONLINE || オンライン +HTML - ONLINE_ACTIVITY || 活動指数 +HTML - OPERATOR || 管理者 +HTML - OVERVIEW || 概要 +HTML - PER_DAY || /日 +HTML - PLAYER_CAUSED_DEATHS || プレイヤーによるキル +HTML - PLAYER_KILLS || プレイヤーキル +HTML - PLAYER_LIST || プレイヤー一覧 +HTML - PLAYERBASE_DEVELOPMENT || 登録されているプレイヤーの推移 +HTML - PLAYERS || プレイヤー +HTML - PLAYERS_ONLINE || オンラインのプレイヤー +HTML - PLAYERS_ONLINE_TEXT || オンラインのプレイヤー +HTML - PLAYERS_TEXT || プレイヤー +HTML - PLAYTIME || プレイ時間 +HTML - PLEASE_WAIT || 少々お待ち下さい・・・ +HTML - PREDICETED_RETENTION || 推定される継続率 +HTML - PUNCH_CARD || パンチボード +HTML - PUNCHCARD || パンチボード +HTML - RECENT_LOGINS || 最近のログイン +HTML - REGISTERED || 登録日 +HTML - REGISTERED_TEXT || 登録 +HTML - REGULAR || よくいる +HTML - SEEN_NICKNAMES || ニックネーム一覧 +HTML - SERVER || サーバー +HTML - SERVER_ANALYSIS || サーバーの分析結果 +HTML - SERVER_HEALTH_ESTIMATE || サーバーの健康状態 +HTML - SERVER_INFORMATION || サーバーの情報 +HTML - SERVER_PREFERENCE || サーバー毎のプレイ時間 +HTML - SERVERS || 接続されているサーバー +HTML - SESSION || オンライン +HTML - SESSION_ENDED || ログアウト時刻 +HTML - SESSION_LENGTH || 最長オンライン +HTML - SESSION_MEDIAN || 平均オンライン +HTML - SESSIONS || 接続履歴 +HTML - TIME || 時刻 +HTML - TIMES_KICKED || キック回数 +HTML - TIMES_USED || 使用回数 +HTML - TOTAL_ACTIVE_TEXT || 累計活動時間 +HTML - TOTAL_AFK || 累計離席時間 +HTML - TOTAL_PLAYERS || 全プレイヤー数 +HTML - TOTAL_PLAYTIME || 全プレイ時間 +HTML - UNIQUE || ユニーク +HTML - UNIQUE_CALENDAR || ユニーク: +HTML - UNIQUE_PLAYERS || ユニークプレイヤー +HTML - UNIQUE_PLAYERS_TEXT || ユニークプレイヤー +HTML - UNIQUE_TEXT || ユニーク +HTML - USAGE || 使用履歴 +HTML - USED_COMMANDS || 使用したコマンド +HTML - USER_AND_PASS_NOT_SPECIFIED || ユーザーとパスワードが入力されてません +HTML - USER_DOES_NOT_EXIST || 入力されたユーザーは存在しません +HTML - USER_INFORMATION || ユーザー情報 +HTML - USER_PASS_MISMATCH || 入力されたユーザー名とパスワードが間違っています +HTML - WITH ||
    死亡原因 +HTML - WORLD || ワールド +HTML - WORLD_LOAD || ワールドロード数 +HTML - WORLD_PLAYTIME || ワールドごとのプレイ時間 +HTML - WORST_PING || 最高応答時間 +HTML ERRORS - ACCESS_DENIED_403 || アクセスが拒否されました +HTML ERRORS - ANALYSIS_REFRESH || 分析結果に基づいてデータを更新中です・・・ +HTML ERRORS - ANALYSIS_REFRESH_LONG || サーバーを分析中です・・・・
    数秒後にページが更新されない場合、ページを更新して下さい +HTML ERRORS - AUTH_FAIL_TIPS_401 || - 登録したユーザーを「/plan register 」で確認できます。
    - 入力したユーザー名とパスワードが正しいことを確認して下さい
    - ユーザー名とパスワードは大文字と小文字が区別されています

    パスワードを忘れた場合は、管理者に古いユーザーを削除して新しくユーザーを再登録するよう依頼して下さい +HTML ERRORS - AUTHENTICATION_FAILED_401 || 認証に失敗しました +HTML ERRORS - FORBIDDEN_403 || 閲覧禁止 +HTML ERRORS - INSPECT_REFRESH || プレーヤページのリクエスト処理が実行中です・・ +HTML ERRORS - INSPECT_REFRESH_LONG || ページは自動的に更新されます・・ +HTML ERRORS - NO_SERVERS_404 || リクエストを処理するサーバーがオンラインではありません +HTML ERRORS - NOT_FOUND_404 || ページが見つかりませんでした +HTML ERRORS - NOT_PLAYED_404 || プレイヤーはこのサーバーでプレイしていません +HTML ERRORS - PAGE_NOT_FOUND_404 || ページは存在しません +HTML ERRORS - PLUGIN_TAB_REFRESH || 計算中です・・・ +HTML ERRORS - UNAUTHORIZED_401 || 未認証状態です +HTML ERRORS - UNKNOWN_PAGE_404 || リンクが間違っています、コマンドを使用してURLを確認して下さい。 URL例:

    /player/PlayerName
    /server/ServerName

    +HTML ERRORS - UUID_404 || データベース内にプレヤーのUUIDが存在しません +In Depth Help - /plan ? || > §2主要なコマンド\ 補助コマンドの表示とヘルプ\ §2/plan §f補助コマンドの一覧を表示します\ §2/plan ? §f詳細なヘルプを表示します +In Depth Help - /plan analyze ? || > §2分析コマンド\ サーバーページを更新して、ウェブページへのリンクを表示します +In Depth Help - /plan inspect ? || > §2検査コマンド\ プレイヤーページを更新して、ウェブページへのリンクを表示します +In Depth Help - /plan manage ? || > §2管理コマンド\ MySQLまたはSQLiteの「Plan」のデータベースを管理します\ §2/plan m §f補助コマンドの一覧を表示します\ §2/plan m ? §f詳細なヘルプを表示します +In Depth Help - /plan manage backup ? || > §2バックアップコマンド\ 現在アクティブになっているデータベースの内容を含むSQLiteの新しいデータベースを「Plan」のプラグインフォルダ内に作成します(拡張子は「.db」です) +In Depth Help - /plan manage clear ? || > §2クリア補助コマンド\ アクティブになっているデータベースの全てを削除します。使用には注意が必要です +In Depth Help - /plan manage con ? || > §2接続デバッグ補助コマンド\ ネットワーク内の接続をデバッグするために使用します\ データベース内の各サーバーにリクエストを送信します。 +In Depth Help - /plan manage disable ? || > §2無効補助コマンド\ 次回のリロードまでプラグインの一部を無効にすることができます\ 使用できる引数:\ §2キックカウント §fシャットダウンマクロで「/kickball」が使用されている場合の無効化されたキックカウント。 +In Depth Help - /plan manage export ? || > §2エクスポート補助コマンド\ 分析結果を特定のフォルダーへのエクスポートを有効化します\ 使用できる引数:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. +In Depth Help - /plan manage import ? || > §2インポート補助コマンド\ 別のソースからデータをインポートします\ 使用できる引数:\ §2オフライン §fBukkitのプレイヤーデータ、登録日と名前のみを登録できます +In Depth Help - /plan manage move ? || > §2移動補助コマンド\ SQLiteからMySQLもしくは他の形式にデータを移行させます\ ターゲットとなるデータベースは移行前に空である必要があります +In Depth Help - /plan manage raw ? || > §2生のデータ補助コマンド\ 生のJSONデータページへのリンクを表示します\ 「Plan」の内部ウェブサーバーが有効になっていない場合は利用できません +In Depth Help - /plan manage remove ? || > §2削除補助コマンド\ プレイヤーのデータをアクティブになっているデータベースから削除します +In Depth Help - /plan manage restore ? || > §2復元補助コマンド\ データベースを以前にバックアップしたSQLiteデータベース(拡張子が「.db」のファイル)に復元します\ 他のサーバーの拡張子が「.db」のデータベースをMySQLに復元することもできます\ ターゲットとなるデータベースは移行前に空である必要があります +In Depth Help - /plan manage setup ? || > §2セットアップ補助コマンド\ 「ネットワーク」のページを機能させるために、BungeeCordとこのBukkit/Spigotサーバーの間の接続を設定します.\ BungeeCordのアドレスはBungeeCordで「Plan」が有効化された時のログで見つけることができます。 +In Depth Help - /plan manage uninstalled ? || > §2サーバーアンインストール補助コマンド\ 指定されたサーバーをデータベースからアンインストールします\ コマンドが実行されているサーバーをアンインストールするサーバーとしてマークすることはできません\ サーバーの接続システムに影響します +In Depth Help - /plan network ? || > §2ネットワークコマンド\ 「ネットワーク」のページのURLを表示します\ BungeeCordのネットワーク上にない場合は、このページにサーバーページが表示されます。 +In Depth Help - /plan players ? || > §2プレイヤーコマンド\ 「プレイヤー」のページのURLを表示します +In Depth Help - /plan qinspect ? || > §2クイック検査コマンド\ ゲーム内にいるプレイヤーに関する情報を表示します。 +In Depth Help - /plan reload ? || > §2リロードコマンド\ 「onDisable」と「onEnable」を使ってプラグインを再起動します。\ §bその状態で「Swapping jar」は対応していません +In Depth Help - /plan search ? || > §2検索コマンド\ 与えられた引数に一致するプレイヤー名のリストを取得します\§7 例: /plan search 123 - 名前に「123」を含むすべてのユーザーを検索します +In Depth Help - /plan servers ? || > §2サーバーコマンド\ データベース内の「Plan」サーバーのリストを表示します\ BungeeCordネットワークのデータベースにサーバーを登録する際の問題をデバッグするために使用できます +In Depth Help - /plan web ? || < §2ウェブユーザー管理コマンド\ §2/plan web §f補助コマンドの一覧を表示します\ §2/plan web ? §f詳細なヘルプを表示します +In Depth Help - /plan web register ? || > §2登録コマンド\ 新しいウェブユーザーを登録します。\ プレイヤーが他のプレイヤーをユーザー登録するには「plan.webmanage」権限が必要です\ パスワードはPBKDF2(64,000回のSHA1の繰り返し)でソルト(暗号論)をかけてハッシュされます。 +In Depth Help - /planbungee disable ? || > §2無効化コマンド\ BungeeCordの「Plan」で「onDisable」を実行します\ あとで「/planbungee reload」を使ってプラグインを有効にすることができます\ §bその状態で「Swapping jar」は対応していません +In Depth Help - /planbungee setup ? || > §2セットアップ切り替えコマンド\ BungeeCordでセットアップモードを切り替えます\ 許可されていない他のサーバーがMySQLを詮索するのを防ぎます。 +Manage - Confirm Overwrite || ${0}のデータは上書きされます! +Manage - Confirm Removal || ${0}のデータは削除されます! +Manage - Fail || > §c何かがうまくいきませんでした: ${0} +Manage - Fail File not found || > §c「${0}」にファイルが見つかりませんでした +Manage - Fail Incorrect Database || > §c「${0}」はサポートされていないデータベースです +Manage - Fail No Exporter || §eエクスポーター 「${0}」が存在しません +Manage - Fail No Importer || §eインポーター 「${0}」が存在しません +Manage - Fail No Server || 指定されたパラメーターを持つサーバーが存在しませんでした +Manage - Fail Same Database || > §c同じデータベースを操作することはできません! +Manage - Fail Same server || このサーバーをアンインストールするサーバーとして指定することはできません(あなたがこのサーバーにログインしているため) +Manage - Fail, Confirmation || > §c実行を確認するために引数「-a」を追加します: ${0} +Manage - Fail, Connection Exception || §e失敗した理由: +Manage - Fail, No Servers || §cデータベース内にサーバーが存在しませんでした +Manage - Fail, Old version || §e失敗した理由:受信サーバーで実行されている「Plan」のバージョンが古いです +Manage - Fail, Unauthorized || §e失敗した理由:権限制限があります。サーバーが別のデータベースを使用している可能性があります +Manage - Fail, Unexpected Exception || §e不明な例外: ${0} +Manage - List Importers || インポーター: +Manage - Notify External Url || §e非ローカルアドレスです、接続先のポートが開放されていることを確認して下さい +Manage - Remind HotSwap || §e新しいデータベースに交換することを忘れないで下さい(/plan m hotswap ${0})。そして、プラグインをリロードして下さい +Manage - Start || > §2データを処理中です.. +Manage - Success || > §a成功しました! +Negative || ない +Positive || ある +Today || '今日' +Yesterday || '昨日' +Unknown || 不明 +Version - DEV || このバージョンは開発版です +Version - Latest || 最新版の「Plan」を使用しています +Version - New || 新しいバージョンの${0}が次のURLで入手可能です ${1} +Version - New (old) || 新しいバージョンは次のURLで入手可能です${0} +Version FAIL - Read info (old) || 新しいバージョンのチェックに失敗しました +Version FAIL - Read versions.txt || Github/versions.txtに存在するバージョン情報のロードに失敗しました +Web User Listing || §2${0} §7: §f${1} +WebServer - Notify HTTP || Webサーバー: 証明書が存在ません -> HTTPサーバーを使用します +WebServer - Notify HTTP User Auth || Webサーバー: ユーザー認証が無効になりました! (HTTP経由だと安全ではないためです) +WebServer - Notify no Cert file || Webサーバー: 以下のパスに保存された認証キーファイルが存在しません: ${0} +WebServer FAIL - Port Bind || Webサーバーの初期化が正常に終了しませんでした。ポート番号(${0})は使用されていませんか? +WebServer FAIL - SSL Context || Webサーバー: SSLコンテキストの初期化に失敗しました。 +WebServer FAIL - Store Load || Webサーバー: SSL証明書のロードに失敗しました diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_NL.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_NL.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_NL.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_NO.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_NO.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_NO.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_PL.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_PL.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_PL.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_PT.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_PT.txt deleted file mode 100644 index 2d9750a68..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_PT.txt +++ /dev/null @@ -1,336 +0,0 @@ -Cmd - Click Me || Click me -Cmd - Link || §2Link: §f -Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. -Cmd FAIL - Invalid Username || §cUser does not have an UUID. -Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) -Cmd FAIL - No Permission || §cYou do not have the required permission. -Cmd FAIL - Require only one Argument || §cSingle Argument required ${1} -Cmd FAIL - Requires Arguments || §cArguments required (${0}) ${1} -Cmd FAIL - Unknown Username || §cUser has not been seen on this server -Cmd FAIL - WebUser does not exists || §cUser does not exists! -Cmd FAIL - WebUser exists || §cUser already exists! -Cmd Header - Analysis || > §2Analysis Results -Cmd Header - Info || > §2Player Analytics -Cmd Header - Inspect || > §2Player: §f${0} -Cmd Header - Network || > §2Network Page -Cmd Header - Players || > §2Players -Cmd Header - Search || > §2${0} Results for §f${1}§2: -Cmd Header - Servers || > §2Servers -Cmd Header - Web Users || > §2${0} Web Users -Cmd Info - Bungee Connection || §2Connected to Bungee: §f${0} -Cmd Info - Database || §2Active Database: §f${0} -Cmd Info - Reload Complete || §aReload Complete -Cmd Info - Reload Failed || §cSomething went wrong during reload of the plugin, a restart is recommended. -Cmd Info - Update || §2Update Available: §f${0} -Cmd Info - Version || §2Version: §f${0} -Cmd Notify - No WebUser || You might not have a web user, use /plan register -Cmd Notify - WebUser register || Registered new user: '${0}' Perm level: ${1} -Cmd Qinspect - Activity Index || §2Activity Index: §f${0} | ${1} -Cmd Qinspect - Deaths || §2Deaths: §f${0} -Cmd Qinspect - Geolocation || §2Logged in from: §f${0} -Cmd Qinspect - Last Seen || §2Last Seen: §f${0} -Cmd Qinspect - Longest Session || §2Longest Session: §f${0} -Cmd Qinspect - Mob Kills || §2Mob Kills: §f${0} -Cmd Qinspect - Player Kills || §2Player Kills: §f${0} -Cmd Qinspect - Playtime || §2Playtime: §f${0} -Cmd Qinspect - Registered || §2Registered: §f${0} -Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was not a Bungee server. Use Bungee address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Bungee has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Bungee failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Bungee enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! -Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. -Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): -Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions -Command Help - /plan analyze || View the Server Page -Command Help - /plan dev || Development mode command -Command Help - /plan help || Show command list -Command Help - /plan info || Check the version of Plan -Command Help - /plan inspect || View a Player Page -Command Help - /plan manage || Manage Plan Database -Command Help - /plan manage backup || Backup a Database -Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Bungee connections -Command Help - /plan manage disable || Disable a feature temporarily -Command Help - /plan manage hotswap || Change Database quickly -Command Help - /plan manage import || Import data from elsewhere -Command Help - /plan manage move || Move data between Databases -Command Help - /plan manage remove || Remove Player's data -Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Bungee connection -Command Help - /plan network || View the Network Page -Command Help - /plan players || View the Players Page -Command Help - /plan qinspect || View Player info in game -Command Help - /plan register || Register a Web User -Command Help - /plan reload || Restart Plan -Command Help - /plan search || Search for a player name -Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin -Command Help - /plan web check || Inspect a Web User -Command Help - /plan web delete || Delete a Web User -Command Help - /plan web level || Information about permission levels -Command Help - /plan web list || List Web Users -Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Bungee-Server connections -Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode -Database - Apply Patch || Applying Patch: ${0}.. -Database - Patches Applied || All database patches applied successfully. -Database - Patches Applied Already || All database patches already applied. -Database MySQL - Launch Options Error || Launch Options were faulty, using default (${0}) -Database Notify - Clean || Removed data of ${0} players. -Database Notify - SQLite No WAL || SQLite WAL mode not supported on this server version, using default. This may or may not affect performance. -Disable || Player Analytics Disabled. -Disable - Processing || Processing critical unprocessed tasks. (${0}) -Disable - Processing Complete || Processing complete. -Disable - WebServer || Webserver has been disabled. -Enable || Player Analytics Enabled. -Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} -Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! -Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) -Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. -Enable - Notify Webserver disabled || WebServer was not initialized. (WebServer.DisableWebServer: true) -Enable - WebServer || Webserver running on PORT ${0} (${1}) -Enable FAIL - Database || ${0}-Database Connection failed: ${1} -Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue -Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! -Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities -HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate -HTML - INDEX_ACTIVE || Active -HTML - INDEX_INACTIVE || Inactive -HTML - INDEX_IRREGULAR || Irregular -HTML - INDEX_REGULAR || Regular -HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address -HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities -HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players -HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW -HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname -HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths -HTML - OFFLINE || Offline -HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW -HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online -HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers -HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used -HTML - TOTAL_ACTIVE_TEXT || Total Active -HTML - TOTAL_AFK || Total AFK -HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE -HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands -HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified -HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION -HTML - USER_PASS_MISMATCH || User and Password did not match -HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping -HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. -HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Authentication Failed. -HTML ERRORS - FORBIDDEN_403 || Forbidden -HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. -HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. -HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. -HTML ERRORS - UNAUTHORIZED_401 || Unauthorized -HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

    /player/PlayerName
    /server/ServerName

    -HTML ERRORS - UUID_404 || Player UUID was not found in the database. -In Depth Help - /plan ? || > §2Main Command\ Access to subcommands and help\ §2/plan §fList subcommands\ §2/plan ? §fIn depth help -In Depth Help - /plan analyze ? || > §2Analysis Command\ Refreshes server page and displays link to the web page. -In Depth Help - /plan inspect ? || > §2Inspect Command\ Refreshes player page and displays link to the web page. -In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help -In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. -In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. -In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. -In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. -In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. -In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. -In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Bungee and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. -In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. -In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. -In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. -In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly -In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. -In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. -In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help -In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. -In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Bungee.\ Safeguard against unauthorized MySQL snooping with another server. -Manage - Confirm Overwrite || Data in ${0} will be overwritten! -Manage - Confirm Removal || Data in ${0} will be removed! -Manage - Fail || > §cSomething went wrong: ${0} -Manage - Fail File not found || > §cNo File found at ${0} -Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. -Manage - Fail No Importer || §eImporter '${0}' doesn't exist -Manage - Fail Same Database || > §cCan not operate on to and from the same database! -Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open -Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. -Manage - Start || > §2Processing data.. -Manage - Success || > §aSuccess! -Negative || No -Positive || Yes -Unknown || Unknown -Version - DEV || This is a DEV release. -Version - Latest || You're using the latest version. -Version - New || New Release (${0}) is available ${1} -Version - New (old) || New Version is available at ${0} -Version FAIL - Read info (old) || Failed to check newest version number -Version FAIL - Read versions.txt || Version information could not be loaded from Github/versions.txt -Web User Listing || §2${0} §7: §f${1} -WebServer - Notify HTTP || WebServer: No Certificate -> Using HTTP-server for Visualization. -WebServer - Notify HTTP User Auth || WebServer: User Authorization Disabled! (Not secure over HTTP) -WebServer - Notify no Cert file || WebServer: Certificate KeyStore File not Found: ${0} -WebServer FAIL - Port Bind || WebServer was not initialized successfully. Is the port (${0}) in use? -WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. -WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. -Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ 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 8b9e6fdfc..107462cee 100644 --- a/Plan/common/src/main/resources/assets/plan/web/error.html +++ b/Plan/common/src/main/resources/assets/plan/web/error.html @@ -15,7 +15,8 @@ - + @@ -35,6 +36,9 @@ + + + @@ -243,11 +247,9 @@ - - - - + @@ -255,6 +257,9 @@ + + + diff --git a/Plan/common/src/main/resources/assets/plan/web/network.html b/Plan/common/src/main/resources/assets/plan/web/network.html index 2b0fe840b..b505ffa37 100644 --- a/Plan/common/src/main/resources/assets/plan/web/network.html +++ b/Plan/common/src/main/resources/assets/plan/web/network.html @@ -15,7 +15,8 @@ - + @@ -503,7 +504,8 @@ - + diff --git a/Plan/common/src/main/resources/assets/plan/web/player.html b/Plan/common/src/main/resources/assets/plan/web/player.html index 295e9b2a9..49cb5369b 100644 --- a/Plan/common/src/main/resources/assets/plan/web/player.html +++ b/Plan/common/src/main/resources/assets/plan/web/player.html @@ -15,7 +15,8 @@ - + @@ -800,17 +801,18 @@ - + - + - + - - + + diff --git a/Plan/common/src/main/resources/assets/plan/web/players.html b/Plan/common/src/main/resources/assets/plan/web/players.html index bec3edc25..a6d631e45 100644 --- a/Plan/common/src/main/resources/assets/plan/web/players.html +++ b/Plan/common/src/main/resources/assets/plan/web/players.html @@ -15,7 +15,8 @@ - + @@ -271,7 +272,8 @@ - + @@ -280,8 +282,9 @@ - - + + + diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.css deleted file mode 100644 index ebe57fbf6..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.css +++ /dev/null @@ -1,587 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -.btn-default, -.btn-primary, -.btn-success, -.btn-info, -.btn-warning, -.btn-danger { - text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); -} -.btn-default:active, -.btn-primary:active, -.btn-success:active, -.btn-info:active, -.btn-warning:active, -.btn-danger:active, -.btn-default.active, -.btn-primary.active, -.btn-success.active, -.btn-info.active, -.btn-warning.active, -.btn-danger.active { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-default.disabled, -.btn-primary.disabled, -.btn-success.disabled, -.btn-info.disabled, -.btn-warning.disabled, -.btn-danger.disabled, -.btn-default[disabled], -.btn-primary[disabled], -.btn-success[disabled], -.btn-info[disabled], -.btn-warning[disabled], -.btn-danger[disabled], -fieldset[disabled] .btn-default, -fieldset[disabled] .btn-primary, -fieldset[disabled] .btn-success, -fieldset[disabled] .btn-info, -fieldset[disabled] .btn-warning, -fieldset[disabled] .btn-danger { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-default .badge, -.btn-primary .badge, -.btn-success .badge, -.btn-info .badge, -.btn-warning .badge, -.btn-danger .badge { - text-shadow: none; -} -.btn:active, -.btn.active { - background-image: none; -} -.btn-default { - text-shadow: 0 1px 0 #fff; - background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); - background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #dbdbdb; - border-color: #ccc; -} -.btn-default:hover, -.btn-default:focus { - background-color: #e0e0e0; - background-position: 0 -15px; -} -.btn-default:active, -.btn-default.active { - background-color: #e0e0e0; - border-color: #dbdbdb; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #e0e0e0; - background-image: none; -} -.btn-primary { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); - background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #245580; -} -.btn-primary:hover, -.btn-primary:focus { - background-color: #265a88; - background-position: 0 -15px; -} -.btn-primary:active, -.btn-primary.active { - background-color: #265a88; - border-color: #245580; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #265a88; - background-image: none; -} -.btn-success { - background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); - background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); - background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #3e8f3e; -} -.btn-success:hover, -.btn-success:focus { - background-color: #419641; - background-position: 0 -15px; -} -.btn-success:active, -.btn-success.active { - background-color: #419641; - border-color: #3e8f3e; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #419641; - background-image: none; -} -.btn-info { - background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); - background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); - background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #28a4c9; -} -.btn-info:hover, -.btn-info:focus { - background-color: #2aabd2; - background-position: 0 -15px; -} -.btn-info:active, -.btn-info.active { - background-color: #2aabd2; - border-color: #28a4c9; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #2aabd2; - background-image: none; -} -.btn-warning { - background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); - background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); - background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #e38d13; -} -.btn-warning:hover, -.btn-warning:focus { - background-color: #eb9316; - background-position: 0 -15px; -} -.btn-warning:active, -.btn-warning.active { - background-color: #eb9316; - border-color: #e38d13; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #eb9316; - background-image: none; -} -.btn-danger { - background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); - background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); - background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #b92c28; -} -.btn-danger:hover, -.btn-danger:focus { - background-color: #c12e2a; - background-position: 0 -15px; -} -.btn-danger:active, -.btn-danger.active { - background-color: #c12e2a; - border-color: #b92c28; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #c12e2a; - background-image: none; -} -.thumbnail, -.img-thumbnail { - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); - box-shadow: 0 1px 2px rgba(0, 0, 0, .075); -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - background-color: #e8e8e8; - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); - background-repeat: repeat-x; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - background-color: #2e6da4; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; -} -.navbar-default { - background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); - background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); - background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); - background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); - background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); - background-repeat: repeat-x; - -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); - box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); -} -.navbar-brand, -.navbar-nav > li > a { - text-shadow: 0 1px 0 rgba(255, 255, 255, .25); -} -.navbar-inverse { - background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); - background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); - background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-radius: 4px; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); - background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); - background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); - background-repeat: repeat-x; - -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); - box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); -} -.navbar-inverse .navbar-brand, -.navbar-inverse .navbar-nav > li > a { - text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); -} -.navbar-static-top, -.navbar-fixed-top, -.navbar-fixed-bottom { - border-radius: 0; -} -@media (max-width: 767px) { - .navbar .navbar-nav .open .dropdown-menu > .active > a, - .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; - } -} -.alert { - text-shadow: 0 1px 0 rgba(255, 255, 255, .2); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); -} -.alert-success { - background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); - background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); - background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); - background-repeat: repeat-x; - border-color: #b2dba1; -} -.alert-info { - background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); - background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); - background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); - background-repeat: repeat-x; - border-color: #9acfea; -} -.alert-warning { - background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); - background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); - background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); - background-repeat: repeat-x; - border-color: #f5e79e; -} -.alert-danger { - background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); - background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); - background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); - background-repeat: repeat-x; - border-color: #dca7a7; -} -.progress { - background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); - background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); - background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-success { - background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); - background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); - background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-info { - background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); - background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); - background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-warning { - background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); - background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); - background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-danger { - background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); - background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); - background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.list-group { - border-radius: 4px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); - box-shadow: 0 1px 2px rgba(0, 0, 0, .075); -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - text-shadow: 0 -1px 0 #286090; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); - background-repeat: repeat-x; - border-color: #2b669a; -} -.list-group-item.active .badge, -.list-group-item.active:hover .badge, -.list-group-item.active:focus .badge { - text-shadow: none; -} -.panel { - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); - box-shadow: 0 1px 2px rgba(0, 0, 0, .05); -} -.panel-default > .panel-heading { - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); - background-repeat: repeat-x; -} -.panel-primary > .panel-heading { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; -} -.panel-success > .panel-heading { - background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); - background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); - background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); - background-repeat: repeat-x; -} -.panel-info > .panel-heading { - background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); - background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); - background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); - background-repeat: repeat-x; -} -.panel-warning > .panel-heading { - background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); - background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); - background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); - background-repeat: repeat-x; -} -.panel-danger > .panel-heading { - background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); - background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); - background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); - background-repeat: repeat-x; -} -.well { - background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); - background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); - background-repeat: repeat-x; - border-color: #dcdcdc; - -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); -} -/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.min.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.min.css deleted file mode 100644 index dc95d8e4e..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap-theme.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.css deleted file mode 100644 index 42c79d6e4..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.css +++ /dev/null @@ -1,6760 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} -/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ -@media print { - *, - *:before, - *:after { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.min.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.min.css deleted file mode 100644 index 4cf729e43..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953f..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb5490a..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc609..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e612858f..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54c..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.js b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.js deleted file mode 100644 index d98bf6955..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.js +++ /dev/null @@ -1,2363 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ - -if (typeof jQuery === 'undefined') { - throw new Error('Bootstrap\'s JavaScript requires jQuery') -} - -+function ($) { - 'use strict'; - var version = $.fn.jquery.split(' ')[0].split('.'); - if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) { - throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3') - } -}(jQuery); - -/* ======================================================================== - * Bootstrap: transition.js v3.3.6 - * http://getbootstrap.com/javascript/#transitions - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) - // ============================================================ - - function transitionEnd() { - var el = document.createElement('bootstrap'); - - var transEndEventNames = { - WebkitTransition : 'webkitTransitionEnd', - MozTransition : 'transitionend', - OTransition : 'oTransitionEnd otransitionend', - transition : 'transitionend' - }; - - for (var name in transEndEventNames) { - if (el.style[name] !== undefined) { - return { end: transEndEventNames[name] } - } - } - - return false // explicit for ie8 ( ._.) - } - - // http://blog.alexmaccaw.com/css-transitions - $.fn.emulateTransitionEnd = function (duration) { - var called = false; - var $el = this; - $(this).one('bsTransitionEnd', function () { called = true }); - var callback = function () { if (!called) $($el).trigger($.support.transition.end) }; - setTimeout(callback, duration); - return this - }; - - $(function () { - $.support.transition = transitionEnd(); - - if (!$.support.transition) return; - - $.event.special.bsTransitionEnd = { - bindType: $.support.transition.end, - delegateType: $.support.transition.end, - handle: function (e) { - if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) - } - } - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: alert.js v3.3.6 - * http://getbootstrap.com/javascript/#alerts - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // ALERT CLASS DEFINITION - // ====================== - - var dismiss = '[data-dismiss="alert"]'; - var Alert = function (el) { - $(el).on('click', dismiss, this.close) - }; - - Alert.VERSION = '3.3.6'; - - Alert.TRANSITION_DURATION = 150; - - Alert.prototype.close = function (e) { - var $this = $(this); - var selector = $this.attr('data-target'); - - if (!selector) { - selector = $this.attr('href'); - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - var $parent = $(selector); - - if (e) e.preventDefault(); - - if (!$parent.length) { - $parent = $this.closest('.alert') - } - - $parent.trigger(e = $.Event('close.bs.alert')); - - if (e.isDefaultPrevented()) return; - - $parent.removeClass('in'); - - function removeElement() { - // detach from parent, fire event then clean up data - $parent.detach().trigger('closed.bs.alert').remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent - .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(Alert.TRANSITION_DURATION) : - removeElement() - }; - - - // ALERT PLUGIN DEFINITION - // ======================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.alert'); - - if (!data) $this.data('bs.alert', (data = new Alert(this))); - if (typeof option == 'string') data[option].call($this) - }) - } - - var old = $.fn.alert; - - $.fn.alert = Plugin; - $.fn.alert.Constructor = Alert; - - - // ALERT NO CONFLICT - // ================= - - $.fn.alert.noConflict = function () { - $.fn.alert = old; - return this - }; - - - // ALERT DATA-API - // ============== - - $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: button.js v3.3.6 - * http://getbootstrap.com/javascript/#buttons - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // BUTTON PUBLIC CLASS DEFINITION - // ============================== - - var Button = function (element, options) { - this.$element = $(element); - this.options = $.extend({}, Button.DEFAULTS, options); - this.isLoading = false - }; - - Button.VERSION = '3.3.6'; - - Button.DEFAULTS = { - loadingText: 'loading...' - }; - - Button.prototype.setState = function (state) { - var d = 'disabled'; - var $el = this.$element; - var val = $el.is('input') ? 'val' : 'html'; - var data = $el.data(); - - state += 'Text'; - - if (data.resetText == null) $el.data('resetText', $el[val]()); - - // push to event loop to allow forms to submit - setTimeout($.proxy(function () { - $el[val](data[state] == null ? this.options[state] : data[state]); - - if (state == 'loadingText') { - this.isLoading = true; - $el.addClass(d).attr(d, d) - } else if (this.isLoading) { - this.isLoading = false; - $el.removeClass(d).removeAttr(d) - } - }, this), 0) - }; - - Button.prototype.toggle = function () { - var changed = true; - var $parent = this.$element.closest('[data-toggle="buttons"]'); - - if ($parent.length) { - var $input = this.$element.find('input'); - if ($input.prop('type') == 'radio') { - if ($input.prop('checked')) changed = false; - $parent.find('.active').removeClass('active'); - this.$element.addClass('active') - } else if ($input.prop('type') == 'checkbox') { - if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false; - this.$element.toggleClass('active') - } - $input.prop('checked', this.$element.hasClass('active')); - if (changed) $input.trigger('change') - } else { - this.$element.attr('aria-pressed', !this.$element.hasClass('active')); - this.$element.toggleClass('active') - } - }; - - - // BUTTON PLUGIN DEFINITION - // ======================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.button'); - var options = typeof option == 'object' && option; - - if (!data) $this.data('bs.button', (data = new Button(this, options))); - - if (option == 'toggle') data.toggle(); - else if (option) data.setState(option) - }) - } - - var old = $.fn.button; - - $.fn.button = Plugin; - $.fn.button.Constructor = Button; - - - // BUTTON NO CONFLICT - // ================== - - $.fn.button.noConflict = function () { - $.fn.button = old; - return this - }; - - - // BUTTON DATA-API - // =============== - - $(document) - .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { - var $btn = $(e.target); - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn'); - Plugin.call($btn, 'toggle'); - if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() - }) - .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { - $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: carousel.js v3.3.6 - * http://getbootstrap.com/javascript/#carousel - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CAROUSEL CLASS DEFINITION - // ========================= - - var Carousel = function (element, options) { - this.$element = $(element); - this.$indicators = this.$element.find('.carousel-indicators'); - this.options = options; - this.paused = null; - this.sliding = null; - this.interval = null; - this.$active = null; - this.$items = null; - - this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)); - - this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element - .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) - .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) - }; - - Carousel.VERSION = '3.3.6'; - - Carousel.TRANSITION_DURATION = 600; - - Carousel.DEFAULTS = { - interval: 5000, - pause: 'hover', - wrap: true, - keyboard: true - }; - - Carousel.prototype.keydown = function (e) { - if (/input|textarea/i.test(e.target.tagName)) return; - switch (e.which) { - case 37: this.prev(); break; - case 39: this.next(); break; - default: return - } - - e.preventDefault() - }; - - Carousel.prototype.cycle = function (e) { - e || (this.paused = false); - - this.interval && clearInterval(this.interval); - - this.options.interval - && !this.paused - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)); - - return this - }; - - Carousel.prototype.getItemIndex = function (item) { - this.$items = item.parent().children('.item'); - return this.$items.index(item || this.$active) - }; - - Carousel.prototype.getItemForDirection = function (direction, active) { - var activeIndex = this.getItemIndex(active); - var willWrap = (direction == 'prev' && activeIndex === 0) - || (direction == 'next' && activeIndex == (this.$items.length - 1)); - if (willWrap && !this.options.wrap) return active; - var delta = direction == 'prev' ? -1 : 1; - var itemIndex = (activeIndex + delta) % this.$items.length; - return this.$items.eq(itemIndex) - }; - - Carousel.prototype.to = function (pos) { - var that = this; - var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')); - - if (pos > (this.$items.length - 1) || pos < 0) return; - - if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }); // yes, "slid" - if (activeIndex == pos) return this.pause().cycle(); - - return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) - }; - - Carousel.prototype.pause = function (e) { - e || (this.paused = true); - - if (this.$element.find('.next, .prev').length && $.support.transition) { - this.$element.trigger($.support.transition.end); - this.cycle(true) - } - - this.interval = clearInterval(this.interval); - - return this - }; - - Carousel.prototype.next = function () { - if (this.sliding) return; - return this.slide('next') - }; - - Carousel.prototype.prev = function () { - if (this.sliding) return; - return this.slide('prev') - }; - - Carousel.prototype.slide = function (type, next) { - var $active = this.$element.find('.item.active'); - var $next = next || this.getItemForDirection(type, $active); - var isCycling = this.interval; - var direction = type == 'next' ? 'left' : 'right'; - var that = this; - - if ($next.hasClass('active')) return (this.sliding = false); - - var relatedTarget = $next[0]; - var slideEvent = $.Event('slide.bs.carousel', { - relatedTarget: relatedTarget, - direction: direction - }); - this.$element.trigger(slideEvent); - if (slideEvent.isDefaultPrevented()) return; - - this.sliding = true; - - isCycling && this.pause(); - - if (this.$indicators.length) { - this.$indicators.find('.active').removeClass('active'); - var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]); - $nextIndicator && $nextIndicator.addClass('active') - } - - var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }); // yes, "slid" - if ($.support.transition && this.$element.hasClass('slide')) { - $next.addClass(type); - $next[0].offsetWidth; // force reflow - $active.addClass(direction); - $next.addClass(direction); - $active - .one('bsTransitionEnd', function () { - $next.removeClass([type, direction].join(' ')).addClass('active'); - $active.removeClass(['active', direction].join(' ')); - that.sliding = false; - setTimeout(function () { - that.$element.trigger(slidEvent) - }, 0) - }) - .emulateTransitionEnd(Carousel.TRANSITION_DURATION) - } else { - $active.removeClass('active'); - $next.addClass('active'); - this.sliding = false; - this.$element.trigger(slidEvent) - } - - isCycling && this.cycle(); - - return this - }; - - - // CAROUSEL PLUGIN DEFINITION - // ========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.carousel'); - var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option); - var action = typeof option == 'string' ? option : options.slide; - - if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))); - if (typeof option == 'number') data.to(option); - else if (action) data[action](); - else if (options.interval) data.pause().cycle() - }) - } - - var old = $.fn.carousel; - - $.fn.carousel = Plugin; - $.fn.carousel.Constructor = Carousel; - - - // CAROUSEL NO CONFLICT - // ==================== - - $.fn.carousel.noConflict = function () { - $.fn.carousel = old; - return this - }; - - - // CAROUSEL DATA-API - // ================= - - var clickHandler = function (e) { - var href; - var $this = $(this); - var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')); // strip for ie7 - if (!$target.hasClass('carousel')) return; - var options = $.extend({}, $target.data(), $this.data()); - var slideIndex = $this.attr('data-slide-to'); - if (slideIndex) options.interval = false; - - Plugin.call($target, options); - - if (slideIndex) { - $target.data('bs.carousel').to(slideIndex) - } - - e.preventDefault() - }; - - $(document) - .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) - .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler); - - $(window).on('load', function () { - $('[data-ride="carousel"]').each(function () { - var $carousel = $(this); - Plugin.call($carousel, $carousel.data()) - }) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: collapse.js v3.3.6 - * http://getbootstrap.com/javascript/#collapse - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // COLLAPSE PUBLIC CLASS DEFINITION - // ================================ - - var Collapse = function (element, options) { - this.$element = $(element); - this.options = $.extend({}, Collapse.DEFAULTS, options); - this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + - '[data-toggle="collapse"][data-target="#' + element.id + '"]'); - this.transitioning = null; - - if (this.options.parent) { - this.$parent = this.getParent() - } else { - this.addAriaAndCollapsedClass(this.$element, this.$trigger) - } - - if (this.options.toggle) this.toggle() - }; - - Collapse.VERSION = '3.3.6'; - - Collapse.TRANSITION_DURATION = 350; - - Collapse.DEFAULTS = { - toggle: true - }; - - Collapse.prototype.dimension = function () { - var hasWidth = this.$element.hasClass('width'); - return hasWidth ? 'width' : 'height' - }; - - Collapse.prototype.show = function () { - if (this.transitioning || this.$element.hasClass('in')) return; - - var activesData; - var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing'); - - if (actives && actives.length) { - activesData = actives.data('bs.collapse'); - if (activesData && activesData.transitioning) return - } - - var startEvent = $.Event('show.bs.collapse'); - this.$element.trigger(startEvent); - if (startEvent.isDefaultPrevented()) return; - - if (actives && actives.length) { - Plugin.call(actives, 'hide'); - activesData || actives.data('bs.collapse', null) - } - - var dimension = this.dimension(); - - this.$element - .removeClass('collapse') - .addClass('collapsing')[dimension](0) - .attr('aria-expanded', true); - - this.$trigger - .removeClass('collapsed') - .attr('aria-expanded', true); - - this.transitioning = 1; - - var complete = function () { - this.$element - .removeClass('collapsing') - .addClass('collapse in')[dimension](''); - this.transitioning = 0; - this.$element - .trigger('shown.bs.collapse') - }; - - if (!$.support.transition) return complete.call(this); - - var scrollSize = $.camelCase(['scroll', dimension].join('-')); - - this.$element - .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) - }; - - Collapse.prototype.hide = function () { - if (this.transitioning || !this.$element.hasClass('in')) return; - - var startEvent = $.Event('hide.bs.collapse'); - this.$element.trigger(startEvent); - if (startEvent.isDefaultPrevented()) return; - - var dimension = this.dimension(); - - this.$element[dimension](this.$element[dimension]())[0].offsetHeight; - - this.$element - .addClass('collapsing') - .removeClass('collapse in') - .attr('aria-expanded', false); - - this.$trigger - .addClass('collapsed') - .attr('aria-expanded', false); - - this.transitioning = 1; - - var complete = function () { - this.transitioning = 0; - this.$element - .removeClass('collapsing') - .addClass('collapse') - .trigger('hidden.bs.collapse') - }; - - if (!$.support.transition) return complete.call(this); - - this.$element - [dimension](0) - .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(Collapse.TRANSITION_DURATION) - }; - - Collapse.prototype.toggle = function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - }; - - Collapse.prototype.getParent = function () { - return $(this.options.parent) - .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') - .each($.proxy(function (i, element) { - var $element = $(element); - this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) - }, this)) - .end() - }; - - Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { - var isOpen = $element.hasClass('in'); - - $element.attr('aria-expanded', isOpen); - $trigger - .toggleClass('collapsed', !isOpen) - .attr('aria-expanded', isOpen) - }; - - function getTargetFromTrigger($trigger) { - var href; - var target = $trigger.attr('data-target') - || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, ''); // strip for ie7 - - return $(target) - } - - - // COLLAPSE PLUGIN DEFINITION - // ========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.collapse'); - var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option); - - if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false; - if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.collapse; - - $.fn.collapse = Plugin; - $.fn.collapse.Constructor = Collapse; - - - // COLLAPSE NO CONFLICT - // ==================== - - $.fn.collapse.noConflict = function () { - $.fn.collapse = old; - return this - }; - - - // COLLAPSE DATA-API - // ================= - - $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { - var $this = $(this); - - if (!$this.attr('data-target')) e.preventDefault(); - - var $target = getTargetFromTrigger($this); - var data = $target.data('bs.collapse'); - var option = data ? 'toggle' : $this.data(); - - Plugin.call($target, option) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: dropdown.js v3.3.6 - * http://getbootstrap.com/javascript/#dropdowns - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // DROPDOWN CLASS DEFINITION - // ========================= - - var backdrop = '.dropdown-backdrop'; - var toggle = '[data-toggle="dropdown"]'; - var Dropdown = function (element) { - $(element).on('click.bs.dropdown', this.toggle) - }; - - Dropdown.VERSION = '3.3.6'; - - function getParent($this) { - var selector = $this.attr('data-target'); - - if (!selector) { - selector = $this.attr('href'); - selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - var $parent = selector && $(selector); - - return $parent && $parent.length ? $parent : $this.parent() - } - - function clearMenus(e) { - if (e && e.which === 3) return; - $(backdrop).remove(); - $(toggle).each(function () { - var $this = $(this); - var $parent = getParent($this); - var relatedTarget = { relatedTarget: this }; - - if (!$parent.hasClass('open')) return; - - if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return; - - $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)); - - if (e.isDefaultPrevented()) return; - - $this.attr('aria-expanded', 'false'); - $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) - }) - } - - Dropdown.prototype.toggle = function (e) { - var $this = $(this); - - if ($this.is('.disabled, :disabled')) return; - - var $parent = getParent($this); - var isActive = $parent.hasClass('open'); - - clearMenus(); - - if (!isActive) { - if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { - // if mobile we use a backdrop because click events don't delegate - $(document.createElement('div')) - .addClass('dropdown-backdrop') - .insertAfter($(this)) - .on('click', clearMenus) - } - - var relatedTarget = { relatedTarget: this }; - $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)); - - if (e.isDefaultPrevented()) return; - - $this - .trigger('focus') - .attr('aria-expanded', 'true'); - - $parent - .toggleClass('open') - .trigger($.Event('shown.bs.dropdown', relatedTarget)) - } - - return false - }; - - Dropdown.prototype.keydown = function (e) { - if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return; - - var $this = $(this); - - e.preventDefault(); - e.stopPropagation(); - - if ($this.is('.disabled, :disabled')) return; - - var $parent = getParent($this); - var isActive = $parent.hasClass('open'); - - if (!isActive && e.which != 27 || isActive && e.which == 27) { - if (e.which == 27) $parent.find(toggle).trigger('focus'); - return $this.trigger('click') - } - - var desc = ' li:not(.disabled):visible a'; - var $items = $parent.find('.dropdown-menu' + desc); - - if (!$items.length) return; - - var index = $items.index(e.target); - - if (e.which == 38 && index > 0) index--; // up - if (e.which == 40 && index < $items.length - 1) index++; // down - if (!~index) index = 0; - - $items.eq(index).trigger('focus') - }; - - - // DROPDOWN PLUGIN DEFINITION - // ========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.dropdown'); - - if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))); - if (typeof option == 'string') data[option].call($this) - }) - } - - var old = $.fn.dropdown; - - $.fn.dropdown = Plugin; - $.fn.dropdown.Constructor = Dropdown; - - - // DROPDOWN NO CONFLICT - // ==================== - - $.fn.dropdown.noConflict = function () { - $.fn.dropdown = old; - return this - }; - - - // APPLY TO STANDARD DROPDOWN ELEMENTS - // =================================== - - $(document) - .on('click.bs.dropdown.data-api', clearMenus) - .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) - .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) - .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) - .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: modal.js v3.3.6 - * http://getbootstrap.com/javascript/#modals - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // MODAL CLASS DEFINITION - // ====================== - - var Modal = function (element, options) { - this.options = options; - this.$body = $(document.body); - this.$element = $(element); - this.$dialog = this.$element.find('.modal-dialog'); - this.$backdrop = null; - this.isShown = null; - this.originalBodyPad = null; - this.scrollbarWidth = 0; - this.ignoreBackdropClick = false; - - if (this.options.remote) { - this.$element - .find('.modal-content') - .load(this.options.remote, $.proxy(function () { - this.$element.trigger('loaded.bs.modal') - }, this)) - } - }; - - Modal.VERSION = '3.3.6'; - - Modal.TRANSITION_DURATION = 300; - Modal.BACKDROP_TRANSITION_DURATION = 150; - - Modal.DEFAULTS = { - backdrop: true, - keyboard: true, - show: true - }; - - Modal.prototype.toggle = function (_relatedTarget) { - return this.isShown ? this.hide() : this.show(_relatedTarget) - }; - - Modal.prototype.show = function (_relatedTarget) { - var that = this; - var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }); - - this.$element.trigger(e); - - if (this.isShown || e.isDefaultPrevented()) return; - - this.isShown = true; - - this.checkScrollbar(); - this.setScrollbar(); - this.$body.addClass('modal-open'); - - this.escape(); - this.resize(); - - this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)); - - this.$dialog.on('mousedown.dismiss.bs.modal', function () { - that.$element.one('mouseup.dismiss.bs.modal', function (e) { - if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true - }) - }); - - this.backdrop(function () { - var transition = $.support.transition && that.$element.hasClass('fade'); - - if (!that.$element.parent().length) { - that.$element.appendTo(that.$body) // don't move modals dom position - } - - that.$element - .show() - .scrollTop(0); - - that.adjustDialog(); - - if (transition) { - that.$element[0].offsetWidth // force reflow - } - - that.$element.addClass('in'); - - that.enforceFocus(); - - var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }); - - transition ? - that.$dialog // wait for modal to slide in - .one('bsTransitionEnd', function () { - that.$element.trigger('focus').trigger(e) - }) - .emulateTransitionEnd(Modal.TRANSITION_DURATION) : - that.$element.trigger('focus').trigger(e) - }) - }; - - Modal.prototype.hide = function (e) { - if (e) e.preventDefault(); - - e = $.Event('hide.bs.modal'); - - this.$element.trigger(e); - - if (!this.isShown || e.isDefaultPrevented()) return; - - this.isShown = false; - - this.escape(); - this.resize(); - - $(document).off('focusin.bs.modal'); - - this.$element - .removeClass('in') - .off('click.dismiss.bs.modal') - .off('mouseup.dismiss.bs.modal'); - - this.$dialog.off('mousedown.dismiss.bs.modal'); - - $.support.transition && this.$element.hasClass('fade') ? - this.$element - .one('bsTransitionEnd', $.proxy(this.hideModal, this)) - .emulateTransitionEnd(Modal.TRANSITION_DURATION) : - this.hideModal() - }; - - Modal.prototype.enforceFocus = function () { - $(document) - .off('focusin.bs.modal') // guard against infinite focus loop - .on('focusin.bs.modal', $.proxy(function (e) { - if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { - this.$element.trigger('focus') - } - }, this)) - }; - - Modal.prototype.escape = function () { - if (this.isShown && this.options.keyboard) { - this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { - e.which == 27 && this.hide() - }, this)) - } else if (!this.isShown) { - this.$element.off('keydown.dismiss.bs.modal') - } - }; - - Modal.prototype.resize = function () { - if (this.isShown) { - $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) - } else { - $(window).off('resize.bs.modal') - } - }; - - Modal.prototype.hideModal = function () { - var that = this; - this.$element.hide(); - this.backdrop(function () { - that.$body.removeClass('modal-open'); - that.resetAdjustments(); - that.resetScrollbar(); - that.$element.trigger('hidden.bs.modal') - }) - }; - - Modal.prototype.removeBackdrop = function () { - this.$backdrop && this.$backdrop.remove(); - this.$backdrop = null - }; - - Modal.prototype.backdrop = function (callback) { - var that = this; - var animate = this.$element.hasClass('fade') ? 'fade' : ''; - - if (this.isShown && this.options.backdrop) { - var doAnimate = $.support.transition && animate; - - this.$backdrop = $(document.createElement('div')) - .addClass('modal-backdrop ' + animate) - .appendTo(this.$body); - - this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { - if (this.ignoreBackdropClick) { - this.ignoreBackdropClick = false; - return - } - if (e.target !== e.currentTarget) return; - this.options.backdrop == 'static' - ? this.$element[0].focus() - : this.hide() - }, this)); - - if (doAnimate) this.$backdrop[0].offsetWidth; // force reflow - - this.$backdrop.addClass('in'); - - if (!callback) return; - - doAnimate ? - this.$backdrop - .one('bsTransitionEnd', callback) - .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : - callback() - - } else if (!this.isShown && this.$backdrop) { - this.$backdrop.removeClass('in'); - - var callbackRemove = function () { - that.removeBackdrop(); - callback && callback() - }; - $.support.transition && this.$element.hasClass('fade') ? - this.$backdrop - .one('bsTransitionEnd', callbackRemove) - .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : - callbackRemove() - - } else if (callback) { - callback() - } - }; - - // these following methods are used to handle overflowing modals - - Modal.prototype.handleUpdate = function () { - this.adjustDialog() - }; - - Modal.prototype.adjustDialog = function () { - var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight; - - this.$element.css({ - paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', - paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' - }) - }; - - Modal.prototype.resetAdjustments = function () { - this.$element.css({ - paddingLeft: '', - paddingRight: '' - }) - }; - - Modal.prototype.checkScrollbar = function () { - var fullWindowWidth = window.innerWidth; - if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 - var documentElementRect = document.documentElement.getBoundingClientRect(); - fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) - } - this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth; - this.scrollbarWidth = this.measureScrollbar() - }; - - Modal.prototype.setScrollbar = function () { - var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10); - this.originalBodyPad = document.body.style.paddingRight || ''; - if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) - }; - - Modal.prototype.resetScrollbar = function () { - this.$body.css('padding-right', this.originalBodyPad) - }; - - Modal.prototype.measureScrollbar = function () { // thx walsh - var scrollDiv = document.createElement('div'); - scrollDiv.className = 'modal-scrollbar-measure'; - this.$body.append(scrollDiv); - var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; - this.$body[0].removeChild(scrollDiv); - return scrollbarWidth - }; - - - // MODAL PLUGIN DEFINITION - // ======================= - - function Plugin(option, _relatedTarget) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.modal'); - var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option); - - if (!data) $this.data('bs.modal', (data = new Modal(this, options))); - if (typeof option == 'string') data[option](_relatedTarget); - else if (options.show) data.show(_relatedTarget) - }) - } - - var old = $.fn.modal; - - $.fn.modal = Plugin; - $.fn.modal.Constructor = Modal; - - - // MODAL NO CONFLICT - // ================= - - $.fn.modal.noConflict = function () { - $.fn.modal = old; - return this - }; - - - // MODAL DATA-API - // ============== - - $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { - var $this = $(this); - var href = $this.attr('href'); - var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))); // strip for ie7 - var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()); - - if ($this.is('a')) e.preventDefault(); - - $target.one('show.bs.modal', function (showEvent) { - if (showEvent.isDefaultPrevented()) return; // only register focus restorer if modal will actually get shown - $target.one('hidden.bs.modal', function () { - $this.is(':visible') && $this.trigger('focus') - }) - }); - Plugin.call($target, option, this) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: tooltip.js v3.3.6 - * http://getbootstrap.com/javascript/#tooltip - * Inspired by the original jQuery.tipsy by Jason Frame - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // TOOLTIP PUBLIC CLASS DEFINITION - // =============================== - - var Tooltip = function (element, options) { - this.type = null; - this.options = null; - this.enabled = null; - this.timeout = null; - this.hoverState = null; - this.$element = null; - this.inState = null; - - this.init('tooltip', element, options) - }; - - Tooltip.VERSION = '3.3.6'; - - Tooltip.TRANSITION_DURATION = 150; - - Tooltip.DEFAULTS = { - animation: true, - placement: 'top', - selector: false, - template: '', - trigger: 'hover focus', - title: '', - delay: 0, - html: false, - container: false, - viewport: { - selector: 'body', - padding: 0 - } - }; - - Tooltip.prototype.init = function (type, element, options) { - this.enabled = true; - this.type = type; - this.$element = $(element); - this.options = this.getOptions(options); - this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)); - this.inState = { click: false, hover: false, focus: false }; - - if (this.$element[0] instanceof document.constructor && !this.options.selector) { - throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') - } - - var triggers = this.options.trigger.split(' '); - - for (var i = triggers.length; i--;) { - var trigger = triggers[i]; - - if (trigger == 'click') { - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) - } else if (trigger != 'manual') { - var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'; - var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'; - - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)); - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) - } - } - - this.options.selector ? - (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : - this.fixTitle() - }; - - Tooltip.prototype.getDefaults = function () { - return Tooltip.DEFAULTS - }; - - Tooltip.prototype.getOptions = function (options) { - options = $.extend({}, this.getDefaults(), this.$element.data(), options); - - if (options.delay && typeof options.delay == 'number') { - options.delay = { - show: options.delay, - hide: options.delay - } - } - - return options - }; - - Tooltip.prototype.getDelegateOptions = function () { - var options = {}; - var defaults = this.getDefaults(); - - this._options && $.each(this._options, function (key, value) { - if (defaults[key] != value) options[key] = value - }); - - return options - }; - - Tooltip.prototype.enter = function (obj) { - var self = obj instanceof this.constructor ? - obj : $(obj.currentTarget).data('bs.' + this.type); - - if (!self) { - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()); - $(obj.currentTarget).data('bs.' + this.type, self) - } - - if (obj instanceof $.Event) { - self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true - } - - if (self.tip().hasClass('in') || self.hoverState == 'in') { - self.hoverState = 'in'; - return - } - - clearTimeout(self.timeout); - - self.hoverState = 'in'; - - if (!self.options.delay || !self.options.delay.show) return self.show(); - - self.timeout = setTimeout(function () { - if (self.hoverState == 'in') self.show() - }, self.options.delay.show) - }; - - Tooltip.prototype.isInStateTrue = function () { - for (var key in this.inState) { - if (this.inState[key]) return true - } - - return false - }; - - Tooltip.prototype.leave = function (obj) { - var self = obj instanceof this.constructor ? - obj : $(obj.currentTarget).data('bs.' + this.type); - - if (!self) { - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()); - $(obj.currentTarget).data('bs.' + this.type, self) - } - - if (obj instanceof $.Event) { - self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false - } - - if (self.isInStateTrue()) return; - - clearTimeout(self.timeout); - - self.hoverState = 'out'; - - if (!self.options.delay || !self.options.delay.hide) return self.hide(); - - self.timeout = setTimeout(function () { - if (self.hoverState == 'out') self.hide() - }, self.options.delay.hide) - }; - - Tooltip.prototype.show = function () { - var e = $.Event('show.bs.' + this.type); - - if (this.hasContent() && this.enabled) { - this.$element.trigger(e); - - var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]); - if (e.isDefaultPrevented() || !inDom) return; - var that = this; - - var $tip = this.tip(); - - var tipId = this.getUID(this.type); - - this.setContent(); - $tip.attr('id', tipId); - this.$element.attr('aria-describedby', tipId); - - if (this.options.animation) $tip.addClass('fade'); - - var placement = typeof this.options.placement == 'function' ? - this.options.placement.call(this, $tip[0], this.$element[0]) : - this.options.placement; - - var autoToken = /\s?auto?\s?/i; - var autoPlace = autoToken.test(placement); - if (autoPlace) placement = placement.replace(autoToken, '') || 'top'; - - $tip - .detach() - .css({ top: 0, left: 0, display: 'block' }) - .addClass(placement) - .data('bs.' + this.type, this); - - this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element); - this.$element.trigger('inserted.bs.' + this.type); - - var pos = this.getPosition(); - var actualWidth = $tip[0].offsetWidth; - var actualHeight = $tip[0].offsetHeight; - - if (autoPlace) { - var orgPlacement = placement; - var viewportDim = this.getPosition(this.$viewport); - - placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : - placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : - placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : - placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : - placement; - - $tip - .removeClass(orgPlacement) - .addClass(placement) - } - - var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight); - - this.applyPlacement(calculatedOffset, placement); - - var complete = function () { - var prevHoverState = that.hoverState; - that.$element.trigger('shown.bs.' + that.type); - that.hoverState = null; - - if (prevHoverState == 'out') that.leave(that) - }; - - $.support.transition && this.$tip.hasClass('fade') ? - $tip - .one('bsTransitionEnd', complete) - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : - complete() - } - }; - - Tooltip.prototype.applyPlacement = function (offset, placement) { - var $tip = this.tip(); - var width = $tip[0].offsetWidth; - var height = $tip[0].offsetHeight; - - // manually read margins because getBoundingClientRect includes difference - var marginTop = parseInt($tip.css('margin-top'), 10); - var marginLeft = parseInt($tip.css('margin-left'), 10); - - // we must check for NaN for ie 8/9 - if (isNaN(marginTop)) marginTop = 0; - if (isNaN(marginLeft)) marginLeft = 0; - - offset.top += marginTop; - offset.left += marginLeft; - - // $.fn.offset doesn't round pixel values - // so we use setOffset directly with our own function B-0 - $.offset.setOffset($tip[0], $.extend({ - using: function (props) { - $tip.css({ - top: Math.round(props.top), - left: Math.round(props.left) - }) - } - }, offset), 0); - - $tip.addClass('in'); - - // check to see if placing tip in new offset caused the tip to resize itself - var actualWidth = $tip[0].offsetWidth; - var actualHeight = $tip[0].offsetHeight; - - if (placement == 'top' && actualHeight != height) { - offset.top = offset.top + height - actualHeight - } - - var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight); - - if (delta.left) offset.left += delta.left; - else offset.top += delta.top; - - var isVertical = /top|bottom/.test(placement); - var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight; - var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'; - - $tip.offset(offset); - this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) - }; - - Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { - this.arrow() - .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') - .css(isVertical ? 'top' : 'left', '') - }; - - Tooltip.prototype.setContent = function () { - var $tip = this.tip(); - var title = this.getTitle(); - - $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title); - $tip.removeClass('fade in top bottom left right') - }; - - Tooltip.prototype.hide = function (callback) { - var that = this; - var $tip = $(this.$tip); - var e = $.Event('hide.bs.' + this.type); - - function complete() { - if (that.hoverState != 'in') $tip.detach(); - that.$element - .removeAttr('aria-describedby') - .trigger('hidden.bs.' + that.type); - callback && callback() - } - - this.$element.trigger(e); - - if (e.isDefaultPrevented()) return; - - $tip.removeClass('in'); - - $.support.transition && $tip.hasClass('fade') ? - $tip - .one('bsTransitionEnd', complete) - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : - complete(); - - this.hoverState = null; - - return this - }; - - Tooltip.prototype.fixTitle = function () { - var $e = this.$element; - if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { - $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') - } - }; - - Tooltip.prototype.hasContent = function () { - return this.getTitle() - }; - - Tooltip.prototype.getPosition = function ($element) { - $element = $element || this.$element; - - var el = $element[0]; - var isBody = el.tagName == 'BODY'; - - var elRect = el.getBoundingClientRect(); - if (elRect.width == null) { - // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 - elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) - } - var elOffset = isBody ? { top: 0, left: 0 } : $element.offset(); - var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }; - var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null; - - return $.extend({}, elRect, scroll, outerDims, elOffset) - }; - - Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { - return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : - /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } - - }; - - Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { - var delta = { top: 0, left: 0 }; - if (!this.$viewport) return delta; - - var viewportPadding = this.options.viewport && this.options.viewport.padding || 0; - var viewportDimensions = this.getPosition(this.$viewport); - - if (/right|left/.test(placement)) { - var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll; - var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight; - if (topEdgeOffset < viewportDimensions.top) { // top overflow - delta.top = viewportDimensions.top - topEdgeOffset - } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow - delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset - } - } else { - var leftEdgeOffset = pos.left - viewportPadding; - var rightEdgeOffset = pos.left + viewportPadding + actualWidth; - if (leftEdgeOffset < viewportDimensions.left) { // left overflow - delta.left = viewportDimensions.left - leftEdgeOffset - } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow - delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset - } - } - - return delta - }; - - Tooltip.prototype.getTitle = function () { - var title; - var $e = this.$element; - var o = this.options; - - title = $e.attr('data-original-title') - || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title); - - return title - }; - - Tooltip.prototype.getUID = function (prefix) { - do prefix += ~~(Math.random() * 1000000); - while (document.getElementById(prefix)); - return prefix - }; - - Tooltip.prototype.tip = function () { - if (!this.$tip) { - this.$tip = $(this.options.template); - if (this.$tip.length != 1) { - throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') - } - } - return this.$tip - }; - - Tooltip.prototype.arrow = function () { - return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) - }; - - Tooltip.prototype.enable = function () { - this.enabled = true - }; - - Tooltip.prototype.disable = function () { - this.enabled = false - }; - - Tooltip.prototype.toggleEnabled = function () { - this.enabled = !this.enabled - }; - - Tooltip.prototype.toggle = function (e) { - var self = this; - if (e) { - self = $(e.currentTarget).data('bs.' + this.type); - if (!self) { - self = new this.constructor(e.currentTarget, this.getDelegateOptions()); - $(e.currentTarget).data('bs.' + this.type, self) - } - } - - if (e) { - self.inState.click = !self.inState.click; - if (self.isInStateTrue()) self.enter(self); - else self.leave(self) - } else { - self.tip().hasClass('in') ? self.leave(self) : self.enter(self) - } - }; - - Tooltip.prototype.destroy = function () { - var that = this; - clearTimeout(this.timeout); - this.hide(function () { - that.$element.off('.' + that.type).removeData('bs.' + that.type); - if (that.$tip) { - that.$tip.detach() - } - that.$tip = null; - that.$arrow = null; - that.$viewport = null - }) - }; - - - // TOOLTIP PLUGIN DEFINITION - // ========================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.tooltip'); - var options = typeof option == 'object' && option; - - if (!data && /destroy|hide/.test(option)) return; - if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.tooltip; - - $.fn.tooltip = Plugin; - $.fn.tooltip.Constructor = Tooltip; - - - // TOOLTIP NO CONFLICT - // =================== - - $.fn.tooltip.noConflict = function () { - $.fn.tooltip = old; - return this - } - -}(jQuery); - -/* ======================================================================== - * Bootstrap: popover.js v3.3.6 - * http://getbootstrap.com/javascript/#popovers - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // POPOVER PUBLIC CLASS DEFINITION - // =============================== - - var Popover = function (element, options) { - this.init('popover', element, options) - }; - - if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js'); - - Popover.VERSION = '3.3.6'; - - Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { - placement: 'right', - trigger: 'click', - content: '', - template: '' - }); - - - // NOTE: POPOVER EXTENDS tooltip.js - // ================================ - - Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype); - - Popover.prototype.constructor = Popover; - - Popover.prototype.getDefaults = function () { - return Popover.DEFAULTS - }; - - Popover.prototype.setContent = function () { - var $tip = this.tip(); - var title = this.getTitle(); - var content = this.getContent(); - - $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title); - $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events - this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' - ](content); - - $tip.removeClass('fade top bottom left right in'); - - // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do - // this manually by checking the contents. - if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() - }; - - Popover.prototype.hasContent = function () { - return this.getTitle() || this.getContent() - }; - - Popover.prototype.getContent = function () { - var $e = this.$element; - var o = this.options; - - return $e.attr('data-content') - || (typeof o.content == 'function' ? - o.content.call($e[0]) : - o.content) - }; - - Popover.prototype.arrow = function () { - return (this.$arrow = this.$arrow || this.tip().find('.arrow')) - }; - - - // POPOVER PLUGIN DEFINITION - // ========================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.popover'); - var options = typeof option == 'object' && option; - - if (!data && /destroy|hide/.test(option)) return; - if (!data) $this.data('bs.popover', (data = new Popover(this, options))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.popover; - - $.fn.popover = Plugin; - $.fn.popover.Constructor = Popover; - - - // POPOVER NO CONFLICT - // =================== - - $.fn.popover.noConflict = function () { - $.fn.popover = old; - return this - } - -}(jQuery); - -/* ======================================================================== - * Bootstrap: scrollspy.js v3.3.6 - * http://getbootstrap.com/javascript/#scrollspy - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // SCROLLSPY CLASS DEFINITION - // ========================== - - function ScrollSpy(element, options) { - this.$body = $(document.body); - this.$scrollElement = $(element).is(document.body) ? $(window) : $(element); - this.options = $.extend({}, ScrollSpy.DEFAULTS, options); - this.selector = (this.options.target || '') + ' .nav li > a'; - this.offsets = []; - this.targets = []; - this.activeTarget = null; - this.scrollHeight = 0; - - this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)); - this.refresh(); - this.process() - } - - ScrollSpy.VERSION = '3.3.6'; - - ScrollSpy.DEFAULTS = { - offset: 10 - }; - - ScrollSpy.prototype.getScrollHeight = function () { - return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) - }; - - ScrollSpy.prototype.refresh = function () { - var that = this; - var offsetMethod = 'offset'; - var offsetBase = 0; - - this.offsets = []; - this.targets = []; - this.scrollHeight = this.getScrollHeight(); - - if (!$.isWindow(this.$scrollElement[0])) { - offsetMethod = 'position'; - offsetBase = this.$scrollElement.scrollTop() - } - - this.$body - .find(this.selector) - .map(function () { - var $el = $(this); - var href = $el.data('target') || $el.attr('href'); - var $href = /^#./.test(href) && $(href); - - return ($href - && $href.length - && $href.is(':visible') - && [[$href[offsetMethod]().top + offsetBase, href]]) || null - }) - .sort(function (a, b) { return a[0] - b[0] }) - .each(function () { - that.offsets.push(this[0]); - that.targets.push(this[1]) - }) - }; - - ScrollSpy.prototype.process = function () { - var scrollTop = this.$scrollElement.scrollTop() + this.options.offset; - var scrollHeight = this.getScrollHeight(); - var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height(); - var offsets = this.offsets; - var targets = this.targets; - var activeTarget = this.activeTarget; - var i; - - if (this.scrollHeight != scrollHeight) { - this.refresh() - } - - if (scrollTop >= maxScroll) { - return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) - } - - if (activeTarget && scrollTop < offsets[0]) { - this.activeTarget = null; - return this.clear() - } - - for (i = offsets.length; i--;) { - activeTarget != targets[i] - && scrollTop >= offsets[i] - && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) - && this.activate(targets[i]) - } - }; - - ScrollSpy.prototype.activate = function (target) { - this.activeTarget = target; - - this.clear(); - - var selector = this.selector + - '[data-target="' + target + '"],' + - this.selector + '[href="' + target + '"]'; - - var active = $(selector) - .parents('li') - .addClass('active'); - - if (active.parent('.dropdown-menu').length) { - active = active - .closest('li.dropdown') - .addClass('active') - } - - active.trigger('activate.bs.scrollspy') - }; - - ScrollSpy.prototype.clear = function () { - $(this.selector) - .parentsUntil(this.options.target, '.active') - .removeClass('active') - }; - - - // SCROLLSPY PLUGIN DEFINITION - // =========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.scrollspy'); - var options = typeof option == 'object' && option; - - if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.scrollspy; - - $.fn.scrollspy = Plugin; - $.fn.scrollspy.Constructor = ScrollSpy; - - - // SCROLLSPY NO CONFLICT - // ===================== - - $.fn.scrollspy.noConflict = function () { - $.fn.scrollspy = old; - return this - }; - - - // SCROLLSPY DATA-API - // ================== - - $(window).on('load.bs.scrollspy.data-api', function () { - $('[data-spy="scroll"]').each(function () { - var $spy = $(this); - Plugin.call($spy, $spy.data()) - }) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: tab.js v3.3.6 - * http://getbootstrap.com/javascript/#tabs - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // TAB CLASS DEFINITION - // ==================== - - var Tab = function (element) { - // jscs:disable requireDollarBeforejQueryAssignment - this.element = $(element) - // jscs:enable requireDollarBeforejQueryAssignment - }; - - Tab.VERSION = '3.3.6'; - - Tab.TRANSITION_DURATION = 150; - - Tab.prototype.show = function () { - var $this = this.element; - var $ul = $this.closest('ul:not(.dropdown-menu)'); - var selector = $this.data('target'); - - if (!selector) { - selector = $this.attr('href'); - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - if ($this.parent('li').hasClass('active')) return; - - var $previous = $ul.find('.active:last a'); - var hideEvent = $.Event('hide.bs.tab', { - relatedTarget: $this[0] - }); - var showEvent = $.Event('show.bs.tab', { - relatedTarget: $previous[0] - }); - - $previous.trigger(hideEvent); - $this.trigger(showEvent); - - if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return; - - var $target = $(selector); - - this.activate($this.closest('li'), $ul); - this.activate($target, $target.parent(), function () { - $previous.trigger({ - type: 'hidden.bs.tab', - relatedTarget: $this[0] - }); - $this.trigger({ - type: 'shown.bs.tab', - relatedTarget: $previous[0] - }) - }) - }; - - Tab.prototype.activate = function (element, container, callback) { - var $active = container.find('> .active'); - var transition = callback - && $.support.transition - && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length); - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', false); - - element - .addClass('active') - .find('[data-toggle="tab"]') - .attr('aria-expanded', true); - - if (transition) { - element[0].offsetWidth; // reflow for transition - element.addClass('in') - } else { - element.removeClass('fade') - } - - if (element.parent('.dropdown-menu').length) { - element - .closest('li.dropdown') - .addClass('active') - .end() - .find('[data-toggle="tab"]') - .attr('aria-expanded', true) - } - - callback && callback() - } - - $active.length && transition ? - $active - .one('bsTransitionEnd', next) - .emulateTransitionEnd(Tab.TRANSITION_DURATION) : - next(); - - $active.removeClass('in') - }; - - - // TAB PLUGIN DEFINITION - // ===================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.tab'); - - if (!data) $this.data('bs.tab', (data = new Tab(this))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.tab; - - $.fn.tab = Plugin; - $.fn.tab.Constructor = Tab; - - - // TAB NO CONFLICT - // =============== - - $.fn.tab.noConflict = function () { - $.fn.tab = old; - return this - }; - - - // TAB DATA-API - // ============ - - var clickHandler = function (e) { - e.preventDefault(); - Plugin.call($(this), 'show') - }; - - $(document) - .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) - .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: affix.js v3.3.6 - * http://getbootstrap.com/javascript/#affix - * ======================================================================== - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // AFFIX CLASS DEFINITION - // ====================== - - var Affix = function (element, options) { - this.options = $.extend({}, Affix.DEFAULTS, options); - - this.$target = $(this.options.target) - .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) - .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)); - - this.$element = $(element); - this.affixed = null; - this.unpin = null; - this.pinnedOffset = null; - - this.checkPosition() - }; - - Affix.VERSION = '3.3.6'; - - Affix.RESET = 'affix affix-top affix-bottom'; - - Affix.DEFAULTS = { - offset: 0, - target: window - }; - - Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { - var scrollTop = this.$target.scrollTop(); - var position = this.$element.offset(); - var targetHeight = this.$target.height(); - - if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false; - - if (this.affixed == 'bottom') { - if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'; - return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' - } - - var initializing = this.affixed == null; - var colliderTop = initializing ? scrollTop : position.top; - var colliderHeight = initializing ? targetHeight : height; - - if (offsetTop != null && scrollTop <= offsetTop) return 'top'; - if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'; - - return false - }; - - Affix.prototype.getPinnedOffset = function () { - if (this.pinnedOffset) return this.pinnedOffset; - this.$element.removeClass(Affix.RESET).addClass('affix'); - var scrollTop = this.$target.scrollTop(); - var position = this.$element.offset(); - return (this.pinnedOffset = position.top - scrollTop) - }; - - Affix.prototype.checkPositionWithEventLoop = function () { - setTimeout($.proxy(this.checkPosition, this), 1) - }; - - Affix.prototype.checkPosition = function () { - if (!this.$element.is(':visible')) return; - - var height = this.$element.height(); - var offset = this.options.offset; - var offsetTop = offset.top; - var offsetBottom = offset.bottom; - var scrollHeight = Math.max($(document).height(), $(document.body).height()); - - if (typeof offset != 'object') offsetBottom = offsetTop = offset; - if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element); - if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element); - - var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom); - - if (this.affixed != affix) { - if (this.unpin != null) this.$element.css('top', ''); - - var affixType = 'affix' + (affix ? '-' + affix : ''); - var e = $.Event(affixType + '.bs.affix'); - - this.$element.trigger(e); - - if (e.isDefaultPrevented()) return; - - this.affixed = affix; - this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null; - - this.$element - .removeClass(Affix.RESET) - .addClass(affixType) - .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') - } - - if (affix == 'bottom') { - this.$element.offset({ - top: scrollHeight - height - offsetBottom - }) - } - }; - - - // AFFIX PLUGIN DEFINITION - // ======================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this); - var data = $this.data('bs.affix'); - var options = typeof option == 'object' && option; - - if (!data) $this.data('bs.affix', (data = new Affix(this, options))); - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.affix; - - $.fn.affix = Plugin; - $.fn.affix.Constructor = Affix; - - - // AFFIX NO CONFLICT - // ================= - - $.fn.affix.noConflict = function () { - $.fn.affix = old; - return this - }; - - - // AFFIX DATA-API - // ============== - - $(window).on('load', function () { - $('[data-spy="affix"]').each(function () { - var $spy = $(this); - var data = $spy.data(); - - data.offset = data.offset || {}; - - if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom; - if (data.offsetTop != null) data.offset.top = data.offsetTop; - - Plugin.call($spy, data) - }) - }) - -}(jQuery); diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.min.js b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.min.js deleted file mode 100644 index e79c06513..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/npm.js b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/npm.js deleted file mode 100644 index f5825e753..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap/js/npm.js +++ /dev/null @@ -1,13 +0,0 @@ -// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. -require('../../js/transition.js'); -require('../../js/alert.js'); -require('../../js/button.js'); -require('../../js/carousel.js'); -require('../../js/collapse.js'); -require('../../js/dropdown.js'); -require('../../js/modal.js'); -require('../../js/tooltip.js'); -require('../../js/popover.js'); -require('../../js/scrollspy.js'); -require('../../js/tab.js'); -require('../../js/affix.js'); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/jquery.dataTables.js b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/jquery.dataTables.js deleted file mode 100644 index 87566af62..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/jquery.dataTables.js +++ /dev/null @@ -1,15254 +0,0 @@ -/*! DataTables 1.10.12 - * ©2008-2015 SpryMedia Ltd - datatables.net/license - */ - -/** - * @summary DataTables - * @description Paginate, search and order HTML tables - * @version 1.10.12 - * @file jquery.dataTables.js - * @author SpryMedia Ltd (www.sprymedia.co.uk) - * @contact www.sprymedia.co.uk/contact - * @copyright Copyright 2008-2015 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 - */ - -/*jslint evil: true, undef: true, browser: true */ -/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ - -(function( factory ) { - "use strict"; - - if ( typeof define === 'function' && define.amd ) { - // AMD - define( ['jquery'], function ( $ ) { - return factory( $, window, document ); - } ); - } - else if ( typeof exports === 'object' ) { - // CommonJS - module.exports = function (root, $) { - if ( ! root ) { - // CommonJS environments without a window global must pass a - // root. This will give an error otherwise - root = window; - } - - if ( ! $ ) { - $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window - require('jquery') : - require('jquery')( root ); - } - - return factory( $, root, root.document ); - }; - } - else { - // Browser - factory( jQuery, window, document ); - } -} -(function( $, window, document, undefined ) { - "use strict"; - - /** - * DataTables is a plug-in for the jQuery Javascript library. It is a highly - * flexible tool, based upon the foundations of progressive enhancement, - * which will add advanced interaction controls to any HTML table. For a - * full list of features please refer to - * [DataTables.net](href="http://datatables.net). - * - * Note that the `DataTable` object is not a global variable but is aliased - * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may - * be accessed. - * - * @class - * @param {object} [init={}] Configuration object for DataTables. Options - * are defined by {@link DataTable.defaults} - * @requires jQuery 1.7+ - * - * @example - * // Basic initialisation - * $(document).ready( function { - * $('#example').dataTable(); - * } ); - * - * @example - * // Initialisation with configuration options - in this case, disable - * // pagination and sorting. - * $(document).ready( function { - * $('#example').dataTable( { - * "paginate": false, - * "sort": false - * } ); - * } ); - */ - var DataTable = function ( options ) - { - /** - * Perform a jQuery selector action on the table's TR elements (from the tbody) and - * return the resulting jQuery object. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter - * criterion ("applied") or all TR elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {object} jQuery object, filtered by the given selector. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Highlight every second row - * oTable.$('tr:odd').css('backgroundColor', 'blue'); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to rows with 'Webkit' in them, add a background colour and then - * // remove the filter, thus highlighting the 'Webkit' rows only. - * oTable.fnFilter('Webkit'); - * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); - * oTable.fnFilter(''); - * } ); - */ - this.$ = function ( sSelector, oOpts ) - { - return this.api(true).$( sSelector, oOpts ); - }; - - - /** - * Almost identical to $ in operation, but in this case returns the data for the matched - * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes - * rather than any descendants, so the data can be obtained for the row/cell. If matching - * rows are found, the data returned is the original data array/object that was used to - * create the row (or a generated array if from a DOM source). - * - * This method is often useful in-combination with $ where both functions are given the - * same parameters and the array indexes will match identically. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select elements that meet the current filter - * criterion ("applied") or all elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the data in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {array} Data for the matched elements. If any elements, as a result of the - * selector, were not TR, TD or TH elements in the DataTable, they will have a null - * entry in the array. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Get the data from the first row in the table - * var data = oTable._('tr:first'); - * - * // Do something useful with the data - * alert( "First cell is: "+data[0] ); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to 'Webkit' and get all data for - * oTable.fnFilter('Webkit'); - * var data = oTable._('tr', {"search": "applied"}); - * - * // Do something with the data - * alert( data.length+" rows matched the search" ); - * } ); - */ - this._ = function ( sSelector, oOpts ) - { - return this.api(true).rows( sSelector, oOpts ).data(); - }; - - - /** - * Create a DataTables Api instance, with the currently selected tables for - * the Api's context. - * @param {boolean} [traditional=false] Set the API instance's context to be - * only the table referred to by the `DataTable.ext.iApiIndex` option, as was - * used in the API presented by DataTables 1.9- (i.e. the traditional mode), - * or if all tables captured in the jQuery object should be used. - * @return {DataTables.Api} - */ - this.api = function ( traditional ) - { - return traditional ? - new _Api( - _fnSettingsFromNode( this[ _ext.iApiIndex ] ) - ) : - new _Api( this ); - }; - - - /** - * Add a single new row or multiple rows of data to the table. Please note - * that this is suitable for client-side processing only - if you are using - * server-side processing (i.e. "bServerSide": true), then to add data, you - * must add it to the data source, i.e. the server-side, through an Ajax call. - * @param {array|object} data The data to be added to the table. This can be: - *
      - *
    • 1D array of data - add a single row with the data provided
    • - *
    • 2D array of arrays - add multiple rows in a single call
    • - *
    • object - data object when using mData
    • - *
    • array of objects - multiple data objects when using mData
    • - *
    - * @param {bool} [redraw=true] redraw the table or not - * @returns {array} An array of integers, representing the list of indexes in - * aoData ({@link DataTable.models.oSettings}) that have been added to - * the table. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Global var for counter - * var giCount = 2; - * - * $(document).ready(function() { - * $('#example').dataTable(); - * } ); - * - * function fnClickAddRow() { - * $('#example').dataTable().fnAddData( [ - * giCount+".1", - * giCount+".2", - * giCount+".3", - * giCount+".4" ] - * ); - * - * giCount++; - * } - */ - this.fnAddData = function( data, redraw ) - { - var api = this.api( true ); - - /* Check if we want to add multiple rows or not */ - var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ? - api.rows.add( data ) : - api.row.add( data ); - - if ( redraw === undefined || redraw ) { - api.draw(); - } - - return rows.flatten().toArray(); - }; - - - /** - * This function will make DataTables recalculate the column sizes, based on the data - * contained in the table and the sizes applied to the columns (in the DOM, CSS or - * through the sWidth parameter). This can be useful when the width of the table's - * parent element changes (for example a window resize). - * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable( { - * "sScrollY": "200px", - * "bPaginate": false - * } ); - * - * $(window).bind('resize', function () { - * oTable.fnAdjustColumnSizing(); - * } ); - * } ); - */ - this.fnAdjustColumnSizing = function ( bRedraw ) - { - var api = this.api( true ).columns.adjust(); - var settings = api.settings()[0]; - var scroll = settings.oScroll; - - if ( bRedraw === undefined || bRedraw ) { - api.draw( false ); - } - else if ( scroll.sX !== "" || scroll.sY !== "" ) { - /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ - _fnScrollDraw( settings ); - } - }; - - - /** - * Quickly and simply clear a table - * @param {bool} [bRedraw=true] redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) - * oTable.fnClearTable(); - * } ); - */ - this.fnClearTable = function( bRedraw ) - { - var api = this.api( true ).clear(); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); - } - }; - - - /** - * The exact opposite of 'opening' a row, this function will close any rows which - * are currently 'open'. - * @param {node} nTr the table row to 'close' - * @returns {int} 0 on success, or 1 if failed (can't find the row) - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnClose = function( nTr ) - { - this.api( true ).row( nTr ).child.hide(); - }; - - - /** - * Remove a row for the table - * @param {mixed} target The index of the row from aoData to be deleted, or - * the TR element you want to delete - * @param {function|null} [callBack] Callback function - * @param {bool} [redraw=true] Redraw the table or not - * @returns {array} The row that was deleted - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Immediately remove the first row - * oTable.fnDeleteRow( 0 ); - * } ); - */ - this.fnDeleteRow = function( target, callback, redraw ) - { - var api = this.api( true ); - var rows = api.rows( target ); - var settings = rows.settings()[0]; - var data = settings.aoData[ rows[0][0] ]; - - rows.remove(); - - if ( callback ) { - callback.call( this, settings, data ); - } - - if ( redraw === undefined || redraw ) { - api.draw(); - } - - return data; - }; - - - /** - * Restore the table to it's original state in the DOM by removing all of DataTables - * enhancements, alterations to the DOM structure of the table and event listeners. - * @param {boolean} [remove=false] Completely remove the table from the DOM - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * // This example is fairly pointless in reality, but shows how fnDestroy can be used - * var oTable = $('#example').dataTable(); - * oTable.fnDestroy(); - * } ); - */ - this.fnDestroy = function ( remove ) - { - this.api( true ).destroy( remove ); - }; - - - /** - * Redraw the table - * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) - * oTable.fnDraw(); - * } ); - */ - this.fnDraw = function( complete ) - { - // Note that this isn't an exact match to the old call to _fnDraw - it takes - // into account the new data, but can hold position. - this.api( true ).draw( complete ); - }; - - - /** - * Filter the input based on data - * @param {string} sInput String to filter the table on - * @param {int|null} [iColumn] Column to limit filtering to - * @param {bool} [bRegex=false] Treat as regular expression or not - * @param {bool} [bSmart=true] Perform smart filtering or not - * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) - * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sometime later - filter... - * oTable.fnFilter( 'test string' ); - * } ); - */ - this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) - { - var api = this.api( true ); - - if ( iColumn === null || iColumn === undefined ) { - api.search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - else { - api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - - api.draw(); - }; - - - /** - * Get the data for the whole table, an individual row or an individual cell based on the - * provided parameters. - * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as - * a TR node then the data source for the whole row will be returned. If given as a - * TD/TH cell node then iCol will be automatically calculated and the data for the - * cell returned. If given as an integer, then this is treated as the aoData internal - * data index for the row (see fnGetPosition) and the data for that row used. - * @param {int} [col] Optional column index that you want the data of. - * @returns {array|object|string} If mRow is undefined, then the data for all rows is - * returned. If mRow is defined, just data for that row, and is iCol is - * defined, only data for the designated cell is returned. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Row data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); - * - * oTable.$('tr').click( function () { - * var data = oTable.fnGetData( this ); - * // ... do something with the array / object of data for the row - * } ); - * } ); - * - * @example - * // Individual cell data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); - * - * oTable.$('td').click( function () { - * var sData = oTable.fnGetData( this ); - * alert( 'The cell clicked on had the value of '+sData ); - * } ); - * } ); - */ - this.fnGetData = function( src, col ) - { - var api = this.api( true ); - - if ( src !== undefined ) { - var type = src.nodeName ? src.nodeName.toLowerCase() : ''; - - return col !== undefined || type == 'td' || type == 'th' ? - api.cell( src, col ).data() : - api.row( src ).data() || null; - } - - return api.data().toArray(); - }; - - - /** - * Get an array of the TR nodes that are used in the table's body. Note that you will - * typically want to use the '$' API method in preference to this as it is more - * flexible. - * @param {int} [iRow] Optional row index for the TR element you want - * @returns {array|node} If iRow is undefined, returns an array of all TR elements - * in the table's body, or iRow is defined, just the TR element requested. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Get the nodes from the table - * var nNodes = oTable.fnGetNodes( ); - * } ); - */ - this.fnGetNodes = function( iRow ) - { - var api = this.api( true ); - - return iRow !== undefined ? - api.row( iRow ).node() : - api.rows().nodes().flatten().toArray(); - }; - - - /** - * Get the array indexes of a particular cell from it's DOM element - * and column index including hidden columns - * @param {node} node this can either be a TR, TD or TH in the table's body - * @returns {int} If nNode is given as a TR, then a single index is returned, or - * if given as a cell, an array of [row index, column index (visible), - * column index (all)] is given. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * $('#example tbody td').click( function () { - * // Get the position of the current data from the node - * var aPos = oTable.fnGetPosition( this ); - * - * // Get the data array for this row - * var aData = oTable.fnGetData( aPos[0] ); - * - * // Update the data array and return the value - * aData[ aPos[1] ] = 'clicked'; - * this.innerHTML = 'clicked'; - * } ); - * - * // Init DataTables - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnGetPosition = function( node ) - { - var api = this.api( true ); - var nodeName = node.nodeName.toUpperCase(); - - if ( nodeName == 'TR' ) { - return api.row( node ).index(); - } - else if ( nodeName == 'TD' || nodeName == 'TH' ) { - var cell = api.cell( node ).index(); - - return [ - cell.row, - cell.columnVisible, - cell.column - ]; - } - return null; - }; - - - /** - * Check to see if a row is 'open' or not. - * @param {node} nTr the table row to check - * @returns {boolean} true if the row is currently open, false otherwise - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnIsOpen = function( nTr ) - { - return this.api( true ).row( nTr ).child.isShown(); - }; - - - /** - * This function will place a new row directly after a row which is currently - * on display on the page, with the HTML contents that is passed into the - * function. This can be used, for example, to ask for confirmation that a - * particular record should be deleted. - * @param {node} nTr The table row to 'open' - * @param {string|node|jQuery} mHtml The HTML to put into the row - * @param {string} sClass Class to give the new TD cell - * @returns {node} The row opened. Note that if the table row passed in as the - * first parameter, is not found in the table, this method will silently - * return. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnOpen = function( nTr, mHtml, sClass ) - { - return this.api( true ) - .row( nTr ) - .child( mHtml, sClass ) - .show() - .child()[0]; - }; - - - /** - * Change the pagination - provides the internal logic for pagination in a simple API - * function. With this function you can have a DataTables table go to the next, - * previous, first or last pages. - * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" - * or page number to jump to (integer), note that page 0 is the first page. - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnPageChange( 'next' ); - * } ); - */ - this.fnPageChange = function ( mAction, bRedraw ) - { - var api = this.api( true ).page( mAction ); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(false); - } - }; - - - /** - * Show a particular column - * @param {int} iCol The column whose display should be changed - * @param {bool} bShow Show (true) or hide (false) the column - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Hide the second column after initialisation - * oTable.fnSetColumnVis( 1, false ); - * } ); - */ - this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) - { - var api = this.api( true ).column( iCol ).visible( bShow ); - - if ( bRedraw === undefined || bRedraw ) { - api.columns.adjust().draw(); - } - }; - - - /** - * Get the settings for a particular table for external manipulation - * @returns {object} DataTables settings object. See - * {@link DataTable.models.oSettings} - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * var oSettings = oTable.fnSettings(); - * - * // Show an example parameter from the settings - * alert( oSettings._iDisplayStart ); - * } ); - */ - this.fnSettings = function() - { - return _fnSettingsFromNode( this[_ext.iApiIndex] ); - }; - - - /** - * Sort the table by a particular column - * @param {int} iCol the data index to sort on. Note that this will not match the - * 'display index' if you have hidden data entries - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sort immediately with columns 0 and 1 - * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); - * } ); - */ - this.fnSort = function( aaSort ) - { - this.api( true ).order( aaSort ).draw(); - }; - - - /** - * Attach a sort listener to an element for a given column - * @param {node} nNode the element to attach the sort listener to - * @param {int} iColumn the column that a click on this node will sort on - * @param {function} [fnCallback] callback function when sort is run - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sort on column 1, when 'sorter' is clicked on - * oTable.fnSortListener( document.getElementById('sorter'), 1 ); - * } ); - */ - this.fnSortListener = function( nNode, iColumn, fnCallback ) - { - this.api( true ).order.listener( nNode, iColumn, fnCallback ); - }; - - - /** - * Update a table cell or row - this method will accept either a single value to - * update the cell with, an array of values with one element for each column or - * an object in the same format as the original data source. The function is - * self-referencing in order to make the multi column updates easier. - * @param {object|array|string} mData Data to update the cell/row with - * @param {node|int} mRow TR element you want to update or the aoData index - * @param {int} [iColumn] The column to update, give as null or undefined to - * update a whole row. - * @param {bool} [bRedraw=true] Redraw the table or not - * @param {bool} [bAction=true] Perform pre-draw actions or not - * @returns {int} 0 on success, 1 on error - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell - * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row - * } ); - */ - this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) - { - var api = this.api( true ); - - if ( iColumn === undefined || iColumn === null ) { - api.row( mRow ).data( mData ); - } - else { - api.cell( mRow, iColumn ).data( mData ); - } - - if ( bAction === undefined || bAction ) { - api.columns.adjust(); - } - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); - } - return 0; - }; - - - /** - * Provide a common method for plug-ins to check the version of DataTables being used, in order - * to ensure compatibility. - * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the - * formats "X" and "X.Y" are also acceptable. - * @returns {boolean} true if this version of DataTables is greater or equal to the required - * version, or false if this version of DataTales is not suitable - * @method - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * alert( oTable.fnVersionCheck( '1.9.0' ) ); - * } ); - */ - this.fnVersionCheck = _ext.fnVersionCheck; - - - var _that = this; - var emptyInit = options === undefined; - var len = this.length; - - if ( emptyInit ) { - options = {}; - } - - this.oApi = this.internal = _ext.internal; - - // Extend with old style plug-in API methods - for ( var fn in DataTable.ext.internal ) { - if ( fn ) { - this[fn] = _fnExternApiFunc(fn); - } - } - - this.each(function() { - // For each initialisation we want to give it a clean initialisation - // object that can be bashed around - var o = {}; - var oInit = len > 1 ? // optimisation for single table case - _fnExtend( o, options, true ) : - options; - - /*global oInit,_that,emptyInit*/ - var i=0, iLen, j, jLen, k, kLen; - var sId = this.getAttribute( 'id' ); - var bInitHandedOff = false; - var defaults = DataTable.defaults; - var $this = $(this); - - - /* Sanity check */ - if ( this.nodeName.toLowerCase() != 'table' ) - { - _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); - return; - } - - /* Backwards compatibility for the defaults */ - _fnCompatOpts( defaults ); - _fnCompatCols( defaults.column ); - - /* Convert the camel-case defaults to Hungarian */ - _fnCamelToHungarian( defaults, defaults, true ); - _fnCamelToHungarian( defaults.column, defaults.column, true ); - - /* Setting up the initialisation object */ - _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) ); - - - - /* Check to see if we are re-initialising a table */ - var allSettings = DataTable.settings; - for ( i=0, iLen=allSettings.length ; i').appendTo(this); - } - oSettings.nTHead = thead[0]; - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) - { - tbody = $('
    ', { - 'valign': 'top', - 'colSpan': _fnVisbleColumns( oSettings ), - 'class': oSettings.oClasses.sRowEmpty - } ).html( sZero ) )[0]; - } - - /* Header and footer callbacks */ - _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], - _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); - - _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], - _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); - - var body = $(oSettings.nTBody); - - body.children().detach(); - body.append( $(anRows) ); - - /* Call all required callback functions for the end of a draw */ - _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); - - /* Draw is complete, sorting and filtering must be as well */ - oSettings.bSorted = false; - oSettings.bFiltered = false; - oSettings.bDrawing = false; - } - - - /** - * Redraw the table - taking account of the various features which are enabled - * @param {object} oSettings dataTables settings object - * @param {boolean} [holdPosition] Keep the current paging position. By default - * the paging is reset to the first page - * @memberof DataTable#oApi - */ - function _fnReDraw( settings, holdPosition ) - { - var - features = settings.oFeatures, - sort = features.bSort, - filter = features.bFilter; - - if ( sort ) { - _fnSort( settings ); - } - - if ( filter ) { - _fnFilterComplete( settings, settings.oPreviousSearch ); - } - else { - // No filtering, so we want to just use the display master - settings.aiDisplay = settings.aiDisplayMaster.slice(); - } - - if ( holdPosition !== true ) { - settings._iDisplayStart = 0; - } - - // Let any modules know about the draw hold position state (used by - // scrolling internally) - settings._drawHold = holdPosition; - - _fnDraw( settings ); - - settings._drawHold = false; - } - - - /** - * Add the options to the page HTML for the table - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAddOptionsHtml ( oSettings ) - { - var classes = oSettings.oClasses; - var table = $(oSettings.nTable); - var holding = $('
    ').insertBefore( table ); // Holding element for speed - var features = oSettings.oFeatures; - - // All DataTables are wrapped in a div - var insert = $('
    ', { - id: oSettings.sTableId+'_wrapper', - 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) - } ); - - oSettings.nHolding = holding[0]; - oSettings.nTableWrapper = insert[0]; - oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; - - /* Loop over the user set positioning and place the elements as needed */ - var aDom = oSettings.sDom.split(''); - var featureNode, cOption, nNewNode, cNext, sAttr, j; - for ( var i=0 ; i')[0]; - - /* Check to see if we should append an id and/or a class name to the container */ - cNext = aDom[i+1]; - if ( cNext == "'" || cNext == '"' ) - { - sAttr = ""; - j = 2; - while ( aDom[i+j] != cNext ) - { - sAttr += aDom[i+j]; - j++; - } - - if ( sAttr == "H" ) - { - sAttr = classes.sJUIHeader; - } - else if ( sAttr == "F" ) - { - sAttr = classes.sJUIFooter; - } - - /* The attribute can be in the format of "#id.class", "#id" or "class" This logic - * breaks the string into parts and applies them as needed - */ - if ( sAttr.indexOf('.') != -1 ) - { - var aSplit = sAttr.split('.'); - nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); - nNewNode.className = aSplit[1]; - } - else if ( sAttr.charAt(0) == "#" ) - { - nNewNode.id = sAttr.substr(1, sAttr.length-1); - } - else - { - nNewNode.className = sAttr; - } - - i += j; /* Move along the position array */ - } - - insert.append( nNewNode ); - insert = $(nNewNode); - } - else if ( cOption == '>' ) - { - /* End container div */ - insert = insert.parent(); - } - else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) - { - /* Length */ - featureNode = _fnFeatureHtmlLength( oSettings ); - } - else if ( cOption == 'f' && features.bFilter ) - { - /* Filter */ - featureNode = _fnFeatureHtmlFilter( oSettings ); - } - else if ( cOption == 'r' && features.bProcessing ) - { - /* pRocessing */ - featureNode = _fnFeatureHtmlProcessing( oSettings ); - } - else if ( cOption == 't' ) - { - /* Table */ - featureNode = _fnFeatureHtmlTable( oSettings ); - } - else if ( cOption == 'i' && features.bInfo ) - { - /* Info */ - featureNode = _fnFeatureHtmlInfo( oSettings ); - } - else if ( cOption == 'p' && features.bPaginate ) - { - /* Pagination */ - featureNode = _fnFeatureHtmlPaginate( oSettings ); - } - else if ( DataTable.ext.feature.length !== 0 ) - { - /* Plug-in features */ - var aoFeatures = DataTable.ext.feature; - for ( var k=0, kLen=aoFeatures.length ; k'; - - var str = language.sSearch; - str = str.match(/_INPUT_/) ? - str.replace('_INPUT_', input) : - str+input; - - var filter = $('
    ', { - 'id': ! features.f ? tableId+'_filter' : null, - 'class': classes.sFilter - } ) - .append( $('
    ').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : - data.anCells[ colIdx ]; - } - - - /** - * Get the maximum strlen for each data column - * @param {object} settings dataTables settings object - * @param {int} colIdx column of interest - * @returns {string} max string length for each column - * @memberof DataTable#oApi - */ - function _fnGetMaxLenString( settings, colIdx ) - { - var s, max=-1, maxIdx = -1; - - for ( var i=0, ien=settings.aoData.length ; i max ) { - max = s.length; - maxIdx = i; - } - } - - return maxIdx; - } - - - /** - * Append a CSS unit (only if required) to a string - * @param {string} value to css-ify - * @returns {string} value with css unit - * @memberof DataTable#oApi - */ - function _fnStringToCss( s ) - { - if ( s === null ) { - return '0px'; - } - - if ( typeof s == 'number' ) { - return s < 0 ? - '0px' : - s+'px'; - } - - // Check it has a unit character already - return s.match(/\d$/) ? - s+'px' : - s; - } - - - - function _fnSortFlatten ( settings ) - { - var - i, iLen, k, kLen, - aSort = [], - aiOrig = [], - aoColumns = settings.aoColumns, - aDataSort, iCol, sType, srcCol, - fixed = settings.aaSortingFixed, - fixedObj = $.isPlainObject( fixed ), - nestedSort = [], - add = function ( a ) { - if ( a.length && ! $.isArray( a[0] ) ) { - // 1D array - nestedSort.push( a ); - } - else { - // 2D array - $.merge( nestedSort, a ); - } - }; - - // Build the sort array, with pre-fix and post-fix options if they have been - // specified - if ( $.isArray( fixed ) ) { - add( fixed ); - } - - if ( fixedObj && fixed.pre ) { - add( fixed.pre ); - } - - add( settings.aaSorting ); - - if (fixedObj && fixed.post ) { - add( fixed.post ); - } - - for ( i=0 ; iy ? 1 : 0; - if ( test !== 0 ) { - return sort.dir === 'asc' ? test : -test; - } - } - - x = aiOrig[a]; - y = aiOrig[b]; - return xy ? 1 : 0; - } ); - } - else { - // Depreciated - remove in 1.11 (providing a plug-in option) - // Not all sort types have formatting methods, so we have to call their sorting - // methods. - displayMaster.sort( function ( a, b ) { - var - x, y, k, l, test, sort, fn, - len=aSort.length, - dataA = aoData[a]._aSortData, - dataB = aoData[b]._aSortData; - - for ( k=0 ; ky ? 1 : 0; - } ); - } - } - - /* Tell the draw function that we have sorted the data */ - oSettings.bSorted = true; - } - - - function _fnSortAria ( settings ) - { - var label; - var nextSort; - var columns = settings.aoColumns; - var aSort = _fnSortFlatten( settings ); - var oAria = settings.oLanguage.oAria; - - // ARIA attributes - need to loop all columns, to update all (removing old - // attributes as needed) - for ( var i=0, iLen=columns.length ; i/g, "" ); - var th = col.nTh; - - // IE7 is throwing an error when setting these properties with jQuery's - // attr() and removeAttr() methods... - th.removeAttribute('aria-sort'); - - /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ - if ( col.bSortable ) { - if ( aSort.length > 0 && aSort[0].col == i ) { - th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" ); - nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0]; - } - else { - nextSort = asSorting[0]; - } - - label = sTitle + ( nextSort === "asc" ? - oAria.sSortAscending : - oAria.sSortDescending - ); - } - else { - label = sTitle; - } - - th.setAttribute('aria-label', label); - } - } - - - /** - * Function to run on user sort request - * @param {object} settings dataTables settings object - * @param {node} attachTo node to attach the handler to - * @param {int} colIdx column sorting index - * @param {boolean} [append=false] Append the requested sort to the existing - * sort if true (i.e. multi-column sort) - * @param {function} [callback] callback function - * @memberof DataTable#oApi - */ - function _fnSortListener ( settings, colIdx, append, callback ) - { - var col = settings.aoColumns[ colIdx ]; - var sorting = settings.aaSorting; - var asSorting = col.asSorting; - var nextSortIdx; - var next = function ( a, overflow ) { - var idx = a._idx; - if ( idx === undefined ) { - idx = $.inArray( a[1], asSorting ); - } - - return idx+1 < asSorting.length ? - idx+1 : - overflow ? - null : - 0; - }; - - // Convert to 2D array if needed - if ( typeof sorting[0] === 'number' ) { - sorting = settings.aaSorting = [ sorting ]; - } - - // If appending the sort then we are multi-column sorting - if ( append && settings.oFeatures.bSortMulti ) { - // Are we already doing some kind of sort on this column? - var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') ); - - if ( sortIdx !== -1 ) { - // Yes, modify the sort - nextSortIdx = next( sorting[sortIdx], true ); - - if ( nextSortIdx === null && sorting.length === 1 ) { - nextSortIdx = 0; // can't remove sorting completely - } - - if ( nextSortIdx === null ) { - sorting.splice( sortIdx, 1 ); - } - else { - sorting[sortIdx][1] = asSorting[ nextSortIdx ]; - sorting[sortIdx]._idx = nextSortIdx; - } - } - else { - // No sort on this column yet - sorting.push( [ colIdx, asSorting[0], 0 ] ); - sorting[sorting.length-1]._idx = 0; - } - } - else if ( sorting.length && sorting[0][0] == colIdx ) { - // Single column - already sorting on this column, modify the sort - nextSortIdx = next( sorting[0] ); - - sorting.length = 1; - sorting[0][1] = asSorting[ nextSortIdx ]; - sorting[0]._idx = nextSortIdx; - } - else { - // Single column - sort only on this column - sorting.length = 0; - sorting.push( [ colIdx, asSorting[0] ] ); - sorting[0]._idx = 0; - } - - // Run the sort by calling a full redraw - _fnReDraw( settings ); - - // callback used for async user interaction - if ( typeof callback == 'function' ) { - callback( settings ); - } - } - - - /** - * Attach a sort handler (click) to a node - * @param {object} settings dataTables settings object - * @param {node} attachTo node to attach the handler to - * @param {int} colIdx column sorting index - * @param {function} [callback] callback function - * @memberof DataTable#oApi - */ - function _fnSortAttachListener ( settings, attachTo, colIdx, callback ) - { - var col = settings.aoColumns[ colIdx ]; - - _fnBindAction( attachTo, {}, function (e) { - /* If the column is not sortable - don't to anything */ - if ( col.bSortable === false ) { - return; - } - - // If processing is enabled use a timeout to allow the processing - // display to be shown - otherwise to it synchronously - if ( settings.oFeatures.bProcessing ) { - _fnProcessingDisplay( settings, true ); - - setTimeout( function() { - _fnSortListener( settings, colIdx, e.shiftKey, callback ); - - // In server-side processing, the draw callback will remove the - // processing display - if ( _fnDataSource( settings ) !== 'ssp' ) { - _fnProcessingDisplay( settings, false ); - } - }, 0 ); - } - else { - _fnSortListener( settings, colIdx, e.shiftKey, callback ); - } - } ); - } - - - /** - * Set the sorting classes on table's body, Note: it is safe to call this function - * when bSort and bSortClasses are false - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnSortingClasses( settings ) - { - var oldSort = settings.aLastSort; - var sortClass = settings.oClasses.sSortColumn; - var sort = _fnSortFlatten( settings ); - var features = settings.oFeatures; - var i, ien, colIdx; - - if ( features.bSort && features.bSortClasses ) { - // Remove old sorting classes - for ( i=0, ien=oldSort.length ; i 0 && state.time < +new Date() - (duration*1000) ) { - return; - } - - // Number of columns have changed - all bets are off, no restore of settings - if ( columns.length !== state.columns.length ) { - return; - } - - // Store the saved state so it might be accessed at any time - settings.oLoadedState = $.extend( true, {}, state ); - - if ( state.start !== undefined ) { - settings._iDisplayStart = state.start; - settings.iInitDisplayStart = state.start; - } - if ( state.length !== undefined ) { - settings._iDisplayLength = state.length; - } - - // Order - if ( state.order !== undefined ) { - settings.aaSorting = []; - $.each( state.order, function ( i, col ) { - settings.aaSorting.push( col[0] >= columns.length ? - [ 0, col[1] ] : - col - ); - } ); - } - - // Search - if ( state.search !== undefined ) { - $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) ); - } - - // Columns - for ( i=0, ien=state.columns.length ; i= end ) - { - start = end - len; - } - - // Keep the start record on the current page - start -= (start % len); - - if ( len === -1 || start < 0 ) - { - start = 0; - } - - settings._iDisplayStart = start; - } - - - function _fnRenderer( settings, type ) - { - var renderer = settings.renderer; - var host = DataTable.ext.renderer[type]; - - if ( $.isPlainObject( renderer ) && renderer[type] ) { - // Specific renderer for this type. If available use it, otherwise use - // the default. - return host[renderer[type]] || host._; - } - else if ( typeof renderer === 'string' ) { - // Common renderer - if there is one available for this type use it, - // otherwise use the default - return host[renderer] || host._; - } - - // Use the default - return host._; - } - - - /** - * Detect the data source being used for the table. Used to simplify the code - * a little (ajax) and to make it compress a little smaller. - * - * @param {object} settings dataTables settings object - * @returns {string} Data source - * @memberof DataTable#oApi - */ - function _fnDataSource ( settings ) - { - if ( settings.oFeatures.bServerSide ) { - return 'ssp'; - } - else if ( settings.ajax || settings.sAjaxSource ) { - return 'ajax'; - } - return 'dom'; - } - - - - - /** - * Computed structure of the DataTables API, defined by the options passed to - * `DataTable.Api.register()` when building the API. - * - * The structure is built in order to speed creation and extension of the Api - * objects since the extensions are effectively pre-parsed. - * - * The array is an array of objects with the following structure, where this - * base array represents the Api prototype base: - * - * [ - * { - * name: 'data' -- string - Property name - * val: function () {}, -- function - Api method (or undefined if just an object - * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result - * propExt: [ ... ] -- array - Array of Api object definitions to extend the property - * }, - * { - * name: 'row' - * val: {}, - * methodExt: [ ... ], - * propExt: [ - * { - * name: 'data' - * val: function () {}, - * methodExt: [ ... ], - * propExt: [ ... ] - * }, - * ... - * ] - * } - * ] - * - * @type {Array} - * @ignore - */ - var __apiStruct = []; - - - /** - * `Array.prototype` reference. - * - * @type object - * @ignore - */ - var __arrayProto = Array.prototype; - - - /** - * Abstraction for `context` parameter of the `Api` constructor to allow it to - * take several different forms for ease of use. - * - * Each of the input parameter types will be converted to a DataTables settings - * object where possible. - * - * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one - * of: - * - * * `string` - jQuery selector. Any DataTables' matching the given selector - * with be found and used. - * * `node` - `TABLE` node which has already been formed into a DataTable. - * * `jQuery` - A jQuery object of `TABLE` nodes. - * * `object` - DataTables settings object - * * `DataTables.Api` - API instance - * @return {array|null} Matching DataTables settings objects. `null` or - * `undefined` is returned if no matching DataTable is found. - * @ignore - */ - var _toSettings = function ( mixed ) - { - var idx, jq; - var settings = DataTable.settings; - var tables = $.map( settings, function (el, i) { - return el.nTable; - } ); - - if ( ! mixed ) { - return []; - } - else if ( mixed.nTable && mixed.oApi ) { - // DataTables settings object - return [ mixed ]; - } - else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { - // Table node - idx = $.inArray( mixed, tables ); - return idx !== -1 ? [ settings[idx] ] : null; - } - else if ( mixed && typeof mixed.settings === 'function' ) { - return mixed.settings().toArray(); - } - else if ( typeof mixed === 'string' ) { - // jQuery selector - jq = $(mixed); - } - else if ( mixed instanceof $ ) { - // jQuery object (also DataTables instance) - jq = mixed; - } - - if ( jq ) { - return jq.map( function(i) { - idx = $.inArray( this, tables ); - return idx !== -1 ? settings[idx] : null; - } ).toArray(); - } - }; - - - /** - * DataTables API class - used to control and interface with one or more - * DataTables enhanced tables. - * - * The API class is heavily based on jQuery, presenting a chainable interface - * that you can use to interact with tables. Each instance of the API class has - * a "context" - i.e. the tables that it will operate on. This could be a single - * table, all tables on a page or a sub-set thereof. - * - * Additionally the API is designed to allow you to easily work with the data in - * the tables, retrieving and manipulating it as required. This is done by - * presenting the API class as an array like interface. The contents of the - * array depend upon the actions requested by each method (for example - * `rows().nodes()` will return an array of nodes, while `rows().data()` will - * return an array of objects or arrays depending upon your table's - * configuration). The API object has a number of array like methods (`push`, - * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, - * `unique` etc) to assist your working with the data held in a table. - * - * Most methods (those which return an Api instance) are chainable, which means - * the return from a method call also has all of the methods available that the - * top level object had. For example, these two calls are equivalent: - * - * // Not chained - * api.row.add( {...} ); - * api.draw(); - * - * // Chained - * api.row.add( {...} ).draw(); - * - * @class DataTable.Api - * @param {array|object|string|jQuery} context DataTable identifier. This is - * used to define which DataTables enhanced tables this API will operate on. - * Can be one of: - * - * * `string` - jQuery selector. Any DataTables' matching the given selector - * with be found and used. - * * `node` - `TABLE` node which has already been formed into a DataTable. - * * `jQuery` - A jQuery object of `TABLE` nodes. - * * `object` - DataTables settings object - * @param {array} [data] Data to initialise the Api instance with. - * - * @example - * // Direct initialisation during DataTables construction - * var api = $('#example').DataTable(); - * - * @example - * // Initialisation using a DataTables jQuery object - * var api = $('#example').dataTable().api(); - * - * @example - * // Initialisation as a constructor - * var api = new $.fn.DataTable.Api( 'table.dataTable' ); - */ - _Api = function ( context, data ) - { - if ( ! (this instanceof _Api) ) { - return new _Api( context, data ); - } - - var settings = []; - var ctxSettings = function ( o ) { - var a = _toSettings( o ); - if ( a ) { - settings = settings.concat( a ); - } - }; - - if ( $.isArray( context ) ) { - for ( var i=0, ien=context.length ; i idx ? - new _Api( ctx[idx], this[idx] ) : - null; - }, - - - filter: function ( fn ) - { - var a = []; - - if ( __arrayProto.filter ) { - a = __arrayProto.filter.call( this, fn, this ); - } - else { - // Compatibility for browsers without EMCA-252-5 (JS 1.6) - for ( var i=0, ien=this.length ; i 0 ) { - return ctx[0].json; - } - - // else return undefined; - } ); - - - /** - * Get the data submitted in the last Ajax request - */ - _api_register( 'ajax.params()', function () { - var ctx = this.context; - - if ( ctx.length > 0 ) { - return ctx[0].oAjaxData; - } - - // else return undefined; - } ); - - - /** - * Reload tables from the Ajax data source. Note that this function will - * automatically re-draw the table when the remote data has been loaded. - * - * @param {boolean} [reset=true] Reset (default) or hold the current paging - * position. A full re-sort and re-filter is performed when this method is - * called, which is why the pagination reset is the default action. - * @returns {DataTables.Api} this - */ - _api_register( 'ajax.reload()', function ( callback, resetPaging ) { - return this.iterator( 'table', function (settings) { - __reload( settings, resetPaging===false, callback ); - } ); - } ); - - - /** - * Get the current Ajax URL. Note that this returns the URL from the first - * table in the current context. - * - * @return {string} Current Ajax source URL - *//** - * Set the Ajax URL. Note that this will set the URL for all tables in the - * current context. - * - * @param {string} url URL to set. - * @returns {DataTables.Api} this - */ - _api_register( 'ajax.url()', function ( url ) { - var ctx = this.context; - - if ( url === undefined ) { - // get - if ( ctx.length === 0 ) { - return undefined; - } - ctx = ctx[0]; - - return ctx.ajax ? - $.isPlainObject( ctx.ajax ) ? - ctx.ajax.url : - ctx.ajax : - ctx.sAjaxSource; - } - - // set - return this.iterator( 'table', function ( settings ) { - if ( $.isPlainObject( settings.ajax ) ) { - settings.ajax.url = url; - } - else { - settings.ajax = url; - } - // No need to consider sAjaxSource here since DataTables gives priority - // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any - // value of `sAjaxSource` redundant. - } ); - } ); - - - /** - * Load data from the newly set Ajax URL. Note that this method is only - * available when `ajax.url()` is used to set a URL. Additionally, this method - * has the same effect as calling `ajax.reload()` but is provided for - * convenience when setting a new URL. Like `ajax.reload()` it will - * automatically redraw the table once the remote data has been loaded. - * - * @returns {DataTables.Api} this - */ - _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { - // Same as a reload, but makes sense to present it for easy access after a - // url change - return this.iterator( 'table', function ( ctx ) { - __reload( ctx, resetPaging===false, callback ); - } ); - } ); - - - - - var _selector_run = function ( type, selector, selectFn, settings, opts ) - { - var - out = [], res, - a, i, ien, j, jen, - selectorType = typeof selector; - - // Can't just check for isArray here, as an API or jQuery instance might be - // given with their array like look - if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { - selector = [ selector ]; - } - - for ( i=0, ien=selector.length ; i 0 ) { - // Assign the first element to the first item in the instance - // and truncate the instance and context - inst[0] = inst[i]; - inst[0].length = 1; - inst.length = 1; - inst.context = [ inst.context[i] ]; - - return inst; - } - } - - // Not found - return an empty instance - inst.length = 0; - return inst; - }; - - - var _selector_row_indexes = function ( settings, opts ) - { - var - i, ien, tmp, a=[], - displayFiltered = settings.aiDisplay, - displayMaster = settings.aiDisplayMaster; - - var - search = opts.search, // none, applied, removed - order = opts.order, // applied, current, index (original - compatibility with 1.9) - page = opts.page; // all, current - - if ( _fnDataSource( settings ) == 'ssp' ) { - // In server-side processing mode, most options are irrelevant since - // rows not shown don't exist and the index order is the applied order - // Removed is a special case - for consistency just return an empty - // array - return search === 'removed' ? - [] : - _range( 0, displayMaster.length ); - } - else if ( page == 'current' ) { - // Current page implies that order=current and fitler=applied, since it is - // fairly senseless otherwise, regardless of what order and search actually - // are - for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i= 0 && search == 'applied') ) - { - a.push( i ); - } - } - } - } - - return a; - }; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Rows - * - * {} - no selector - use all available rows - * {integer} - row aoData index - * {node} - TR node - * {string} - jQuery selector to apply to the TR elements - * {array} - jQuery array of nodes, or simply an array of TR nodes - * - */ - - - var __row_selector = function ( settings, selector, opts ) - { - var run = function ( sel ) { - var selInt = _intVal( sel ); - var i, ien; - - // Short cut - selector is a number and no options provided (default is - // all records, so no need to check if the index is in there, since it - // must be - dev error if the index doesn't exist). - if ( selInt !== null && ! opts ) { - return [ selInt ]; - } - - var rows = _selector_row_indexes( settings, opts ); - - if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) { - // Selector - integer - return [ selInt ]; - } - else if ( ! sel ) { - // Selector - none - return rows; - } - - // Selector - function - if ( typeof sel === 'function' ) { - return $.map( rows, function (idx) { - var row = settings.aoData[ idx ]; - return sel( idx, row._aData, row.nTr ) ? idx : null; - } ); - } - - // Get nodes in the order from the `rows` array with null values removed - var nodes = _removeEmpty( - _pluck_order( settings.aoData, rows, 'nTr' ) - ); - - // Selector - node - if ( sel.nodeName ) { - if ( sel._DT_RowIndex !== undefined ) { - return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup - } - else if ( sel._DT_CellIndex ) { - return [ sel._DT_CellIndex.row ]; - } - else { - var host = $(sel).closest('*[data-dt-row]'); - return host.length ? - [ host.data('dt-row') ] : - []; - } - } - - // ID selector. Want to always be able to select rows by id, regardless - // of if the tr element has been created or not, so can't rely upon - // jQuery here - hence a custom implementation. This does not match - // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, - // but to select it using a CSS selector engine (like Sizzle or - // querySelect) it would need to need to be escaped for some characters. - // DataTables simplifies this for row selectors since you can select - // only a row. A # indicates an id any anything that follows is the id - - // unescaped. - if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { - // get row index from id - var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; - if ( rowObj !== undefined ) { - return [ rowObj.idx ]; - } - - // need to fall through to jQuery in case there is DOM id that - // matches - } - - // Selector - jQuery selector string, array of nodes or jQuery object/ - // As jQuery's .filter() allows jQuery objects to be passed in filter, - // it also allows arrays, so this will cope with all three options - return $(nodes) - .filter( sel ) - .map( function () { - return this._DT_RowIndex; - } ) - .toArray(); - }; - - return _selector_run( 'row', selector, run, settings, opts ); - }; - - - _api_register( 'rows()', function ( selector, opts ) { - // argument shifting - if ( selector === undefined ) { - selector = ''; - } - else if ( $.isPlainObject( selector ) ) { - opts = selector; - selector = ''; - } - - opts = _selector_opts( opts ); - - var inst = this.iterator( 'table', function ( settings ) { - return __row_selector( settings, selector, opts ); - }, 1 ); - - // Want argument shifting here and in __row_selector? - inst.selector.rows = selector; - inst.selector.opts = opts; - - return inst; - } ); - - _api_register( 'rows().nodes()', function () { - return this.iterator( 'row', function ( settings, row ) { - return settings.aoData[ row ].nTr || undefined; - }, 1 ); - } ); - - _api_register( 'rows().data()', function () { - return this.iterator( true, 'rows', function ( settings, rows ) { - return _pluck_order( settings.aoData, rows, '_aData' ); - }, 1 ); - } ); - - _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { - return this.iterator( 'row', function ( settings, row ) { - var r = settings.aoData[ row ]; - return type === 'search' ? r._aFilterData : r._aSortData; - }, 1 ); - } ); - - _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { - return this.iterator( 'row', function ( settings, row ) { - _fnInvalidate( settings, row, src ); - } ); - } ); - - _api_registerPlural( 'rows().indexes()', 'row().index()', function () { - return this.iterator( 'row', function ( settings, row ) { - return row; - }, 1 ); - } ); - - _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { - var a = []; - var context = this.context; - - // `iterator` will drop undefined values, but in this case we want them - for ( var i=0, ien=context.length ; i
    ` node is a DataTable table already or not. - * - * @param {node|jquery|string} table Table node, jQuery object or jQuery - * selector for the table to test. Note that if more than more than one - * table is passed on, only the first will be checked - * @returns {boolean} true the table given is a DataTable, or false otherwise - * @static - * @dtopt API-Static - * - * @example - * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { - * $('#example').dataTable(); - * } - */ - DataTable.isDataTable = DataTable.fnIsDataTable = function ( table ) - { - var t = $(table).get(0); - var is = false; - - $.each( DataTable.settings, function (i, o) { - var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; - var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; - - if ( o.nTable === t || head === t || foot === t ) { - is = true; - } - } ); - - return is; - }; - - - /** - * Get all DataTable tables that have been initialised - optionally you can - * select to get only currently visible tables. - * - * @param {boolean} [visible=false] Flag to indicate if you want all (default) - * or visible tables only. - * @returns {array} Array of `table` nodes (not DataTable instances) which are - * DataTables - * @static - * @dtopt API-Static - * - * @example - * $.each( $.fn.dataTable.tables(true), function () { - * $(table).DataTable().columns.adjust(); - * } ); - */ - DataTable.tables = DataTable.fnTables = function ( visible ) - { - var api = false; - - if ( $.isPlainObject( visible ) ) { - api = visible.api; - visible = visible.visible; - } - - var a = $.map( DataTable.settings, function (o) { - if ( !visible || (visible && $(o.nTable).is(':visible')) ) { - return o.nTable; - } - } ); - - return api ? - new _Api( a ) : - a; - }; - - - /** - * Convert from camel case parameters to Hungarian notation. This is made public - * for the extensions to provide the same ability as DataTables core to accept - * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase - * parameters. - * - * @param {object} src The model object which holds all parameters that can be - * mapped. - * @param {object} user The object to convert from camel case to Hungarian. - * @param {boolean} force When set to `true`, properties which already have a - * Hungarian value in the `user` object will be overwritten. Otherwise they - * won't be. - */ - DataTable.camelToHungarian = _fnCamelToHungarian; - - - - /** - * - */ - _api_register( '$()', function ( selector, opts ) { - var - rows = this.rows( opts ).nodes(), // Get all rows - jqRows = $(rows); - - return $( [].concat( - jqRows.filter( selector ).toArray(), - jqRows.find( selector ).toArray() - ) ); - } ); - - - // jQuery functions to operate on the tables - $.each( [ 'on', 'one', 'off' ], function (i, key) { - _api_register( key+'()', function ( /* event, handler */ ) { - var args = Array.prototype.slice.call(arguments); - - // Add the `dt` namespace automatically if it isn't already present - if ( ! args[0].match(/\.dt\b/) ) { - args[0] += '.dt'; - } - - var inst = $( this.tables().nodes() ); - inst[key].apply( inst, args ); - return this; - } ); - } ); - - - _api_register( 'clear()', function () { - return this.iterator( 'table', function ( settings ) { - _fnClearTable( settings ); - } ); - } ); - - - _api_register( 'settings()', function () { - return new _Api( this.context, this.context ); - } ); - - - _api_register( 'init()', function () { - var ctx = this.context; - return ctx.length ? ctx[0].oInit : null; - } ); - - - _api_register( 'data()', function () { - return this.iterator( 'table', function ( settings ) { - return _pluck( settings.aoData, '_aData' ); - } ).flatten(); - } ); - - - _api_register( 'destroy()', function ( remove ) { - remove = remove || false; - - return this.iterator( 'table', function ( settings ) { - var orig = settings.nTableWrapper.parentNode; - var classes = settings.oClasses; - var table = settings.nTable; - var tbody = settings.nTBody; - var thead = settings.nTHead; - var tfoot = settings.nTFoot; - var jqTable = $(table); - var jqTbody = $(tbody); - var jqWrapper = $(settings.nTableWrapper); - var rows = $.map( settings.aoData, function (r) { return r.nTr; } ); - var i, ien; - - // Flag to note that the table is currently being destroyed - no action - // should be taken - settings.bDestroying = true; - - // Fire off the destroy callbacks for plug-ins etc - _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] ); - - // If not being removed from the document, make all columns visible - if ( ! remove ) { - new _Api( settings ).columns().visible( true ); - } - - // Blitz all `DT` namespaced events (these are internal events, the - // lowercase, `dt` events are user subscribed and they are responsible - // for removing them - jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT'); - $(window).unbind('.DT-'+settings.sInstance); - - // When scrolling we had to break the table up - restore it - if ( table != thead.parentNode ) { - jqTable.children('thead').detach(); - jqTable.append( thead ); - } - - if ( tfoot && table != tfoot.parentNode ) { - jqTable.children('tfoot').detach(); - jqTable.append( tfoot ); - } - - settings.aaSorting = []; - settings.aaSortingFixed = []; - _fnSortingClasses( settings ); - - $( rows ).removeClass( settings.asStripeClasses.join(' ') ); - - $('th, td', thead).removeClass( classes.sSortable+' '+ - classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone - ); - - if ( settings.bJUI ) { - $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach(); - $('th, td', thead).each( function () { - var wrapper = $('div.'+classes.sSortJUIWrapper, this); - $(this).append( wrapper.contents() ); - wrapper.detach(); - } ); - } - - // Add the TR elements back into the table in their original order - jqTbody.children().detach(); - jqTbody.append( rows ); - - // Remove the DataTables generated nodes, events and classes - var removedMethod = remove ? 'remove' : 'detach'; - jqTable[ removedMethod ](); - jqWrapper[ removedMethod ](); - - // If we need to reattach the table to the document - if ( ! remove && orig ) { - // insertBefore acts like appendChild if !arg[1] - orig.insertBefore( table, settings.nTableReinsertBefore ); - - // Restore the width of the original table - was read from the style property, - // so we can restore directly to that - jqTable - .css( 'width', settings.sDestroyWidth ) - .removeClass( classes.sTable ); - - // If the were originally stripe classes - then we add them back here. - // Note this is not fool proof (for example if not all rows had stripe - // classes - but it's a good effort without getting carried away - ien = settings.asDestroyStripes.length; - - if ( ien ) { - jqTbody.children().each( function (i) { - $(this).addClass( settings.asDestroyStripes[i % ien] ); - } ); - } - } - - /* Remove the settings object from the settings array */ - var idx = $.inArray( settings, DataTable.settings ); - if ( idx !== -1 ) { - DataTable.settings.splice( idx, 1 ); - } - } ); - } ); - - - // Add the `every()` method for rows, columns and cells in a compact form - $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { - _api_register( type+'s().every()', function ( fn ) { - var opts = this.selector.opts; - var api = this; - - return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) { - // Rows and columns: - // arg1 - index - // arg2 - table counter - // arg3 - loop counter - // arg4 - undefined - // Cells: - // arg1 - row index - // arg2 - column index - // arg3 - table counter - // arg4 - loop counter - fn.call( - api[ type ]( - arg1, - type==='cell' ? arg2 : opts, - type==='cell' ? opts : undefined - ), - arg1, arg2, arg3, arg4 - ); - } ); - } ); - } ); - - - // i18n method for extensions to be able to use the language object from the - // DataTable - _api_register( 'i18n()', function ( token, def, plural ) { - var ctx = this.context[0]; - var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); - - if ( resolved === undefined ) { - resolved = def; - } - - if ( plural !== undefined && $.isPlainObject( resolved ) ) { - resolved = resolved[ plural ] !== undefined ? - resolved[ plural ] : - resolved._; - } - - return resolved.replace( '%d', plural ); // nb: plural might be undefined, - } ); - /** - * Version string for plug-ins to check compatibility. Allowed format is - * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used - * only for non-release builds. See http://semver.org/ for more information. - * @member - * @type string - * @default Version number - */ - DataTable.version = "1.10.12"; - - /** - * Private data store, containing all of the settings objects that are - * created for the tables on a given page. - * - * Note that the `DataTable.settings` object is aliased to - * `jQuery.fn.dataTableExt` through which it may be accessed and - * manipulated, or `jQuery.fn.dataTable.settings`. - * @member - * @type array - * @default [] - * @private - */ - DataTable.settings = []; - - /** - * Object models container, for the various models that DataTables has - * available to it. These models define the objects that are used to hold - * the active state and configuration of the table. - * @namespace - */ - DataTable.models = {}; - - - - /** - * Template object for the way in which DataTables holds information about - * search information for the global filter and individual column filters. - * @namespace - */ - DataTable.models.oSearch = { - /** - * Flag to indicate if the filtering should be case insensitive or not - * @type boolean - * @default true - */ - "bCaseInsensitive": true, - - /** - * Applied search term - * @type string - * @default Empty string - */ - "sSearch": "", - - /** - * Flag to indicate if the search term should be interpreted as a - * regular expression (true) or not (false) and therefore and special - * regex characters escaped. - * @type boolean - * @default false - */ - "bRegex": false, - - /** - * Flag to indicate if DataTables is to use its smart filtering or not. - * @type boolean - * @default true - */ - "bSmart": true - }; - - - - - /** - * Template object for the way in which DataTables holds information about - * each individual row. This is the object format used for the settings - * aoData array. - * @namespace - */ - DataTable.models.oRow = { - /** - * TR element for the row - * @type node - * @default null - */ - "nTr": null, - - /** - * Array of TD elements for each row. This is null until the row has been - * created. - * @type array nodes - * @default [] - */ - "anCells": null, - - /** - * Data object from the original data source for the row. This is either - * an array if using the traditional form of DataTables, or an object if - * using mData options. The exact type will depend on the passed in - * data from the data source, or will be an array if using DOM a data - * source. - * @type array|object - * @default [] - */ - "_aData": [], - - /** - * Sorting data cache - this array is ostensibly the same length as the - * number of columns (although each index is generated only as it is - * needed), and holds the data that is used for sorting each column in the - * row. We do this cache generation at the start of the sort in order that - * the formatting of the sort data need be done only once for each cell - * per sort. This array should not be read from or written to by anything - * other than the master sorting methods. - * @type array - * @default null - * @private - */ - "_aSortData": null, - - /** - * Per cell filtering data cache. As per the sort data cache, used to - * increase the performance of the filtering in DataTables - * @type array - * @default null - * @private - */ - "_aFilterData": null, - - /** - * Filtering data cache. This is the same as the cell filtering cache, but - * in this case a string rather than an array. This is easily computed with - * a join on `_aFilterData`, but is provided as a cache so the join isn't - * needed on every search (memory traded for performance) - * @type array - * @default null - * @private - */ - "_sFilterRow": null, - - /** - * Cache of the class name that DataTables has applied to the row, so we - * can quickly look at this variable rather than needing to do a DOM check - * on className for the nTr property. - * @type string - * @default Empty string - * @private - */ - "_sRowStripe": "", - - /** - * Denote if the original data source was from the DOM, or the data source - * object. This is used for invalidating data, so DataTables can - * automatically read data from the original source, unless uninstructed - * otherwise. - * @type string - * @default null - * @private - */ - "src": null, - - /** - * Index in the aoData array. This saves an indexOf lookup when we have the - * object, but want to know the index - * @type integer - * @default -1 - * @private - */ - "idx": -1 - }; - - - /** - * Template object for the column information object in DataTables. This object - * is held in the settings aoColumns array and contains all the information that - * DataTables needs about each individual column. - * - * Note that this object is related to {@link DataTable.defaults.column} - * but this one is the internal data store for DataTables's cache of columns. - * It should NOT be manipulated outside of DataTables. Any configuration should - * be done through the initialisation options. - * @namespace - */ - DataTable.models.oColumn = { - /** - * Column index. This could be worked out on-the-fly with $.inArray, but it - * is faster to just hold it as a variable - * @type integer - * @default null - */ - "idx": null, - - /** - * A list of the columns that sorting should occur on when this column - * is sorted. That this property is an array allows multi-column sorting - * to be defined for a column (for example first name / last name columns - * would benefit from this). The values are integers pointing to the - * columns to be sorted on (typically it will be a single integer pointing - * at itself, but that doesn't need to be the case). - * @type array - */ - "aDataSort": null, - - /** - * Define the sorting directions that are applied to the column, in sequence - * as the column is repeatedly sorted upon - i.e. the first value is used - * as the sorting direction when the column if first sorted (clicked on). - * Sort it again (click again) and it will move on to the next index. - * Repeat until loop. - * @type array - */ - "asSorting": null, - - /** - * Flag to indicate if the column is searchable, and thus should be included - * in the filtering or not. - * @type boolean - */ - "bSearchable": null, - - /** - * Flag to indicate if the column is sortable or not. - * @type boolean - */ - "bSortable": null, - - /** - * Flag to indicate if the column is currently visible in the table or not - * @type boolean - */ - "bVisible": null, - - /** - * Store for manual type assignment using the `column.type` option. This - * is held in store so we can manipulate the column's `sType` property. - * @type string - * @default null - * @private - */ - "_sManualType": null, - - /** - * Flag to indicate if HTML5 data attributes should be used as the data - * source for filtering or sorting. True is either are. - * @type boolean - * @default false - * @private - */ - "_bAttrSrc": false, - - /** - * Developer definable function that is called whenever a cell is created (Ajax source, - * etc) or processed for input (DOM source). This can be used as a compliment to mRender - * allowing you to modify the DOM element (add background colour for example) when the - * element is available. - * @type function - * @param {element} nTd The TD node that has been created - * @param {*} sData The Data for the cell - * @param {array|object} oData The data for the whole row - * @param {int} iRow The row index for the aoData data store - * @default null - */ - "fnCreatedCell": null, - - /** - * Function to get data from a cell in a column. You should never - * access data directly through _aData internally in DataTables - always use - * the method attached to this property. It allows mData to function as - * required. This function is automatically assigned by the column - * initialisation method - * @type function - * @param {array|object} oData The data array/object for the array - * (i.e. aoData[]._aData) - * @param {string} sSpecific The specific data type you want to get - - * 'display', 'type' 'filter' 'sort' - * @returns {*} The data for the cell from the given row's data - * @default null - */ - "fnGetData": null, - - /** - * Function to set data for a cell in the column. You should never - * set the data directly to _aData internally in DataTables - always use - * this method. It allows mData to function as required. This function - * is automatically assigned by the column initialisation method - * @type function - * @param {array|object} oData The data array/object for the array - * (i.e. aoData[]._aData) - * @param {*} sValue Value to set - * @default null - */ - "fnSetData": null, - - /** - * Property to read the value for the cells in the column from the data - * source array / object. If null, then the default content is used, if a - * function is given then the return from the function is used. - * @type function|int|string|null - * @default null - */ - "mData": null, - - /** - * Partner property to mData which is used (only when defined) to get - * the data - i.e. it is basically the same as mData, but without the - * 'set' option, and also the data fed to it is the result from mData. - * This is the rendering method to match the data method of mData. - * @type function|int|string|null - * @default null - */ - "mRender": null, - - /** - * Unique header TH/TD element for this column - this is what the sorting - * listener is attached to (if sorting is enabled.) - * @type node - * @default null - */ - "nTh": null, - - /** - * Unique footer TH/TD element for this column (if there is one). Not used - * in DataTables as such, but can be used for plug-ins to reference the - * footer for each column. - * @type node - * @default null - */ - "nTf": null, - - /** - * The class to apply to all TD elements in the table's TBODY for the column - * @type string - * @default null - */ - "sClass": null, - - /** - * When DataTables calculates the column widths to assign to each column, - * it finds the longest string in each column and then constructs a - * temporary table and reads the widths from that. The problem with this - * is that "mmm" is much wider then "iiii", but the latter is a longer - * string - thus the calculation can go wrong (doing it properly and putting - * it into an DOM object and measuring that is horribly(!) slow). Thus as - * a "work around" we provide this option. It will append its value to the - * text that is found to be the longest string for the column - i.e. padding. - * @type string - */ - "sContentPadding": null, - - /** - * Allows a default value to be given for a column's data, and will be used - * whenever a null data source is encountered (this can be because mData - * is set to null, or because the data source itself is null). - * @type string - * @default null - */ - "sDefaultContent": null, - - /** - * Name for the column, allowing reference to the column by name as well as - * by index (needs a lookup to work by name). - * @type string - */ - "sName": null, - - /** - * Custom sorting data type - defines which of the available plug-ins in - * afnSortData the custom sorting will use - if any is defined. - * @type string - * @default std - */ - "sSortDataType": 'std', - - /** - * Class to be applied to the header element when sorting on this column - * @type string - * @default null - */ - "sSortingClass": null, - - /** - * Class to be applied to the header element when sorting on this column - - * when jQuery UI theming is used. - * @type string - * @default null - */ - "sSortingClassJUI": null, - - /** - * Title of the column - what is seen in the TH element (nTh). - * @type string - */ - "sTitle": null, - - /** - * Column sorting and filtering type - * @type string - * @default null - */ - "sType": null, - - /** - * Width of the column - * @type string - * @default null - */ - "sWidth": null, - - /** - * Width of the column when it was first "encountered" - * @type string - * @default null - */ - "sWidthOrig": null - }; - - - /* - * Developer note: The properties of the object below are given in Hungarian - * notation, that was used as the interface for DataTables prior to v1.10, however - * from v1.10 onwards the primary interface is camel case. In order to avoid - * breaking backwards compatibility utterly with this change, the Hungarian - * version is still, internally the primary interface, but is is not documented - * - hence the @name tags in each doc comment. This allows a Javascript function - * to create a map from Hungarian notation to camel case (going the other direction - * would require each property to be listed, which would at around 3K to the size - * of DataTables, while this method is about a 0.5K hit. - * - * Ultimately this does pave the way for Hungarian notation to be dropped - * completely, but that is a massive amount of work and will break current - * installs (therefore is on-hold until v2). - */ - - /** - * Initialisation options that can be given to DataTables at initialisation - * time. - * @namespace - */ - DataTable.defaults = { - /** - * An array of data to use for the table, passed in at initialisation which - * will be used in preference to any data which is already in the DOM. This is - * particularly useful for constructing tables purely in Javascript, for - * example with a custom Ajax call. - * @type array - * @default null - * - * @dtopt Option - * @name DataTable.defaults.data - * - * @example - * // Using a 2D array data source - * $(document).ready( function () { - * $('#example').dataTable( { - * "data": [ - * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'], - * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'], - * ], - * "columns": [ - * { "title": "Engine" }, - * { "title": "Browser" }, - * { "title": "Platform" }, - * { "title": "Version" }, - * { "title": "Grade" } - * ] - * } ); - * } ); - * - * @example - * // Using an array of objects as a data source (`data`) - * $(document).ready( function () { - * $('#example').dataTable( { - * "data": [ - * { - * "engine": "Trident", - * "browser": "Internet Explorer 4.0", - * "platform": "Win 95+", - * "version": 4, - * "grade": "X" - * }, - * { - * "engine": "Trident", - * "browser": "Internet Explorer 5.0", - * "platform": "Win 95+", - * "version": 5, - * "grade": "C" - * } - * ], - * "columns": [ - * { "title": "Engine", "data": "engine" }, - * { "title": "Browser", "data": "browser" }, - * { "title": "Platform", "data": "platform" }, - * { "title": "Version", "data": "version" }, - * { "title": "Grade", "data": "grade" } - * ] - * } ); - * } ); - */ - "aaData": null, - - - /** - * If ordering is enabled, then DataTables will perform a first pass sort on - * initialisation. You can define which column(s) the sort is performed - * upon, and the sorting direction, with this variable. The `sorting` array - * should contain an array for each column to be sorted initially containing - * the column's index and a direction string ('asc' or 'desc'). - * @type array - * @default [[0,'asc']] - * - * @dtopt Option - * @name DataTable.defaults.order - * - * @example - * // Sort by 3rd column first, and then 4th column - * $(document).ready( function() { - * $('#example').dataTable( { - * "order": [[2,'asc'], [3,'desc']] - * } ); - * } ); - * - * // No initial sorting - * $(document).ready( function() { - * $('#example').dataTable( { - * "order": [] - * } ); - * } ); - */ - "aaSorting": [[0,'asc']], - - - /** - * This parameter is basically identical to the `sorting` parameter, but - * cannot be overridden by user interaction with the table. What this means - * is that you could have a column (visible or hidden) which the sorting - * will always be forced on first - any sorting after that (from the user) - * will then be performed as required. This can be useful for grouping rows - * together. - * @type array - * @default null - * - * @dtopt Option - * @name DataTable.defaults.orderFixed - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "orderFixed": [[0,'asc']] - * } ); - * } ) - */ - "aaSortingFixed": [], - - - /** - * DataTables can be instructed to load data to display in the table from a - * Ajax source. This option defines how that Ajax call is made and where to. - * - * The `ajax` property has three different modes of operation, depending on - * how it is defined. These are: - * - * * `string` - Set the URL from where the data should be loaded from. - * * `object` - Define properties for `jQuery.ajax`. - * * `function` - Custom data get function - * - * `string` - * -------- - * - * As a string, the `ajax` property simply defines the URL from which - * DataTables will load data. - * - * `object` - * -------- - * - * As an object, the parameters in the object are passed to - * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control - * of the Ajax request. DataTables has a number of default parameters which - * you can override using this option. Please refer to the jQuery - * documentation for a full description of the options available, although - * the following parameters provide additional options in DataTables or - * require special consideration: - * - * * `data` - As with jQuery, `data` can be provided as an object, but it - * can also be used as a function to manipulate the data DataTables sends - * to the server. The function takes a single parameter, an object of - * parameters with the values that DataTables has readied for sending. An - * object may be returned which will be merged into the DataTables - * defaults, or you can add the items to the object that was passed in and - * not return anything from the function. This supersedes `fnServerParams` - * from DataTables 1.9-. - * - * * `dataSrc` - By default DataTables will look for the property `data` (or - * `aaData` for compatibility with DataTables 1.9-) when obtaining data - * from an Ajax source or for server-side processing - this parameter - * allows that property to be changed. You can use Javascript dotted - * object notation to get a data source for multiple levels of nesting, or - * it my be used as a function. As a function it takes a single parameter, - * the JSON returned from the server, which can be manipulated as - * required, with the returned value being that used by DataTables as the - * data source for the table. This supersedes `sAjaxDataProp` from - * DataTables 1.9-. - * - * * `success` - Should not be overridden it is used internally in - * DataTables. To manipulate / transform the data returned by the server - * use `ajax.dataSrc`, or use `ajax` as a function (see below). - * - * `function` - * ---------- - * - * As a function, making the Ajax call is left up to yourself allowing - * complete control of the Ajax request. Indeed, if desired, a method other - * than Ajax could be used to obtain the required data, such as Web storage - * or an AIR database. - * - * The function is given four parameters and no return is required. The - * parameters are: - * - * 1. _object_ - Data to send to the server - * 2. _function_ - Callback function that must be executed when the required - * data has been obtained. That data should be passed into the callback - * as the only parameter - * 3. _object_ - DataTables settings object for the table - * - * Note that this supersedes `fnServerData` from DataTables 1.9-. - * - * @type string|object|function - * @default null - * - * @dtopt Option - * @name DataTable.defaults.ajax - * @since 1.10.0 - * - * @example - * // Get JSON data from a file via Ajax. - * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default). - * $('#example').dataTable( { - * "ajax": "data.json" - * } ); - * - * @example - * // Get JSON data from a file via Ajax, using `dataSrc` to change - * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`) - * $('#example').dataTable( { - * "ajax": { - * "url": "data.json", - * "dataSrc": "tableData" - * } - * } ); - * - * @example - * // Get JSON data from a file via Ajax, using `dataSrc` to read data - * // from a plain array rather than an array in an object - * $('#example').dataTable( { - * "ajax": { - * "url": "data.json", - * "dataSrc": "" - * } - * } ); - * - * @example - * // Manipulate the data returned from the server - add a link to data - * // (note this can, should, be done using `render` for the column - this - * // is just a simple example of how the data can be manipulated). - * $('#example').dataTable( { - * "ajax": { - * "url": "data.json", - * "dataSrc": function ( json ) { - * for ( var i=0, ien=json.length ; iView message'; - * } - * return json; - * } - * } - * } ); - * - * @example - * // Add data to the request - * $('#example').dataTable( { - * "ajax": { - * "url": "data.json", - * "data": function ( d ) { - * return { - * "extra_search": $('#extra').val() - * }; - * } - * } - * } ); - * - * @example - * // Send request as POST - * $('#example').dataTable( { - * "ajax": { - * "url": "data.json", - * "type": "POST" - * } - * } ); - * - * @example - * // Get the data from localStorage (could interface with a form for - * // adding, editing and removing rows). - * $('#example').dataTable( { - * "ajax": function (data, callback, settings) { - * callback( - * JSON.parse( localStorage.getItem('dataTablesData') ) - * ); - * } - * } ); - */ - "ajax": null, - - - /** - * This parameter allows you to readily specify the entries in the length drop - * down menu that DataTables shows when pagination is enabled. It can be - * either a 1D array of options which will be used for both the displayed - * option and the value, or a 2D array which will use the array in the first - * position as the value, and the array in the second position as the - * displayed options (useful for language strings such as 'All'). - * - * Note that the `pageLength` property will be automatically set to the - * first value given in this array, unless `pageLength` is also provided. - * @type array - * @default [ 10, 25, 50, 100 ] - * - * @dtopt Option - * @name DataTable.defaults.lengthMenu - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]] - * } ); - * } ); - */ - "aLengthMenu": [ 10, 25, 50, 100 ], - - - /** - * The `columns` option in the initialisation parameter allows you to define - * details about the way individual columns behave. For a full list of - * column options that can be set, please see - * {@link DataTable.defaults.column}. Note that if you use `columns` to - * define your columns, you must have an entry in the array for every single - * column that you have in your table (these can be null if you don't which - * to specify any options). - * @member - * - * @name DataTable.defaults.column - */ - "aoColumns": null, - - /** - * Very similar to `columns`, `columnDefs` allows you to target a specific - * column, multiple columns, or all columns, using the `targets` property of - * each object in the array. This allows great flexibility when creating - * tables, as the `columnDefs` arrays can be of any length, targeting the - * columns you specifically want. `columnDefs` may use any of the column - * options available: {@link DataTable.defaults.column}, but it _must_ - * have `targets` defined in each object in the array. Values in the `targets` - * array may be: - *
      - *
    • a string - class name will be matched on the TH for the column
    • - *
    • 0 or a positive integer - column index counting from the left
    • - *
    • a negative integer - column index counting from the right
    • - *
    • the string "_all" - all columns (i.e. assign a default)
    • - *
    - * @member - * - * @name DataTable.defaults.columnDefs - */ - "aoColumnDefs": null, - - - /** - * Basically the same as `search`, this parameter defines the individual column - * filtering state at initialisation time. The array must be of the same size - * as the number of columns, and each element be an object with the parameters - * `search` and `escapeRegex` (the latter is optional). 'null' is also - * accepted and the default will be used. - * @type array - * @default [] - * - * @dtopt Option - * @name DataTable.defaults.searchCols - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "searchCols": [ - * null, - * { "search": "My filter" }, - * null, - * { "search": "^[0-9]", "escapeRegex": false } - * ] - * } ); - * } ) - */ - "aoSearchCols": [], - - - /** - * An array of CSS classes that should be applied to displayed rows. This - * array may be of any length, and DataTables will apply each class - * sequentially, looping when required. - * @type array - * @default null Will take the values determined by the `oClasses.stripe*` - * options - * - * @dtopt Option - * @name DataTable.defaults.stripeClasses - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ] - * } ); - * } ) - */ - "asStripeClasses": null, - - - /** - * Enable or disable automatic column width calculation. This can be disabled - * as an optimisation (it takes some time to calculate the widths) if the - * tables widths are passed in using `columns`. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.autoWidth - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "autoWidth": false - * } ); - * } ); - */ - "bAutoWidth": true, - - - /** - * Deferred rendering can provide DataTables with a huge speed boost when you - * are using an Ajax or JS data source for the table. This option, when set to - * true, will cause DataTables to defer the creation of the table elements for - * each row until they are needed for a draw - saving a significant amount of - * time. - * @type boolean - * @default false - * - * @dtopt Features - * @name DataTable.defaults.deferRender - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "ajax": "sources/arrays.txt", - * "deferRender": true - * } ); - * } ); - */ - "bDeferRender": false, - - - /** - * Replace a DataTable which matches the given selector and replace it with - * one which has the properties of the new initialisation object passed. If no - * table matches the selector, then the new DataTable will be constructed as - * per normal. - * @type boolean - * @default false - * - * @dtopt Options - * @name DataTable.defaults.destroy - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "srollY": "200px", - * "paginate": false - * } ); - * - * // Some time later.... - * $('#example').dataTable( { - * "filter": false, - * "destroy": true - * } ); - * } ); - */ - "bDestroy": false, - - - /** - * Enable or disable filtering of data. Filtering in DataTables is "smart" in - * that it allows the end user to input multiple words (space separated) and - * will match a row containing those words, even if not in the order that was - * specified (this allow matching across multiple columns). Note that if you - * wish to use filtering in DataTables this must remain 'true' - to remove the - * default filtering input box and retain filtering abilities, please use - * {@link DataTable.defaults.dom}. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.searching - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "searching": false - * } ); - * } ); - */ - "bFilter": true, - - - /** - * Enable or disable the table information display. This shows information - * about the data that is currently visible on the page, including information - * about filtered data if that action is being performed. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.info - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "info": false - * } ); - * } ); - */ - "bInfo": true, - - - /** - * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some - * slightly different and additional mark-up from what DataTables has - * traditionally used). - * @type boolean - * @default false - * - * @dtopt Features - * @name DataTable.defaults.jQueryUI - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "jQueryUI": true - * } ); - * } ); - */ - "bJQueryUI": false, - - - /** - * Allows the end user to select the size of a formatted page from a select - * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`). - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.lengthChange - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "lengthChange": false - * } ); - * } ); - */ - "bLengthChange": true, - - - /** - * Enable or disable pagination. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.paging - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "paging": false - * } ); - * } ); - */ - "bPaginate": true, - - - /** - * Enable or disable the display of a 'processing' indicator when the table is - * being processed (e.g. a sort). This is particularly useful for tables with - * large amounts of data where it can take a noticeable amount of time to sort - * the entries. - * @type boolean - * @default false - * - * @dtopt Features - * @name DataTable.defaults.processing - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "processing": true - * } ); - * } ); - */ - "bProcessing": false, - - - /** - * Retrieve the DataTables object for the given selector. Note that if the - * table has already been initialised, this parameter will cause DataTables - * to simply return the object that has already been set up - it will not take - * account of any changes you might have made to the initialisation object - * passed to DataTables (setting this parameter to true is an acknowledgement - * that you understand this). `destroy` can be used to reinitialise a table if - * you need. - * @type boolean - * @default false - * - * @dtopt Options - * @name DataTable.defaults.retrieve - * - * @example - * $(document).ready( function() { - * initTable(); - * tableActions(); - * } ); - * - * function initTable () - * { - * return $('#example').dataTable( { - * "scrollY": "200px", - * "paginate": false, - * "retrieve": true - * } ); - * } - * - * function tableActions () - * { - * var table = initTable(); - * // perform API operations with oTable - * } - */ - "bRetrieve": false, - - - /** - * When vertical (y) scrolling is enabled, DataTables will force the height of - * the table's viewport to the given height at all times (useful for layout). - * However, this can look odd when filtering data down to a small data set, - * and the footer is left "floating" further down. This parameter (when - * enabled) will cause DataTables to collapse the table's viewport down when - * the result set will fit within the given Y height. - * @type boolean - * @default false - * - * @dtopt Options - * @name DataTable.defaults.scrollCollapse - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "scrollY": "200", - * "scrollCollapse": true - * } ); - * } ); - */ - "bScrollCollapse": false, - - - /** - * Configure DataTables to use server-side processing. Note that the - * `ajax` parameter must also be given in order to give DataTables a - * source to obtain the required data for each draw. - * @type boolean - * @default false - * - * @dtopt Features - * @dtopt Server-side - * @name DataTable.defaults.serverSide - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "serverSide": true, - * "ajax": "xhr.php" - * } ); - * } ); - */ - "bServerSide": false, - - - /** - * Enable or disable sorting of columns. Sorting of individual columns can be - * disabled by the `sortable` option for each column. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.ordering - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "ordering": false - * } ); - * } ); - */ - "bSort": true, - - - /** - * Enable or display DataTables' ability to sort multiple columns at the - * same time (activated by shift-click by the user). - * @type boolean - * @default true - * - * @dtopt Options - * @name DataTable.defaults.orderMulti - * - * @example - * // Disable multiple column sorting ability - * $(document).ready( function () { - * $('#example').dataTable( { - * "orderMulti": false - * } ); - * } ); - */ - "bSortMulti": true, - - - /** - * Allows control over whether DataTables should use the top (true) unique - * cell that is found for a single column, or the bottom (false - default). - * This is useful when using complex headers. - * @type boolean - * @default false - * - * @dtopt Options - * @name DataTable.defaults.orderCellsTop - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "orderCellsTop": true - * } ); - * } ); - */ - "bSortCellsTop": false, - - - /** - * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and - * `sorting\_3` to the columns which are currently being sorted on. This is - * presented as a feature switch as it can increase processing time (while - * classes are removed and added) so for large data sets you might want to - * turn this off. - * @type boolean - * @default true - * - * @dtopt Features - * @name DataTable.defaults.orderClasses - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "orderClasses": false - * } ); - * } ); - */ - "bSortClasses": true, - - - /** - * Enable or disable state saving. When enabled HTML5 `localStorage` will be - * used to save table display information such as pagination information, - * display length, filtering and sorting. As such when the end user reloads - * the page the display display will match what thy had previously set up. - * - * Due to the use of `localStorage` the default state saving is not supported - * in IE6 or 7. If state saving is required in those browsers, use - * `stateSaveCallback` to provide a storage solution such as cookies. - * @type boolean - * @default false - * - * @dtopt Features - * @name DataTable.defaults.stateSave - * - * @example - * $(document).ready( function () { - * $('#example').dataTable( { - * "stateSave": true - * } ); - * } ); - */ - "bStateSave": false, - - - /** - * This function is called when a TR element is created (and all TD child - * elements have been inserted), or registered if using a DOM source, allowing - * manipulation of the TR element (adding classes etc). - * @type function - * @param {node} row "TR" element for the current row - * @param {array} data Raw data array for this row - * @param {int} dataIndex The index of this row in the internal aoData array - * - * @dtopt Callbacks - * @name DataTable.defaults.createdRow - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "createdRow": function( row, data, dataIndex ) { - * // Bold the grade for all 'A' grade browsers - * if ( data[4] == "A" ) - * { - * $('td:eq(4)', row).html( 'A' ); - * } - * } - * } ); - * } ); - */ - "fnCreatedRow": null, - - - /** - * This function is called on every 'draw' event, and allows you to - * dynamically modify any aspect you want about the created DOM. - * @type function - * @param {object} settings DataTables settings object - * - * @dtopt Callbacks - * @name DataTable.defaults.drawCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "drawCallback": function( settings ) { - * alert( 'DataTables has redrawn the table' ); - * } - * } ); - * } ); - */ - "fnDrawCallback": null, - - - /** - * Identical to fnHeaderCallback() but for the table footer this function - * allows you to modify the table footer on every 'draw' event. - * @type function - * @param {node} foot "TR" element for the footer - * @param {array} data Full table data (as derived from the original HTML) - * @param {int} start Index for the current display starting point in the - * display array - * @param {int} end Index for the current display ending point in the - * display array - * @param {array int} display Index array to translate the visual position - * to the full data array - * - * @dtopt Callbacks - * @name DataTable.defaults.footerCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "footerCallback": function( tfoot, data, start, end, display ) { - * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start; - * } - * } ); - * } ) - */ - "fnFooterCallback": null, - - - /** - * When rendering large numbers in the information element for the table - * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers - * to have a comma separator for the 'thousands' units (e.g. 1 million is - * rendered as "1,000,000") to help readability for the end user. This - * function will override the default method DataTables uses. - * @type function - * @member - * @param {int} toFormat number to be formatted - * @returns {string} formatted string for DataTables to show the number - * - * @dtopt Callbacks - * @name DataTable.defaults.formatNumber - * - * @example - * // Format a number using a single quote for the separator (note that - * // this can also be done with the language.thousands option) - * $(document).ready( function() { - * $('#example').dataTable( { - * "formatNumber": function ( toFormat ) { - * return toFormat.toString().replace( - * /\B(?=(\d{3})+(?!\d))/g, "'" - * ); - * }; - * } ); - * } ); - */ - "fnFormatNumber": function ( toFormat ) { - return toFormat.toString().replace( - /\B(?=(\d{3})+(?!\d))/g, - this.oLanguage.sThousands - ); - }, - - - /** - * This function is called on every 'draw' event, and allows you to - * dynamically modify the header row. This can be used to calculate and - * display useful information about the table. - * @type function - * @param {node} head "TR" element for the header - * @param {array} data Full table data (as derived from the original HTML) - * @param {int} start Index for the current display starting point in the - * display array - * @param {int} end Index for the current display ending point in the - * display array - * @param {array int} display Index array to translate the visual position - * to the full data array - * - * @dtopt Callbacks - * @name DataTable.defaults.headerCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "fheaderCallback": function( head, data, start, end, display ) { - * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records"; - * } - * } ); - * } ) - */ - "fnHeaderCallback": null, - - - /** - * The information element can be used to convey information about the current - * state of the table. Although the internationalisation options presented by - * DataTables are quite capable of dealing with most customisations, there may - * be times where you wish to customise the string further. This callback - * allows you to do exactly that. - * @type function - * @param {object} oSettings DataTables settings object - * @param {int} start Starting position in data for the draw - * @param {int} end End position in data for the draw - * @param {int} max Total number of rows in the table (regardless of - * filtering) - * @param {int} total Total number of rows in the data set, after filtering - * @param {string} pre The string that DataTables has formatted using it's - * own rules - * @returns {string} The string to be displayed in the information element. - * - * @dtopt Callbacks - * @name DataTable.defaults.infoCallback - * - * @example - * $('#example').dataTable( { - * "infoCallback": function( settings, start, end, max, total, pre ) { - * return start +" to "+ end; - * } - * } ); - */ - "fnInfoCallback": null, - - - /** - * Called when the table has been initialised. Normally DataTables will - * initialise sequentially and there will be no need for this function, - * however, this does not hold true when using external language information - * since that is obtained using an async XHR call. - * @type function - * @param {object} settings DataTables settings object - * @param {object} json The JSON object request from the server - only - * present if client-side Ajax sourced data is used - * - * @dtopt Callbacks - * @name DataTable.defaults.initComplete - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "initComplete": function(settings, json) { - * alert( 'DataTables has finished its initialisation.' ); - * } - * } ); - * } ) - */ - "fnInitComplete": null, - - - /** - * Called at the very start of each table draw and can be used to cancel the - * draw by returning false, any other return (including undefined) results in - * the full draw occurring). - * @type function - * @param {object} settings DataTables settings object - * @returns {boolean} False will cancel the draw, anything else (including no - * return) will allow it to complete. - * - * @dtopt Callbacks - * @name DataTable.defaults.preDrawCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "preDrawCallback": function( settings ) { - * if ( $('#test').val() == 1 ) { - * return false; - * } - * } - * } ); - * } ); - */ - "fnPreDrawCallback": null, - - - /** - * This function allows you to 'post process' each row after it have been - * generated for each table draw, but before it is rendered on screen. This - * function might be used for setting the row class name etc. - * @type function - * @param {node} row "TR" element for the current row - * @param {array} data Raw data array for this row - * @param {int} displayIndex The display index for the current table draw - * @param {int} displayIndexFull The index of the data in the full list of - * rows (after filtering) - * - * @dtopt Callbacks - * @name DataTable.defaults.rowCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "rowCallback": function( row, data, displayIndex, displayIndexFull ) { - * // Bold the grade for all 'A' grade browsers - * if ( data[4] == "A" ) { - * $('td:eq(4)', row).html( 'A' ); - * } - * } - * } ); - * } ); - */ - "fnRowCallback": null, - - - /** - * __Deprecated__ The functionality provided by this parameter has now been - * superseded by that provided through `ajax`, which should be used instead. - * - * This parameter allows you to override the default function which obtains - * the data from the server so something more suitable for your application. - * For example you could use POST data, or pull information from a Gears or - * AIR database. - * @type function - * @member - * @param {string} source HTTP source to obtain the data from (`ajax`) - * @param {array} data A key/value pair object containing the data to send - * to the server - * @param {function} callback to be called on completion of the data get - * process that will draw the data on the page. - * @param {object} settings DataTables settings object - * - * @dtopt Callbacks - * @dtopt Server-side - * @name DataTable.defaults.serverData - * - * @deprecated 1.10. Please use `ajax` for this functionality now. - */ - "fnServerData": null, - - - /** - * __Deprecated__ The functionality provided by this parameter has now been - * superseded by that provided through `ajax`, which should be used instead. - * - * It is often useful to send extra data to the server when making an Ajax - * request - for example custom filtering information, and this callback - * function makes it trivial to send extra information to the server. The - * passed in parameter is the data set that has been constructed by - * DataTables, and you can add to this or modify it as you require. - * @type function - * @param {array} data Data array (array of objects which are name/value - * pairs) that has been constructed by DataTables and will be sent to the - * server. In the case of Ajax sourced data with server-side processing - * this will be an empty array, for server-side processing there will be a - * significant number of parameters! - * @returns {undefined} Ensure that you modify the data array passed in, - * as this is passed by reference. - * - * @dtopt Callbacks - * @dtopt Server-side - * @name DataTable.defaults.serverParams - * - * @deprecated 1.10. Please use `ajax` for this functionality now. - */ - "fnServerParams": null, - - - /** - * Load the table state. With this function you can define from where, and how, the - * state of a table is loaded. By default DataTables will load from `localStorage` - * but you might wish to use a server-side database or cookies. - * @type function - * @member - * @param {object} settings DataTables settings object - * @return {object} The DataTables state object to be loaded - * - * @dtopt Callbacks - * @name DataTable.defaults.stateLoadCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateLoadCallback": function (settings) { - * var o; - * - * // Send an Ajax request to the server to get the data. Note that - * // this is a synchronous request. - * $.ajax( { - * "url": "/state_load", - * "async": false, - * "dataType": "json", - * "success": function (json) { - * o = json; - * } - * } ); - * - * return o; - * } - * } ); - * } ); - */ - "fnStateLoadCallback": function ( settings ) { - try { - return JSON.parse( - (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( - 'DataTables_'+settings.sInstance+'_'+location.pathname - ) - ); - } catch (e) {} - }, - - - /** - * Callback which allows modification of the saved state prior to loading that state. - * This callback is called when the table is loading state from the stored data, but - * prior to the settings object being modified by the saved state. Note that for - * plug-in authors, you should use the `stateLoadParams` event to load parameters for - * a plug-in. - * @type function - * @param {object} settings DataTables settings object - * @param {object} data The state object that is to be loaded - * - * @dtopt Callbacks - * @name DataTable.defaults.stateLoadParams - * - * @example - * // Remove a saved filter, so filtering is never loaded - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateLoadParams": function (settings, data) { - * data.oSearch.sSearch = ""; - * } - * } ); - * } ); - * - * @example - * // Disallow state loading by returning false - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateLoadParams": function (settings, data) { - * return false; - * } - * } ); - * } ); - */ - "fnStateLoadParams": null, - - - /** - * Callback that is called when the state has been loaded from the state saving method - * and the DataTables settings object has been modified as a result of the loaded state. - * @type function - * @param {object} settings DataTables settings object - * @param {object} data The state object that was loaded - * - * @dtopt Callbacks - * @name DataTable.defaults.stateLoaded - * - * @example - * // Show an alert with the filtering value that was saved - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateLoaded": function (settings, data) { - * alert( 'Saved filter was: '+data.oSearch.sSearch ); - * } - * } ); - * } ); - */ - "fnStateLoaded": null, - - - /** - * Save the table state. This function allows you to define where and how the state - * information for the table is stored By default DataTables will use `localStorage` - * but you might wish to use a server-side database or cookies. - * @type function - * @member - * @param {object} settings DataTables settings object - * @param {object} data The state object to be saved - * - * @dtopt Callbacks - * @name DataTable.defaults.stateSaveCallback - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateSaveCallback": function (settings, data) { - * // Send an Ajax request to the server with the state object - * $.ajax( { - * "url": "/state_save", - * "data": data, - * "dataType": "json", - * "method": "POST" - * "success": function () {} - * } ); - * } - * } ); - * } ); - */ - "fnStateSaveCallback": function ( settings, data ) { - try { - (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( - 'DataTables_'+settings.sInstance+'_'+location.pathname, - JSON.stringify( data ) - ); - } catch (e) {} - }, - - - /** - * Callback which allows modification of the state to be saved. Called when the table - * has changed state a new state save is required. This method allows modification of - * the state saving object prior to actually doing the save, including addition or - * other state properties or modification. Note that for plug-in authors, you should - * use the `stateSaveParams` event to save parameters for a plug-in. - * @type function - * @param {object} settings DataTables settings object - * @param {object} data The state object to be saved - * - * @dtopt Callbacks - * @name DataTable.defaults.stateSaveParams - * - * @example - * // Remove a saved filter, so filtering is never saved - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateSave": true, - * "stateSaveParams": function (settings, data) { - * data.oSearch.sSearch = ""; - * } - * } ); - * } ); - */ - "fnStateSaveParams": null, - - - /** - * Duration for which the saved state information is considered valid. After this period - * has elapsed the state will be returned to the default. - * Value is given in seconds. - * @type int - * @default 7200 (2 hours) - * - * @dtopt Options - * @name DataTable.defaults.stateDuration - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "stateDuration": 60*60*24; // 1 day - * } ); - * } ) - */ - "iStateDuration": 7200, - - - /** - * When enabled DataTables will not make a request to the server for the first - * page draw - rather it will use the data already on the page (no sorting etc - * will be applied to it), thus saving on an XHR at load time. `deferLoading` - * is used to indicate that deferred loading is required, but it is also used - * to tell DataTables how many records there are in the full table (allowing - * the information element and pagination to be displayed correctly). In the case - * where a filtering is applied to the table on initial load, this can be - * indicated by giving the parameter as an array, where the first element is - * the number of records available after filtering and the second element is the - * number of records without filtering (allowing the table information element - * to be shown correctly). - * @type int | array - * @default null - * - * @dtopt Options - * @name DataTable.defaults.deferLoading - * - * @example - * // 57 records available in the table, no filtering applied - * $(document).ready( function() { - * $('#example').dataTable( { - * "serverSide": true, - * "ajax": "scripts/server_processing.php", - * "deferLoading": 57 - * } ); - * } ); - * - * @example - * // 57 records after filtering, 100 without filtering (an initial filter applied) - * $(document).ready( function() { - * $('#example').dataTable( { - * "serverSide": true, - * "ajax": "scripts/server_processing.php", - * "deferLoading": [ 57, 100 ], - * "search": { - * "search": "my_filter" - * } - * } ); - * } ); - */ - "iDeferLoading": null, - - - /** - * Number of rows to display on a single page when using pagination. If - * feature enabled (`lengthChange`) then the end user will be able to override - * this to a custom setting using a pop-up menu. - * @type int - * @default 10 - * - * @dtopt Options - * @name DataTable.defaults.pageLength - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "pageLength": 50 - * } ); - * } ) - */ - "iDisplayLength": 10, - - - /** - * Define the starting point for data display when using DataTables with - * pagination. Note that this parameter is the number of records, rather than - * the page number, so if you have 10 records per page and want to start on - * the third page, it should be "20". - * @type int - * @default 0 - * - * @dtopt Options - * @name DataTable.defaults.displayStart - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "displayStart": 20 - * } ); - * } ) - */ - "iDisplayStart": 0, - - - /** - * By default DataTables allows keyboard navigation of the table (sorting, paging, - * and filtering) by adding a `tabindex` attribute to the required elements. This - * allows you to tab through the controls and press the enter key to activate them. - * The tabindex is default 0, meaning that the tab follows the flow of the document. - * You can overrule this using this parameter if you wish. Use a value of -1 to - * disable built-in keyboard navigation. - * @type int - * @default 0 - * - * @dtopt Options - * @name DataTable.defaults.tabIndex - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "tabIndex": 1 - * } ); - * } ); - */ - "iTabIndex": 0, - - - /** - * Classes that DataTables assigns to the various components and features - * that it adds to the HTML table. This allows classes to be configured - * during initialisation in addition to through the static - * {@link DataTable.ext.oStdClasses} object). - * @namespace - * @name DataTable.defaults.classes - */ - "oClasses": {}, - - - /** - * All strings that DataTables uses in the user interface that it creates - * are defined in this object, allowing you to modified them individually or - * completely replace them all as required. - * @namespace - * @name DataTable.defaults.language - */ - "oLanguage": { - /** - * Strings that are used for WAI-ARIA labels and controls only (these are not - * actually visible on the page, but will be read by screenreaders, and thus - * must be internationalised as well). - * @namespace - * @name DataTable.defaults.language.aria - */ - "oAria": { - /** - * ARIA label that is added to the table headers when the column may be - * sorted ascending by activing the column (click or return when focused). - * Note that the column header is prefixed to this string. - * @type string - * @default : activate to sort column ascending - * - * @dtopt Language - * @name DataTable.defaults.language.aria.sortAscending - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "aria": { - * "sortAscending": " - click/return to sort ascending" - * } - * } - * } ); - * } ); - */ - "sSortAscending": ": activate to sort column ascending", - - /** - * ARIA label that is added to the table headers when the column may be - * sorted descending by activing the column (click or return when focused). - * Note that the column header is prefixed to this string. - * @type string - * @default : activate to sort column ascending - * - * @dtopt Language - * @name DataTable.defaults.language.aria.sortDescending - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "aria": { - * "sortDescending": " - click/return to sort descending" - * } - * } - * } ); - * } ); - */ - "sSortDescending": ": activate to sort column descending" - }, - - /** - * Pagination string used by DataTables for the built-in pagination - * control types. - * @namespace - * @name DataTable.defaults.language.paginate - */ - "oPaginate": { - /** - * Text to use when using the 'full_numbers' type of pagination for the - * button to take the user to the first page. - * @type string - * @default First - * - * @dtopt Language - * @name DataTable.defaults.language.paginate.first - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "paginate": { - * "first": "First page" - * } - * } - * } ); - * } ); - */ - "sFirst": "First", - - - /** - * Text to use when using the 'full_numbers' type of pagination for the - * button to take the user to the last page. - * @type string - * @default Last - * - * @dtopt Language - * @name DataTable.defaults.language.paginate.last - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "paginate": { - * "last": "Last page" - * } - * } - * } ); - * } ); - */ - "sLast": "Last", - - - /** - * Text to use for the 'next' pagination button (to take the user to the - * next page). - * @type string - * @default Next - * - * @dtopt Language - * @name DataTable.defaults.language.paginate.next - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "paginate": { - * "next": "Next page" - * } - * } - * } ); - * } ); - */ - "sNext": "Next", - - - /** - * Text to use for the 'previous' pagination button (to take the user to - * the previous page). - * @type string - * @default Previous - * - * @dtopt Language - * @name DataTable.defaults.language.paginate.previous - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "paginate": { - * "previous": "Previous page" - * } - * } - * } ); - * } ); - */ - "sPrevious": "Previous" - }, - - /** - * This string is shown in preference to `zeroRecords` when the table is - * empty of data (regardless of filtering). Note that this is an optional - * parameter - if it is not given, the value of `zeroRecords` will be used - * instead (either the default or given value). - * @type string - * @default No data available in table - * - * @dtopt Language - * @name DataTable.defaults.language.emptyTable - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "emptyTable": "No data available in table" - * } - * } ); - * } ); - */ - "sEmptyTable": "No data available in table", - - - /** - * This string gives information to the end user about the information - * that is current on display on the page. The following tokens can be - * used in the string and will be dynamically replaced as the table - * display updates. This tokens can be placed anywhere in the string, or - * removed as needed by the language requires: - * - * * `\_START\_` - Display index of the first record on the current page - * * `\_END\_` - Display index of the last record on the current page - * * `\_TOTAL\_` - Number of records in the table after filtering - * * `\_MAX\_` - Number of records in the table without filtering - * * `\_PAGE\_` - Current page number - * * `\_PAGES\_` - Total number of pages of data in the table - * - * @type string - * @default Showing _START_ to _END_ of _TOTAL_ entries - * - * @dtopt Language - * @name DataTable.defaults.language.info - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "info": "Showing page _PAGE_ of _PAGES_" - * } - * } ); - * } ); - */ - "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", - - - /** - * Display information string for when the table is empty. Typically the - * format of this string should match `info`. - * @type string - * @default Showing 0 to 0 of 0 entries - * - * @dtopt Language - * @name DataTable.defaults.language.infoEmpty - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "infoEmpty": "No entries to show" - * } - * } ); - * } ); - */ - "sInfoEmpty": "Showing 0 to 0 of 0 entries", - - - /** - * When a user filters the information in a table, this string is appended - * to the information (`info`) to give an idea of how strong the filtering - * is. The variable _MAX_ is dynamically updated. - * @type string - * @default (filtered from _MAX_ total entries) - * - * @dtopt Language - * @name DataTable.defaults.language.infoFiltered - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "infoFiltered": " - filtering from _MAX_ records" - * } - * } ); - * } ); - */ - "sInfoFiltered": "(filtered from _MAX_ total entries)", - - - /** - * If can be useful to append extra information to the info string at times, - * and this variable does exactly that. This information will be appended to - * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are - * being used) at all times. - * @type string - * @default Empty string - * - * @dtopt Language - * @name DataTable.defaults.language.infoPostFix - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "infoPostFix": "All records shown are derived from real information." - * } - * } ); - * } ); - */ - "sInfoPostFix": "", - - - /** - * This decimal place operator is a little different from the other - * language options since DataTables doesn't output floating point - * numbers, so it won't ever use this for display of a number. Rather, - * what this parameter does is modify the sort methods of the table so - * that numbers which are in a format which has a character other than - * a period (`.`) as a decimal place will be sorted numerically. - * - * Note that numbers with different decimal places cannot be shown in - * the same table and still be sortable, the table must be consistent. - * However, multiple different tables on the page can use different - * decimal place characters. - * @type string - * @default - * - * @dtopt Language - * @name DataTable.defaults.language.decimal - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "decimal": "," - * "thousands": "." - * } - * } ); - * } ); - */ - "sDecimal": "", - - - /** - * DataTables has a build in number formatter (`formatNumber`) which is - * used to format large numbers that are used in the table information. - * By default a comma is used, but this can be trivially changed to any - * character you wish with this parameter. - * @type string - * @default , - * - * @dtopt Language - * @name DataTable.defaults.language.thousands - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "thousands": "'" - * } - * } ); - * } ); - */ - "sThousands": ",", - - - /** - * Detail the action that will be taken when the drop down menu for the - * pagination length option is changed. The '_MENU_' variable is replaced - * with a default select list of 10, 25, 50 and 100, and can be replaced - * with a custom select box if required. - * @type string - * @default Show _MENU_ entries - * - * @dtopt Language - * @name DataTable.defaults.language.lengthMenu - * - * @example - * // Language change only - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "lengthMenu": "Display _MENU_ records" - * } - * } ); - * } ); - * - * @example - * // Language and options change - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "lengthMenu": 'Display records' - * } - * } ); - * } ); - */ - "sLengthMenu": "Show _MENU_ entries", - - - /** - * When using Ajax sourced data and during the first draw when DataTables is - * gathering the data, this message is shown in an empty row in the table to - * indicate to the end user the the data is being loaded. Note that this - * parameter is not used when loading data by server-side processing, just - * Ajax sourced data with client-side processing. - * @type string - * @default Loading... - * - * @dtopt Language - * @name DataTable.defaults.language.loadingRecords - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "loadingRecords": "Please wait - loading..." - * } - * } ); - * } ); - */ - "sLoadingRecords": "Loading...", - - - /** - * Text which is displayed when the table is processing a user action - * (usually a sort command or similar). - * @type string - * @default Processing... - * - * @dtopt Language - * @name DataTable.defaults.language.processing - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "processing": "DataTables is currently busy" - * } - * } ); - * } ); - */ - "sProcessing": "Processing...", - - - /** - * Details the actions that will be taken when the user types into the - * filtering input text box. The variable "_INPUT_", if used in the string, - * is replaced with the HTML text box for the filtering input allowing - * control over where it appears in the string. If "_INPUT_" is not given - * then the input box is appended to the string automatically. - * @type string - * @default Search: - * - * @dtopt Language - * @name DataTable.defaults.language.search - * - * @example - * // Input text box will be appended at the end automatically - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "search": "Filter records:" - * } - * } ); - * } ); - * - * @example - * // Specify where the filter should appear - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "search": "Apply filter _INPUT_ to table" - * } - * } ); - * } ); - */ - "sSearch": "Search:", - - - /** - * Assign a `placeholder` attribute to the search `input` element - * @type string - * @default - * - * @dtopt Language - * @name DataTable.defaults.language.searchPlaceholder - */ - "sSearchPlaceholder": "", - - - /** - * All of the language information can be stored in a file on the - * server-side, which DataTables will look up if this parameter is passed. - * It must store the URL of the language file, which is in a JSON format, - * and the object has the same properties as the oLanguage object in the - * initialiser object (i.e. the above parameters). Please refer to one of - * the example language files to see how this works in action. - * @type string - * @default Empty string - i.e. disabled - * - * @dtopt Language - * @name DataTable.defaults.language.url - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt" - * } - * } ); - * } ); - */ - "sUrl": "", - - - /** - * Text shown inside the table records when the is no information to be - * displayed after filtering. `emptyTable` is shown when there is simply no - * information in the table at all (regardless of filtering). - * @type string - * @default No matching records found - * - * @dtopt Language - * @name DataTable.defaults.language.zeroRecords - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "language": { - * "zeroRecords": "No records to display" - * } - * } ); - * } ); - */ - "sZeroRecords": "No matching records found" - }, - - - /** - * This parameter allows you to have define the global filtering state at - * initialisation time. As an object the `search` parameter must be - * defined, but all other parameters are optional. When `regex` is true, - * the search string will be treated as a regular expression, when false - * (default) it will be treated as a straight string. When `smart` - * DataTables will use it's smart filtering methods (to word match at - * any point in the data), when false this will not be done. - * @namespace - * @extends DataTable.models.oSearch - * - * @dtopt Options - * @name DataTable.defaults.search - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "search": {"search": "Initial search"} - * } ); - * } ) - */ - "oSearch": $.extend( {}, DataTable.models.oSearch ), - - - /** - * __Deprecated__ The functionality provided by this parameter has now been - * superseded by that provided through `ajax`, which should be used instead. - * - * By default DataTables will look for the property `data` (or `aaData` for - * compatibility with DataTables 1.9-) when obtaining data from an Ajax - * source or for server-side processing - this parameter allows that - * property to be changed. You can use Javascript dotted object notation to - * get a data source for multiple levels of nesting. - * @type string - * @default data - * - * @dtopt Options - * @dtopt Server-side - * @name DataTable.defaults.ajaxDataProp - * - * @deprecated 1.10. Please use `ajax` for this functionality now. - */ - "sAjaxDataProp": "data", - - - /** - * __Deprecated__ The functionality provided by this parameter has now been - * superseded by that provided through `ajax`, which should be used instead. - * - * You can instruct DataTables to load data from an external - * source using this parameter (use aData if you want to pass data in you - * already have). Simply provide a url a JSON object can be obtained from. - * @type string - * @default null - * - * @dtopt Options - * @dtopt Server-side - * @name DataTable.defaults.ajaxSource - * - * @deprecated 1.10. Please use `ajax` for this functionality now. - */ - "sAjaxSource": null, - - - /** - * This initialisation variable allows you to specify exactly where in the - * DOM you want DataTables to inject the various controls it adds to the page - * (for example you might want the pagination controls at the top of the - * table). DIV elements (with or without a custom class) can also be added to - * aid styling. The follow syntax is used: - *
      - *
    • The following options are allowed: - *
        - *
      • 'l' - Length changing
      • - *
      • 'f' - Filtering input
      • - *
      • 't' - The table!
      • - *
      • 'i' - Information
      • - *
      • 'p' - Pagination
      • - *
      • 'r' - pRocessing
      • - *
      - *
    • - *
    • The following constants are allowed: - *
        - *
      • 'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')
      • - *
      • 'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')
      • - *
      - *
    • - *
    • The following syntax is expected: - *
        - *
      • '<' and '>' - div elements
      • - *
      • '<"class" and '>' - div with a class
      • - *
      • '<"#id" and '>' - div with an ID
      • - *
      - *
    • - *
    • Examples: - *
        - *
      • '<"wrapper"flipt>'
      • - *
      • '<lf<t>ip>'
      • - *
      - *
    • - *
    - * @type string - * @default lfrtip (when `jQueryUI` is false) or - * <"H"lfr>t<"F"ip> (when `jQueryUI` is true) - * - * @dtopt Options - * @name DataTable.defaults.dom - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "dom": '<"top"i>rt<"bottom"flp><"clear">' - * } ); - * } ); - */ - "sDom": "lfrtip", - - - /** - * Search delay option. This will throttle full table searches that use the - * DataTables provided search input element (it does not effect calls to - * `dt-api search()`, providing a delay before the search is made. - * @type integer - * @default 0 - * - * @dtopt Options - * @name DataTable.defaults.searchDelay - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "searchDelay": 200 - * } ); - * } ) - */ - "searchDelay": null, - - - /** - * DataTables features four different built-in options for the buttons to - * display for pagination control: - * - * * `simple` - 'Previous' and 'Next' buttons only - * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers - * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons - * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus - * page numbers - * - * Further methods can be added using {@link DataTable.ext.oPagination}. - * @type string - * @default simple_numbers - * - * @dtopt Options - * @name DataTable.defaults.pagingType - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "pagingType": "full_numbers" - * } ); - * } ) - */ - "sPaginationType": "simple_numbers", - - - /** - * Enable horizontal scrolling. When a table is too wide to fit into a - * certain layout, or you have a large number of columns in the table, you - * can enable x-scrolling to show the table in a viewport, which can be - * scrolled. This property can be `true` which will allow the table to - * scroll horizontally when needed, or any CSS unit, or a number (in which - * case it will be treated as a pixel measurement). Setting as simply `true` - * is recommended. - * @type boolean|string - * @default blank string - i.e. disabled - * - * @dtopt Features - * @name DataTable.defaults.scrollX - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "scrollX": true, - * "scrollCollapse": true - * } ); - * } ); - */ - "sScrollX": "", - - - /** - * This property can be used to force a DataTable to use more width than it - * might otherwise do when x-scrolling is enabled. For example if you have a - * table which requires to be well spaced, this parameter is useful for - * "over-sizing" the table, and thus forcing scrolling. This property can by - * any CSS unit, or a number (in which case it will be treated as a pixel - * measurement). - * @type string - * @default blank string - i.e. disabled - * - * @dtopt Options - * @name DataTable.defaults.scrollXInner - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "scrollX": "100%", - * "scrollXInner": "110%" - * } ); - * } ); - */ - "sScrollXInner": "", - - - /** - * Enable vertical scrolling. Vertical scrolling will constrain the DataTable - * to the given height, and enable scrolling for any data which overflows the - * current viewport. This can be used as an alternative to paging to display - * a lot of data in a small area (although paging and scrolling can both be - * enabled at the same time). This property can be any CSS unit, or a number - * (in which case it will be treated as a pixel measurement). - * @type string - * @default blank string - i.e. disabled - * - * @dtopt Features - * @name DataTable.defaults.scrollY - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "scrollY": "200px", - * "paginate": false - * } ); - * } ); - */ - "sScrollY": "", - - - /** - * __Deprecated__ The functionality provided by this parameter has now been - * superseded by that provided through `ajax`, which should be used instead. - * - * Set the HTTP method that is used to make the Ajax call for server-side - * processing or Ajax sourced data. - * @type string - * @default GET - * - * @dtopt Options - * @dtopt Server-side - * @name DataTable.defaults.serverMethod - * - * @deprecated 1.10. Please use `ajax` for this functionality now. - */ - "sServerMethod": "GET", - - - /** - * DataTables makes use of renderers when displaying HTML elements for - * a table. These renderers can be added or modified by plug-ins to - * generate suitable mark-up for a site. For example the Bootstrap - * integration plug-in for DataTables uses a paging button renderer to - * display pagination buttons in the mark-up required by Bootstrap. - * - * For further information about the renderers available see - * DataTable.ext.renderer - * @type string|object - * @default null - * - * @name DataTable.defaults.renderer - * - */ - "renderer": null, - - - /** - * Set the data property name that DataTables should use to get a row's id - * to set as the `id` property in the node. - * @type string - * @default DT_RowId - * - * @name DataTable.defaults.rowId - */ - "rowId": "DT_RowId" - }; - - _fnHungarianMap( DataTable.defaults ); - - - - /* - * Developer note - See note in model.defaults.js about the use of Hungarian - * notation and camel case. - */ - - /** - * Column options that can be given to DataTables at initialisation time. - * @namespace - */ - DataTable.defaults.column = { - /** - * Define which column(s) an order will occur on for this column. This - * allows a column's ordering to take multiple columns into account when - * doing a sort or use the data from a different column. For example first - * name / last name columns make sense to do a multi-column sort over the - * two columns. - * @type array|int - * @default null Takes the value of the column index automatically - * - * @name DataTable.defaults.column.orderData - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "orderData": [ 0, 1 ], "targets": [ 0 ] }, - * { "orderData": [ 1, 0 ], "targets": [ 1 ] }, - * { "orderData": 2, "targets": [ 2 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "orderData": [ 0, 1 ] }, - * { "orderData": [ 1, 0 ] }, - * { "orderData": 2 }, - * null, - * null - * ] - * } ); - * } ); - */ - "aDataSort": null, - "iDataSort": -1, - - - /** - * You can control the default ordering direction, and even alter the - * behaviour of the sort handler (i.e. only allow ascending ordering etc) - * using this parameter. - * @type array - * @default [ 'asc', 'desc' ] - * - * @name DataTable.defaults.column.orderSequence - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "orderSequence": [ "asc" ], "targets": [ 1 ] }, - * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] }, - * { "orderSequence": [ "desc" ], "targets": [ 3 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * null, - * { "orderSequence": [ "asc" ] }, - * { "orderSequence": [ "desc", "asc", "asc" ] }, - * { "orderSequence": [ "desc" ] }, - * null - * ] - * } ); - * } ); - */ - "asSorting": [ 'asc', 'desc' ], - - - /** - * Enable or disable filtering on the data in this column. - * @type boolean - * @default true - * - * @name DataTable.defaults.column.searchable - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "searchable": false, "targets": [ 0 ] } - * ] } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "searchable": false }, - * null, - * null, - * null, - * null - * ] } ); - * } ); - */ - "bSearchable": true, - - - /** - * Enable or disable ordering on this column. - * @type boolean - * @default true - * - * @name DataTable.defaults.column.orderable - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "orderable": false, "targets": [ 0 ] } - * ] } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "orderable": false }, - * null, - * null, - * null, - * null - * ] } ); - * } ); - */ - "bSortable": true, - - - /** - * Enable or disable the display of this column. - * @type boolean - * @default true - * - * @name DataTable.defaults.column.visible - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "visible": false, "targets": [ 0 ] } - * ] } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "visible": false }, - * null, - * null, - * null, - * null - * ] } ); - * } ); - */ - "bVisible": true, - - - /** - * Developer definable function that is called whenever a cell is created (Ajax source, - * etc) or processed for input (DOM source). This can be used as a compliment to mRender - * allowing you to modify the DOM element (add background colour for example) when the - * element is available. - * @type function - * @param {element} td The TD node that has been created - * @param {*} cellData The Data for the cell - * @param {array|object} rowData The data for the whole row - * @param {int} row The row index for the aoData data store - * @param {int} col The column index for aoColumns - * - * @name DataTable.defaults.column.createdCell - * @dtopt Columns - * - * @example - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [3], - * "createdCell": function (td, cellData, rowData, row, col) { - * if ( cellData == "1.7" ) { - * $(td).css('color', 'blue') - * } - * } - * } ] - * }); - * } ); - */ - "fnCreatedCell": null, - - - /** - * This parameter has been replaced by `data` in DataTables to ensure naming - * consistency. `dataProp` can still be used, as there is backwards - * compatibility in DataTables for this option, but it is strongly - * recommended that you use `data` in preference to `dataProp`. - * @name DataTable.defaults.column.dataProp - */ - - - /** - * This property can be used to read data from any data source property, - * including deeply nested objects / properties. `data` can be given in a - * number of different ways which effect its behaviour: - * - * * `integer` - treated as an array index for the data source. This is the - * default that DataTables uses (incrementally increased for each column). - * * `string` - read an object property from the data source. There are - * three 'special' options that can be used in the string to alter how - * DataTables reads the data from the source object: - * * `.` - Dotted Javascript notation. Just as you use a `.` in - * Javascript to read from nested objects, so to can the options - * specified in `data`. For example: `browser.version` or - * `browser.name`. If your object parameter name contains a period, use - * `\\` to escape it - i.e. `first\\.name`. - * * `[]` - Array notation. DataTables can automatically combine data - * from and array source, joining the data with the characters provided - * between the two brackets. For example: `name[, ]` would provide a - * comma-space separated list from the source array. If no characters - * are provided between the brackets, the original array source is - * returned. - * * `()` - Function notation. Adding `()` to the end of a parameter will - * execute a function of the name given. For example: `browser()` for a - * simple function on the data source, `browser.version()` for a - * function in a nested property or even `browser().version` to get an - * object property if the function called returns an object. Note that - * function notation is recommended for use in `render` rather than - * `data` as it is much simpler to use as a renderer. - * * `null` - use the original data source for the row rather than plucking - * data directly from it. This action has effects on two other - * initialisation options: - * * `defaultContent` - When null is given as the `data` option and - * `defaultContent` is specified for the column, the value defined by - * `defaultContent` will be used for the cell. - * * `render` - When null is used for the `data` option and the `render` - * option is specified for the column, the whole data source for the - * row is used for the renderer. - * * `function` - the function given will be executed whenever DataTables - * needs to set or get the data for a cell in the column. The function - * takes three parameters: - * * Parameters: - * * `{array|object}` The data source for the row - * * `{string}` The type call data requested - this will be 'set' when - * setting data or 'filter', 'display', 'type', 'sort' or undefined - * when gathering data. Note that when `undefined` is given for the - * type DataTables expects to get the raw data for the object back< - * * `{*}` Data to set when the second parameter is 'set'. - * * Return: - * * The return value from the function is not required when 'set' is - * the type of call, but otherwise the return is what will be used - * for the data requested. - * - * Note that `data` is a getter and setter option. If you just require - * formatting of data for output, you will likely want to use `render` which - * is simply a getter and thus simpler to use. - * - * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The - * name change reflects the flexibility of this property and is consistent - * with the naming of mRender. If 'mDataProp' is given, then it will still - * be used by DataTables, as it automatically maps the old name to the new - * if required. - * - * @type string|int|function|null - * @default null Use automatically calculated column index - * - * @name DataTable.defaults.column.data - * @dtopt Columns - * - * @example - * // Read table data from objects - * // JSON structure for each row: - * // { - * // "engine": {value}, - * // "browser": {value}, - * // "platform": {value}, - * // "version": {value}, - * // "grade": {value} - * // } - * $(document).ready( function() { - * $('#example').dataTable( { - * "ajaxSource": "sources/objects.txt", - * "columns": [ - * { "data": "engine" }, - * { "data": "browser" }, - * { "data": "platform" }, - * { "data": "version" }, - * { "data": "grade" } - * ] - * } ); - * } ); - * - * @example - * // Read information from deeply nested objects - * // JSON structure for each row: - * // { - * // "engine": {value}, - * // "browser": {value}, - * // "platform": { - * // "inner": {value} - * // }, - * // "details": [ - * // {value}, {value} - * // ] - * // } - * $(document).ready( function() { - * $('#example').dataTable( { - * "ajaxSource": "sources/deep.txt", - * "columns": [ - * { "data": "engine" }, - * { "data": "browser" }, - * { "data": "platform.inner" }, - * { "data": "platform.details.0" }, - * { "data": "platform.details.1" } - * ] - * } ); - * } ); - * - * @example - * // Using `data` as a function to provide different information for - * // sorting, filtering and display. In this case, currency (price) - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": function ( source, type, val ) { - * if (type === 'set') { - * source.price = val; - * // Store the computed dislay and filter values for efficiency - * source.price_display = val=="" ? "" : "$"+numberFormat(val); - * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val; - * return; - * } - * else if (type === 'display') { - * return source.price_display; - * } - * else if (type === 'filter') { - * return source.price_filter; - * } - * // 'sort', 'type' and undefined all just use the integer - * return source.price; - * } - * } ] - * } ); - * } ); - * - * @example - * // Using default content - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": null, - * "defaultContent": "Click to edit" - * } ] - * } ); - * } ); - * - * @example - * // Using array notation - outputting a list from an array - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": "name[, ]" - * } ] - * } ); - * } ); - * - */ - "mData": null, - - - /** - * This property is the rendering partner to `data` and it is suggested that - * when you want to manipulate data for display (including filtering, - * sorting etc) without altering the underlying data for the table, use this - * property. `render` can be considered to be the the read only companion to - * `data` which is read / write (then as such more complex). Like `data` - * this option can be given in a number of different ways to effect its - * behaviour: - * - * * `integer` - treated as an array index for the data source. This is the - * default that DataTables uses (incrementally increased for each column). - * * `string` - read an object property from the data source. There are - * three 'special' options that can be used in the string to alter how - * DataTables reads the data from the source object: - * * `.` - Dotted Javascript notation. Just as you use a `.` in - * Javascript to read from nested objects, so to can the options - * specified in `data`. For example: `browser.version` or - * `browser.name`. If your object parameter name contains a period, use - * `\\` to escape it - i.e. `first\\.name`. - * * `[]` - Array notation. DataTables can automatically combine data - * from and array source, joining the data with the characters provided - * between the two brackets. For example: `name[, ]` would provide a - * comma-space separated list from the source array. If no characters - * are provided between the brackets, the original array source is - * returned. - * * `()` - Function notation. Adding `()` to the end of a parameter will - * execute a function of the name given. For example: `browser()` for a - * simple function on the data source, `browser.version()` for a - * function in a nested property or even `browser().version` to get an - * object property if the function called returns an object. - * * `object` - use different data for the different data types requested by - * DataTables ('filter', 'display', 'type' or 'sort'). The property names - * of the object is the data type the property refers to and the value can - * defined using an integer, string or function using the same rules as - * `render` normally does. Note that an `_` option _must_ be specified. - * This is the default value to use if you haven't specified a value for - * the data type requested by DataTables. - * * `function` - the function given will be executed whenever DataTables - * needs to set or get the data for a cell in the column. The function - * takes three parameters: - * * Parameters: - * * {array|object} The data source for the row (based on `data`) - * * {string} The type call data requested - this will be 'filter', - * 'display', 'type' or 'sort'. - * * {array|object} The full data source for the row (not based on - * `data`) - * * Return: - * * The return value from the function is what will be used for the - * data requested. - * - * @type string|int|function|object|null - * @default null Use the data source value. - * - * @name DataTable.defaults.column.render - * @dtopt Columns - * - * @example - * // Create a comma separated list from an array of objects - * $(document).ready( function() { - * $('#example').dataTable( { - * "ajaxSource": "sources/deep.txt", - * "columns": [ - * { "data": "engine" }, - * { "data": "browser" }, - * { - * "data": "platform", - * "render": "[, ].name" - * } - * ] - * } ); - * } ); - * - * @example - * // Execute a function to obtain data - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": null, // Use the full data source object for the renderer's source - * "render": "browserName()" - * } ] - * } ); - * } ); - * - * @example - * // As an object, extracting different data for the different types - * // This would be used with a data source such as: - * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" } - * // Here the `phone` integer is used for sorting and type detection, while `phone_filter` - * // (which has both forms) is used for filtering for if a user inputs either format, while - * // the formatted phone number is the one that is shown in the table. - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": null, // Use the full data source object for the renderer's source - * "render": { - * "_": "phone", - * "filter": "phone_filter", - * "display": "phone_display" - * } - * } ] - * } ); - * } ); - * - * @example - * // Use as a function to create a link from the data source - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "data": "download_link", - * "render": function ( data, type, full ) { - * return 'Download'; - * } - * } ] - * } ); - * } ); - */ - "mRender": null, - - - /** - * Change the cell type created for the column - either TD cells or TH cells. This - * can be useful as TH cells have semantic meaning in the table body, allowing them - * to act as a header for a row (you may wish to add scope='row' to the TH elements). - * @type string - * @default td - * - * @name DataTable.defaults.column.cellType - * @dtopt Columns - * - * @example - * // Make the first column use TH cells - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ { - * "targets": [ 0 ], - * "cellType": "th" - * } ] - * } ); - * } ); - */ - "sCellType": "td", - - - /** - * Class to give to each cell in this column. - * @type string - * @default Empty string - * - * @name DataTable.defaults.column.class - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "class": "my_class", "targets": [ 0 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "class": "my_class" }, - * null, - * null, - * null, - * null - * ] - * } ); - * } ); - */ - "sClass": "", - - /** - * When DataTables calculates the column widths to assign to each column, - * it finds the longest string in each column and then constructs a - * temporary table and reads the widths from that. The problem with this - * is that "mmm" is much wider then "iiii", but the latter is a longer - * string - thus the calculation can go wrong (doing it properly and putting - * it into an DOM object and measuring that is horribly(!) slow). Thus as - * a "work around" we provide this option. It will append its value to the - * text that is found to be the longest string for the column - i.e. padding. - * Generally you shouldn't need this! - * @type string - * @default Empty string - * - * @name DataTable.defaults.column.contentPadding - * @dtopt Columns - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * null, - * null, - * null, - * { - * "contentPadding": "mmm" - * } - * ] - * } ); - * } ); - */ - "sContentPadding": "", - - - /** - * Allows a default value to be given for a column's data, and will be used - * whenever a null data source is encountered (this can be because `data` - * is set to null, or because the data source itself is null). - * @type string - * @default null - * - * @name DataTable.defaults.column.defaultContent - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { - * "data": null, - * "defaultContent": "Edit", - * "targets": [ -1 ] - * } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * null, - * null, - * null, - * { - * "data": null, - * "defaultContent": "Edit" - * } - * ] - * } ); - * } ); - */ - "sDefaultContent": null, - - - /** - * This parameter is only used in DataTables' server-side processing. It can - * be exceptionally useful to know what columns are being displayed on the - * client side, and to map these to database fields. When defined, the names - * also allow DataTables to reorder information from the server if it comes - * back in an unexpected order (i.e. if you switch your columns around on the - * client-side, your server-side code does not also need updating). - * @type string - * @default Empty string - * - * @name DataTable.defaults.column.name - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "name": "engine", "targets": [ 0 ] }, - * { "name": "browser", "targets": [ 1 ] }, - * { "name": "platform", "targets": [ 2 ] }, - * { "name": "version", "targets": [ 3 ] }, - * { "name": "grade", "targets": [ 4 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "name": "engine" }, - * { "name": "browser" }, - * { "name": "platform" }, - * { "name": "version" }, - * { "name": "grade" } - * ] - * } ); - * } ); - */ - "sName": "", - - - /** - * Defines a data source type for the ordering which can be used to read - * real-time information from the table (updating the internally cached - * version) prior to ordering. This allows ordering to occur on user - * editable elements such as form inputs. - * @type string - * @default std - * - * @name DataTable.defaults.column.orderDataType - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "orderDataType": "dom-text", "targets": [ 2, 3 ] }, - * { "type": "numeric", "targets": [ 3 ] }, - * { "orderDataType": "dom-select", "targets": [ 4 ] }, - * { "orderDataType": "dom-checkbox", "targets": [ 5 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * null, - * null, - * { "orderDataType": "dom-text" }, - * { "orderDataType": "dom-text", "type": "numeric" }, - * { "orderDataType": "dom-select" }, - * { "orderDataType": "dom-checkbox" } - * ] - * } ); - * } ); - */ - "sSortDataType": "std", - - - /** - * The title of this column. - * @type string - * @default null Derived from the 'TH' value for this column in the - * original HTML table. - * - * @name DataTable.defaults.column.title - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "title": "My column title", "targets": [ 0 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "title": "My column title" }, - * null, - * null, - * null, - * null - * ] - * } ); - * } ); - */ - "sTitle": null, - - - /** - * The type allows you to specify how the data for this column will be - * ordered. Four types (string, numeric, date and html (which will strip - * HTML tags before ordering)) are currently available. Note that only date - * formats understood by Javascript's Date() object will be accepted as type - * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', - * 'numeric', 'date' or 'html' (by default). Further types can be adding - * through plug-ins. - * @type string - * @default null Auto-detected from raw data - * - * @name DataTable.defaults.column.type - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "type": "html", "targets": [ 0 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "type": "html" }, - * null, - * null, - * null, - * null - * ] - * } ); - * } ); - */ - "sType": null, - - - /** - * Defining the width of the column, this parameter may take any CSS value - * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not - * been given a specific width through this interface ensuring that the table - * remains readable. - * @type string - * @default null Automatic - * - * @name DataTable.defaults.column.width - * @dtopt Columns - * - * @example - * // Using `columnDefs` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columnDefs": [ - * { "width": "20%", "targets": [ 0 ] } - * ] - * } ); - * } ); - * - * @example - * // Using `columns` - * $(document).ready( function() { - * $('#example').dataTable( { - * "columns": [ - * { "width": "20%" }, - * null, - * null, - * null, - * null - * ] - * } ); - * } ); - */ - "sWidth": null - }; - - _fnHungarianMap( DataTable.defaults.column ); - - - - /** - * DataTables settings object - this holds all the information needed for a - * given table, including configuration, data and current application of the - * table options. DataTables does not have a single instance for each DataTable - * with the settings attached to that instance, but rather instances of the - * DataTable "class" are created on-the-fly as needed (typically by a - * $().dataTable() call) and the settings object is then applied to that - * instance. - * - * Note that this object is related to {@link DataTable.defaults} but this - * one is the internal data store for DataTables's cache of columns. It should - * NOT be manipulated outside of DataTables. Any configuration should be done - * through the initialisation options. - * @namespace - */ - DataTable.models.oSettings = { - /** - * Primary features of DataTables and their enablement state. - * @namespace - */ - "oFeatures": { - - /** - * Flag to say if DataTables should automatically try to calculate the - * optimum table and columns widths (true) or not (false). - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bAutoWidth": null, - - /** - * Delay the creation of TR and TD elements until they are actually - * needed by a driven page draw. This can give a significant speed - * increase for Ajax source and Javascript source data, but makes no - * difference at all fro DOM and server-side processing tables. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bDeferRender": null, - - /** - * Enable filtering on the table or not. Note that if this is disabled - * then there is no filtering at all on the table, including fnFilter. - * To just remove the filtering input use sDom and remove the 'f' option. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bFilter": null, - - /** - * Table information element (the 'Showing x of y records' div) enable - * flag. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bInfo": null, - - /** - * Present a user control allowing the end user to change the page size - * when pagination is enabled. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bLengthChange": null, - - /** - * Pagination enabled or not. Note that if this is disabled then length - * changing must also be disabled. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bPaginate": null, - - /** - * Processing indicator enable flag whenever DataTables is enacting a - * user request - typically an Ajax request for server-side processing. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bProcessing": null, - - /** - * Server-side processing enabled flag - when enabled DataTables will - * get all data from the server for every draw - there is no filtering, - * sorting or paging done on the client-side. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bServerSide": null, - - /** - * Sorting enablement flag. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bSort": null, - - /** - * Multi-column sorting - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bSortMulti": null, - - /** - * Apply a class to the columns which are being sorted to provide a - * visual highlight or not. This can slow things down when enabled since - * there is a lot of DOM interaction. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bSortClasses": null, - - /** - * State saving enablement flag. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bStateSave": null - }, - - - /** - * Scrolling settings for a table. - * @namespace - */ - "oScroll": { - /** - * When the table is shorter in height than sScrollY, collapse the - * table container down to the height of the table (when true). - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bCollapse": null, - - /** - * Width of the scrollbar for the web-browser's platform. Calculated - * during table initialisation. - * @type int - * @default 0 - */ - "iBarWidth": 0, - - /** - * Viewport width for horizontal scrolling. Horizontal scrolling is - * disabled if an empty string. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - */ - "sX": null, - - /** - * Width to expand the table to when using x-scrolling. Typically you - * should not need to use this. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - * @deprecated - */ - "sXInner": null, - - /** - * Viewport height for vertical scrolling. Vertical scrolling is disabled - * if an empty string. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - */ - "sY": null - }, - - /** - * Language information for the table. - * @namespace - * @extends DataTable.defaults.oLanguage - */ - "oLanguage": { - /** - * Information callback function. See - * {@link DataTable.defaults.fnInfoCallback} - * @type function - * @default null - */ - "fnInfoCallback": null - }, - - /** - * Browser support parameters - * @namespace - */ - "oBrowser": { - /** - * Indicate if the browser incorrectly calculates width:100% inside a - * scrolling element (IE6/7) - * @type boolean - * @default false - */ - "bScrollOversize": false, - - /** - * Determine if the vertical scrollbar is on the right or left of the - * scrolling container - needed for rtl language layout, although not - * all browsers move the scrollbar (Safari). - * @type boolean - * @default false - */ - "bScrollbarLeft": false, - - /** - * Flag for if `getBoundingClientRect` is fully supported or not - * @type boolean - * @default false - */ - "bBounding": false, - - /** - * Browser scrollbar width - * @type integer - * @default 0 - */ - "barWidth": 0 - }, - - - "ajax": null, - - - /** - * Array referencing the nodes which are used for the features. The - * parameters of this object match what is allowed by sDom - i.e. - *
      - *
    • 'l' - Length changing
    • - *
    • 'f' - Filtering input
    • - *
    • 't' - The table!
    • - *
    • 'i' - Information
    • - *
    • 'p' - Pagination
    • - *
    • 'r' - pRocessing
    • - *
    - * @type array - * @default [] - */ - "aanFeatures": [], - - /** - * Store data information - see {@link DataTable.models.oRow} for detailed - * information. - * @type array - * @default [] - */ - "aoData": [], - - /** - * Array of indexes which are in the current display (after filtering etc) - * @type array - * @default [] - */ - "aiDisplay": [], - - /** - * Array of indexes for display - no filtering - * @type array - * @default [] - */ - "aiDisplayMaster": [], - - /** - * Map of row ids to data indexes - * @type object - * @default {} - */ - "aIds": {}, - - /** - * Store information about each column that is in use - * @type array - * @default [] - */ - "aoColumns": [], - - /** - * Store information about the table's header - * @type array - * @default [] - */ - "aoHeader": [], - - /** - * Store information about the table's footer - * @type array - * @default [] - */ - "aoFooter": [], - - /** - * Store the applied global search information in case we want to force a - * research or compare the old search to a new one. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @namespace - * @extends DataTable.models.oSearch - */ - "oPreviousSearch": {}, - - /** - * Store the applied search for each column - see - * {@link DataTable.models.oSearch} for the format that is used for the - * filtering information for each column. - * @type array - * @default [] - */ - "aoPreSearchCols": [], - - /** - * Sorting that is applied to the table. Note that the inner arrays are - * used in the following manner: - *
      - *
    • Index 0 - column number
    • - *
    • Index 1 - current sorting direction
    • - *
    - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type array - */ - "aaSorting": null, - - /** - * Sorting that is always applied to the table (i.e. prefixed in front of - * aaSorting). - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type array - * @default [] - */ - "aaSortingFixed": [], - - /** - * Classes to use for the striping of a table. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type array - * @default [] - */ - "asStripeClasses": null, - - /** - * If restoring a table - we should restore its striping classes as well - * @type array - * @default [] - */ - "asDestroyStripes": [], - - /** - * If restoring a table - we should restore its width - * @type int - * @default 0 - */ - "sDestroyWidth": 0, - - /** - * Callback functions array for every time a row is inserted (i.e. on a draw). - * @type array - * @default [] - */ - "aoRowCallback": [], - - /** - * Callback functions for the header on each draw. - * @type array - * @default [] - */ - "aoHeaderCallback": [], - - /** - * Callback function for the footer on each draw. - * @type array - * @default [] - */ - "aoFooterCallback": [], - - /** - * Array of callback functions for draw callback functions - * @type array - * @default [] - */ - "aoDrawCallback": [], - - /** - * Array of callback functions for row created function - * @type array - * @default [] - */ - "aoRowCreatedCallback": [], - - /** - * Callback functions for just before the table is redrawn. A return of - * false will be used to cancel the draw. - * @type array - * @default [] - */ - "aoPreDrawCallback": [], - - /** - * Callback functions for when the table has been initialised. - * @type array - * @default [] - */ - "aoInitComplete": [], - - - /** - * Callbacks for modifying the settings to be stored for state saving, prior to - * saving state. - * @type array - * @default [] - */ - "aoStateSaveParams": [], - - /** - * Callbacks for modifying the settings that have been stored for state saving - * prior to using the stored values to restore the state. - * @type array - * @default [] - */ - "aoStateLoadParams": [], - - /** - * Callbacks for operating on the settings object once the saved state has been - * loaded - * @type array - * @default [] - */ - "aoStateLoaded": [], - - /** - * Cache the table ID for quick access - * @type string - * @default Empty string - */ - "sTableId": "", - - /** - * The TABLE node for the main table - * @type node - * @default null - */ - "nTable": null, - - /** - * Permanent ref to the thead element - * @type node - * @default null - */ - "nTHead": null, - - /** - * Permanent ref to the tfoot element - if it exists - * @type node - * @default null - */ - "nTFoot": null, - - /** - * Permanent ref to the tbody element - * @type node - * @default null - */ - "nTBody": null, - - /** - * Cache the wrapper node (contains all DataTables controlled elements) - * @type node - * @default null - */ - "nTableWrapper": null, - - /** - * Indicate if when using server-side processing the loading of data - * should be deferred until the second draw. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - * @default false - */ - "bDeferLoading": false, - - /** - * Indicate if all required information has been read in - * @type boolean - * @default false - */ - "bInitialised": false, - - /** - * Information about open rows. Each object in the array has the parameters - * 'nTr' and 'nParent' - * @type array - * @default [] - */ - "aoOpenRows": [], - - /** - * Dictate the positioning of DataTables' control elements - see - * {@link DataTable.model.oInit.sDom}. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - * @default null - */ - "sDom": null, - - /** - * Search delay (in mS) - * @type integer - * @default null - */ - "searchDelay": null, - - /** - * Which type of pagination should be used. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - * @default two_button - */ - "sPaginationType": "two_button", - - /** - * The state duration (for `stateSave`) in seconds. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type int - * @default 0 - */ - "iStateDuration": 0, - - /** - * Array of callback functions for state saving. Each array element is an - * object with the following parameters: - *
      - *
    • function:fn - function to call. Takes two parameters, oSettings - * and the JSON string to save that has been thus far created. Returns - * a JSON string to be inserted into a json object - * (i.e. '"param": [ 0, 1, 2]')
    • - *
    • string:sName - name of callback
    • - *
    - * @type array - * @default [] - */ - "aoStateSave": [], - - /** - * Array of callback functions for state loading. Each array element is an - * object with the following parameters: - *
      - *
    • function:fn - function to call. Takes two parameters, oSettings - * and the object stored. May return false to cancel state loading
    • - *
    • string:sName - name of callback
    • - *
    - * @type array - * @default [] - */ - "aoStateLoad": [], - - /** - * State that was saved. Useful for back reference - * @type object - * @default null - */ - "oSavedState": null, - - /** - * State that was loaded. Useful for back reference - * @type object - * @default null - */ - "oLoadedState": null, - - /** - * Source url for AJAX data for the table. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - * @default null - */ - "sAjaxSource": null, - - /** - * Property from a given object from which to read the table data from. This - * can be an empty string (when not server-side processing), in which case - * it is assumed an an array is given directly. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - */ - "sAjaxDataProp": null, - - /** - * Note if draw should be blocked while getting data - * @type boolean - * @default true - */ - "bAjaxDataGet": true, - - /** - * The last jQuery XHR object that was used for server-side data gathering. - * This can be used for working with the XHR information in one of the - * callbacks - * @type object - * @default null - */ - "jqXHR": null, - - /** - * JSON returned from the server in the last Ajax request - * @type object - * @default undefined - */ - "json": undefined, - - /** - * Data submitted as part of the last Ajax request - * @type object - * @default undefined - */ - "oAjaxData": undefined, - - /** - * Function to get the server-side data. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type function - */ - "fnServerData": null, - - /** - * Functions which are called prior to sending an Ajax request so extra - * parameters can easily be sent to the server - * @type array - * @default [] - */ - "aoServerParams": [], - - /** - * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if - * required). - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type string - */ - "sServerMethod": null, - - /** - * Format numbers for display. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type function - */ - "fnFormatNumber": null, - - /** - * List of options that can be used for the user selectable length menu. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type array - * @default [] - */ - "aLengthMenu": null, - - /** - * Counter for the draws that the table does. Also used as a tracker for - * server-side processing - * @type int - * @default 0 - */ - "iDraw": 0, - - /** - * Indicate if a redraw is being done - useful for Ajax - * @type boolean - * @default false - */ - "bDrawing": false, - - /** - * Draw index (iDraw) of the last error when parsing the returned data - * @type int - * @default -1 - */ - "iDrawError": -1, - - /** - * Paging display length - * @type int - * @default 10 - */ - "_iDisplayLength": 10, - - /** - * Paging start point - aiDisplay index - * @type int - * @default 0 - */ - "_iDisplayStart": 0, - - /** - * Server-side processing - number of records in the result set - * (i.e. before filtering), Use fnRecordsTotal rather than - * this property to get the value of the number of records, regardless of - * the server-side processing setting. - * @type int - * @default 0 - * @private - */ - "_iRecordsTotal": 0, - - /** - * Server-side processing - number of records in the current display set - * (i.e. after filtering). Use fnRecordsDisplay rather than - * this property to get the value of the number of records, regardless of - * the server-side processing setting. - * @type boolean - * @default 0 - * @private - */ - "_iRecordsDisplay": 0, - - /** - * Flag to indicate if jQuery UI marking and classes should be used. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bJUI": null, - - /** - * The classes to use for the table - * @type object - * @default {} - */ - "oClasses": {}, - - /** - * Flag attached to the settings object so you can check in the draw - * callback if filtering has been done in the draw. Deprecated in favour of - * events. - * @type boolean - * @default false - * @deprecated - */ - "bFiltered": false, - - /** - * Flag attached to the settings object so you can check in the draw - * callback if sorting has been done in the draw. Deprecated in favour of - * events. - * @type boolean - * @default false - * @deprecated - */ - "bSorted": false, - - /** - * Indicate that if multiple rows are in the header and there is more than - * one unique cell per column, if the top one (true) or bottom one (false) - * should be used for sorting / title by DataTables. - * Note that this parameter will be set by the initialisation routine. To - * set a default use {@link DataTable.defaults}. - * @type boolean - */ - "bSortCellsTop": null, - - /** - * Initialisation object that is used for the table - * @type object - * @default null - */ - "oInit": null, - - /** - * Destroy callback functions - for plug-ins to attach themselves to the - * destroy so they can clean up markup and events. - * @type array - * @default [] - */ - "aoDestroyCallback": [], - - - /** - * Get the number of records in the current record set, before filtering - * @type function - */ - "fnRecordsTotal": function () - { - return _fnDataSource( this ) == 'ssp' ? - this._iRecordsTotal * 1 : - this.aiDisplayMaster.length; - }, - - /** - * Get the number of records in the current record set, after filtering - * @type function - */ - "fnRecordsDisplay": function () - { - return _fnDataSource( this ) == 'ssp' ? - this._iRecordsDisplay * 1 : - this.aiDisplay.length; - }, - - /** - * Get the display end point - aiDisplay index - * @type function - */ - "fnDisplayEnd": function () - { - var - len = this._iDisplayLength, - start = this._iDisplayStart, - calc = start + len, - records = this.aiDisplay.length, - features = this.oFeatures, - paginate = features.bPaginate; - - if ( features.bServerSide ) { - return paginate === false || len === -1 ? - start + records : - Math.min( start+len, this._iRecordsDisplay ); - } - else { - return ! paginate || calc>records || len===-1 ? - records : - calc; - } - }, - - /** - * The DataTables object for this table - * @type object - * @default null - */ - "oInstance": null, - - /** - * Unique identifier for each instance of the DataTables object. If there - * is an ID on the table node, then it takes that value, otherwise an - * incrementing internal counter is used. - * @type string - * @default null - */ - "sInstance": null, - - /** - * tabindex attribute value that is added to DataTables control elements, allowing - * keyboard navigation of the table and its controls. - */ - "iTabIndex": 0, - - /** - * DIV container for the footer scrolling table if scrolling - */ - "nScrollHead": null, - - /** - * DIV container for the footer scrolling table if scrolling - */ - "nScrollFoot": null, - - /** - * Last applied sort - * @type array - * @default [] - */ - "aLastSort": [], - - /** - * Stored plug-in instances - * @type object - * @default {} - */ - "oPlugins": {}, - - /** - * Function used to get a row's id from the row's data - * @type function - * @default null - */ - "rowIdFn": null, - - /** - * Data location where to store a row's id - * @type string - * @default null - */ - "rowId": null - }; - - /** - * Extension object for DataTables that is used to provide all extension - * options. - * - * Note that the `DataTable.ext` object is available through - * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is - * also aliased to `jQuery.fn.dataTableExt` for historic reasons. - * @namespace - * @extends DataTable.models.ext - */ - - - /** - * DataTables extensions - * - * This namespace acts as a collection area for plug-ins that can be used to - * extend DataTables capabilities. Indeed many of the build in methods - * use this method to provide their own capabilities (sorting methods for - * example). - * - * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy - * reasons - * - * @namespace - */ - DataTable.ext = _ext = { - /** - * Buttons. For use with the Buttons extension for DataTables. This is - * defined here so other extensions can define buttons regardless of load - * order. It is _not_ used by DataTables core. - * - * @type object - * @default {} - */ - buttons: {}, - - - /** - * Element class names - * - * @type object - * @default {} - */ - classes: {}, - - - /** - * DataTables build type (expanded by the download builder) - * - * @type string - */ - builder: "-source-", - - - /** - * Error reporting. - * - * How should DataTables report an error. Can take the value 'alert', - * 'throw', 'none' or a function. - * - * @type string|function - * @default alert - */ - errMode: "alert", - - - /** - * Feature plug-ins. - * - * This is an array of objects which describe the feature plug-ins that are - * available to DataTables. These feature plug-ins are then available for - * use through the `dom` initialisation option. - * - * Each feature plug-in is described by an object which must have the - * following properties: - * - * * `fnInit` - function that is used to initialise the plug-in, - * * `cFeature` - a character so the feature can be enabled by the `dom` - * instillation option. This is case sensitive. - * - * The `fnInit` function has the following input parameters: - * - * 1. `{object}` DataTables settings object: see - * {@link DataTable.models.oSettings} - * - * And the following return is expected: - * - * * {node|null} The element which contains your feature. Note that the - * return may also be void if your plug-in does not require to inject any - * DOM elements into DataTables control (`dom`) - for example this might - * be useful when developing a plug-in which allows table control via - * keyboard entry - * - * @type array - * - * @example - * $.fn.dataTable.ext.features.push( { - * "fnInit": function( oSettings ) { - * return new TableTools( { "oDTSettings": oSettings } ); - * }, - * "cFeature": "T" - * } ); - */ - feature: [], - - - /** - * Row searching. - * - * This method of searching is complimentary to the default type based - * searching, and a lot more comprehensive as it allows you complete control - * over the searching logic. Each element in this array is a function - * (parameters described below) that is called for every row in the table, - * and your logic decides if it should be included in the searching data set - * or not. - * - * Searching functions have the following input parameters: - * - * 1. `{object}` DataTables settings object: see - * {@link DataTable.models.oSettings} - * 2. `{array|object}` Data for the row to be processed (same as the - * original format that was passed in as the data source, or an array - * from a DOM data source - * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which - * can be useful to retrieve the `TR` element if you need DOM interaction. - * - * And the following return is expected: - * - * * {boolean} Include the row in the searched result set (true) or not - * (false) - * - * Note that as with the main search ability in DataTables, technically this - * is "filtering", since it is subtractive. However, for consistency in - * naming we call it searching here. - * - * @type array - * @default [] - * - * @example - * // The following example shows custom search being applied to the - * // fourth column (i.e. the data[3] index) based on two input values - * // from the end-user, matching the data in a certain range. - * $.fn.dataTable.ext.search.push( - * function( settings, data, dataIndex ) { - * var min = document.getElementById('min').value * 1; - * var max = document.getElementById('max').value * 1; - * var version = data[3] == "-" ? 0 : data[3]*1; - * - * if ( min == "" && max == "" ) { - * return true; - * } - * else if ( min == "" && version < max ) { - * return true; - * } - * else if ( min < version && "" == max ) { - * return true; - * } - * else if ( min < version && version < max ) { - * return true; - * } - * return false; - * } - * ); - */ - search: [], - - - /** - * Selector extensions - * - * The `selector` option can be used to extend the options available for the - * selector modifier options (`selector-modifier` object data type) that - * each of the three built in selector types offer (row, column and cell + - * their plural counterparts). For example the Select extension uses this - * mechanism to provide an option to select only rows, columns and cells - * that have been marked as selected by the end user (`{selected: true}`), - * which can be used in conjunction with the existing built in selector - * options. - * - * Each property is an array to which functions can be pushed. The functions - * take three attributes: - * - * * Settings object for the host table - * * Options object (`selector-modifier` object type) - * * Array of selected item indexes - * - * The return is an array of the resulting item indexes after the custom - * selector has been applied. - * - * @type object - */ - selector: { - cell: [], - column: [], - row: [] - }, - - - /** - * Internal functions, exposed for used in plug-ins. - * - * Please note that you should not need to use the internal methods for - * anything other than a plug-in (and even then, try to avoid if possible). - * The internal function may change between releases. - * - * @type object - * @default {} - */ - internal: {}, - - - /** - * Legacy configuration options. Enable and disable legacy options that - * are available in DataTables. - * - * @type object - */ - legacy: { - /** - * Enable / disable DataTables 1.9 compatible server-side processing - * requests - * - * @type boolean - * @default null - */ - ajax: null - }, - - - /** - * Pagination plug-in methods. - * - * Each entry in this object is a function and defines which buttons should - * be shown by the pagination rendering method that is used for the table: - * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the - * buttons are displayed in the document, while the functions here tell it - * what buttons to display. This is done by returning an array of button - * descriptions (what each button will do). - * - * Pagination types (the four built in options and any additional plug-in - * options defined here) can be used through the `paginationType` - * initialisation parameter. - * - * The functions defined take two parameters: - * - * 1. `{int} page` The current page index - * 2. `{int} pages` The number of pages in the table - * - * Each function is expected to return an array where each element of the - * array can be one of: - * - * * `first` - Jump to first page when activated - * * `last` - Jump to last page when activated - * * `previous` - Show previous page when activated - * * `next` - Show next page when activated - * * `{int}` - Show page of the index given - * * `{array}` - A nested array containing the above elements to add a - * containing 'DIV' element (might be useful for styling). - * - * Note that DataTables v1.9- used this object slightly differently whereby - * an object with two functions would be defined for each plug-in. That - * ability is still supported by DataTables 1.10+ to provide backwards - * compatibility, but this option of use is now decremented and no longer - * documented in DataTables 1.10+. - * - * @type object - * @default {} - * - * @example - * // Show previous, next and current page buttons only - * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { - * return [ 'previous', page, 'next' ]; - * }; - */ - pager: {}, - - - renderer: { - pageButton: {}, - header: {} - }, - - - /** - * Ordering plug-ins - custom data source - * - * The extension options for ordering of data available here is complimentary - * to the default type based ordering that DataTables typically uses. It - * allows much greater control over the the data that is being used to - * order a column, but is necessarily therefore more complex. - * - * This type of ordering is useful if you want to do ordering based on data - * live from the DOM (for example the contents of an 'input' element) rather - * than just the static string that DataTables knows of. - * - * The way these plug-ins work is that you create an array of the values you - * wish to be ordering for the column in question and then return that - * array. The data in the array much be in the index order of the rows in - * the table (not the currently ordering order!). Which order data gathering - * function is run here depends on the `dt-init columns.orderDataType` - * parameter that is used for the column (if any). - * - * The functions defined take two parameters: - * - * 1. `{object}` DataTables settings object: see - * {@link DataTable.models.oSettings} - * 2. `{int}` Target column index - * - * Each function is expected to return an array: - * - * * `{array}` Data for the column to be ordering upon - * - * @type array - * - * @example - * // Ordering using `input` node values - * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) - * { - * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { - * return $('input', td).val(); - * } ); - * } - */ - order: {}, - - - /** - * Type based plug-ins. - * - * Each column in DataTables has a type assigned to it, either by automatic - * detection or by direct assignment using the `type` option for the column. - * The type of a column will effect how it is ordering and search (plug-ins - * can also make use of the column type if required). - * - * @namespace - */ - type: { - /** - * Type detection functions. - * - * The functions defined in this object are used to automatically detect - * a column's type, making initialisation of DataTables super easy, even - * when complex data is in the table. - * - * The functions defined take two parameters: - * - * 1. `{*}` Data from the column cell to be analysed - * 2. `{settings}` DataTables settings object. This can be used to - * perform context specific type detection - for example detection - * based on language settings such as using a comma for a decimal - * place. Generally speaking the options from the settings will not - * be required - * - * Each function is expected to return: - * - * * `{string|null}` Data type detected, or null if unknown (and thus - * pass it on to the other type detection functions. - * - * @type array - * - * @example - * // Currency type detection plug-in: - * $.fn.dataTable.ext.type.detect.push( - * function ( data, settings ) { - * // Check the numeric part - * if ( ! $.isNumeric( data.substring(1) ) ) { - * return null; - * } - * - * // Check prefixed by currency - * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { - * return 'currency'; - * } - * return null; - * } - * ); - */ - detect: [], - - - /** - * Type based search formatting. - * - * The type based searching functions can be used to pre-format the - * data to be search on. For example, it can be used to strip HTML - * tags or to de-format telephone numbers for numeric only searching. - * - * Note that is a search is not defined for a column of a given type, - * no search formatting will be performed. - * - * Pre-processing of searching data plug-ins - When you assign the sType - * for a column (or have it automatically detected for you by DataTables - * or a type detection plug-in), you will typically be using this for - * custom sorting, but it can also be used to provide custom searching - * by allowing you to pre-processing the data and returning the data in - * the format that should be searched upon. This is done by adding - * functions this object with a parameter name which matches the sType - * for that target column. This is the corollary of afnSortData - * for searching data. - * - * The functions defined take a single parameter: - * - * 1. `{*}` Data from the column cell to be prepared for searching - * - * Each function is expected to return: - * - * * `{string|null}` Formatted string that will be used for the searching. - * - * @type object - * @default {} - * - * @example - * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { - * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); - * } - */ - search: {}, - - - /** - * Type based ordering. - * - * The column type tells DataTables what ordering to apply to the table - * when a column is sorted upon. The order for each type that is defined, - * is defined by the functions available in this object. - * - * Each ordering option can be described by three properties added to - * this object: - * - * * `{type}-pre` - Pre-formatting function - * * `{type}-asc` - Ascending order function - * * `{type}-desc` - Descending order function - * - * All three can be used together, only `{type}-pre` or only - * `{type}-asc` and `{type}-desc` together. It is generally recommended - * that only `{type}-pre` is used, as this provides the optimal - * implementation in terms of speed, although the others are provided - * for compatibility with existing Javascript sort functions. - * - * `{type}-pre`: Functions defined take a single parameter: - * - * 1. `{*}` Data from the column cell to be prepared for ordering - * - * And return: - * - * * `{*}` Data to be sorted upon - * - * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort - * functions, taking two parameters: - * - * 1. `{*}` Data to compare to the second parameter - * 2. `{*}` Data to compare to the first parameter - * - * And returning: - * - * * `{*}` Ordering match: <0 if first parameter should be sorted lower - * than the second parameter, ===0 if the two parameters are equal and - * >0 if the first parameter should be sorted height than the second - * parameter. - * - * @type object - * @default {} - * - * @example - * // Numeric ordering of formatted numbers with a pre-formatter - * $.extend( $.fn.dataTable.ext.type.order, { - * "string-pre": function(x) { - * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); - * return parseFloat( a ); - * } - * } ); - * - * @example - * // Case-sensitive string ordering, with no pre-formatting method - * $.extend( $.fn.dataTable.ext.order, { - * "string-case-asc": function(x,y) { - * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); - * }, - * "string-case-desc": function(x,y) { - * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); - * } - * } ); - */ - order: {} - }, - - /** - * Unique DataTables instance counter - * - * @type int - * @private - */ - _unique: 0, - - - // - // Depreciated - // The following properties are retained for backwards compatiblity only. - // The should not be used in new projects and will be removed in a future - // version - // - - /** - * Version check function. - * @type function - * @depreciated Since 1.10 - */ - fnVersionCheck: DataTable.fnVersionCheck, - - - /** - * Index for what 'this' index API functions should use - * @type int - * @deprecated Since v1.10 - */ - iApiIndex: 0, - - - /** - * jQuery UI class container - * @type object - * @deprecated Since v1.10 - */ - oJUIClasses: {}, - - - /** - * Software version - * @type string - * @deprecated Since v1.10 - */ - sVersion: DataTable.version - }; - - - // - // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts - // - $.extend( _ext, { - afnFiltering: _ext.search, - aTypes: _ext.type.detect, - ofnSearch: _ext.type.search, - oSort: _ext.type.order, - afnSortData: _ext.order, - aoFeatures: _ext.feature, - oApi: _ext.internal, - oStdClasses: _ext.classes, - oPagination: _ext.pager - } ); - - - $.extend( DataTable.ext.classes, { - "sTable": "dataTable", - "sNoFooter": "no-footer", - - /* Paging buttons */ - "sPageButton": "paginate_button", - "sPageButtonActive": "current", - "sPageButtonDisabled": "disabled", - - /* Striping classes */ - "sStripeOdd": "odd", - "sStripeEven": "even", - - /* Empty row */ - "sRowEmpty": "dataTables_empty", - - /* Features */ - "sWrapper": "dataTables_wrapper", - "sFilter": "dataTables_filter", - "sInfo": "dataTables_info", - "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ - "sLength": "dataTables_length", - "sProcessing": "dataTables_processing", - - /* Sorting */ - "sSortAsc": "sorting_asc", - "sSortDesc": "sorting_desc", - "sSortable": "sorting", /* Sortable in both directions */ - "sSortableAsc": "sorting_asc_disabled", - "sSortableDesc": "sorting_desc_disabled", - "sSortableNone": "sorting_disabled", - "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ - - /* Filtering */ - "sFilterInput": "", - - /* Page length */ - "sLengthSelect": "", - - /* Scrolling */ - "sScrollWrapper": "dataTables_scroll", - "sScrollHead": "dataTables_scrollHead", - "sScrollHeadInner": "dataTables_scrollHeadInner", - "sScrollBody": "dataTables_scrollBody", - "sScrollFoot": "dataTables_scrollFoot", - "sScrollFootInner": "dataTables_scrollFootInner", - - /* Misc */ - "sHeaderTH": "", - "sFooterTH": "", - - // Deprecated - "sSortJUIAsc": "", - "sSortJUIDesc": "", - "sSortJUI": "", - "sSortJUIAscAllowed": "", - "sSortJUIDescAllowed": "", - "sSortJUIWrapper": "", - "sSortIcon": "", - "sJUIHeader": "", - "sJUIFooter": "" - } ); - - - (function() { - - // Reused strings for better compression. Closure compiler appears to have a - // weird edge case where it is trying to expand strings rather than use the - // variable version. This results in about 200 bytes being added, for very - // little preference benefit since it this run on script load only. - var _empty = ''; - _empty = ''; - - var _stateDefault = _empty + 'ui-state-default'; - var _sortIcon = _empty + 'css_right ui-icon ui-icon-'; - var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix'; - - $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, { - /* Full numbers paging buttons */ - "sPageButton": "fg-button ui-button "+_stateDefault, - "sPageButtonActive": "ui-state-disabled", - "sPageButtonDisabled": "ui-state-disabled", - - /* Features */ - "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ - "ui-buttonset-multi paging_", /* Note that the type is postfixed */ - - /* Sorting */ - "sSortAsc": _stateDefault+" sorting_asc", - "sSortDesc": _stateDefault+" sorting_desc", - "sSortable": _stateDefault+" sorting", - "sSortableAsc": _stateDefault+" sorting_asc_disabled", - "sSortableDesc": _stateDefault+" sorting_desc_disabled", - "sSortableNone": _stateDefault+" sorting_disabled", - "sSortJUIAsc": _sortIcon+"triangle-1-n", - "sSortJUIDesc": _sortIcon+"triangle-1-s", - "sSortJUI": _sortIcon+"carat-2-n-s", - "sSortJUIAscAllowed": _sortIcon+"carat-1-n", - "sSortJUIDescAllowed": _sortIcon+"carat-1-s", - "sSortJUIWrapper": "DataTables_sort_wrapper", - "sSortIcon": "DataTables_sort_icon", - - /* Scrolling */ - "sScrollHead": "dataTables_scrollHead "+_stateDefault, - "sScrollFoot": "dataTables_scrollFoot "+_stateDefault, - - /* Misc */ - "sHeaderTH": _stateDefault, - "sFooterTH": _stateDefault, - "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr", - "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br" - } ); - - }()); - - - - var extPagination = DataTable.ext.pager; - - function _numbers ( page, pages ) { - var - numbers = [], - buttons = extPagination.numbers_length, - half = Math.floor( buttons / 2 ), - i = 1; - - if ( pages <= buttons ) { - numbers = _range( 0, pages ); - } - else if ( page <= half ) { - numbers = _range( 0, buttons-2 ); - numbers.push( 'ellipsis' ); - numbers.push( pages-1 ); - } - else if ( page >= pages - 1 - half ) { - numbers = _range( pages-(buttons-2), pages ); - numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6 - numbers.splice( 0, 0, 0 ); - } - else { - numbers = _range( page-half+2, page+half-1 ); - numbers.push( 'ellipsis' ); - numbers.push( pages-1 ); - numbers.splice( 0, 0, 'ellipsis' ); - numbers.splice( 0, 0, 0 ); - } - - numbers.DT_el = 'span'; - return numbers; - } - - - $.extend( extPagination, { - simple: function ( page, pages ) { - return [ 'previous', 'next' ]; - }, - - full: function ( page, pages ) { - return [ 'first', 'previous', 'next', 'last' ]; - }, - - numbers: function ( page, pages ) { - return [ _numbers(page, pages) ]; - }, - - simple_numbers: function ( page, pages ) { - return [ 'previous', _numbers(page, pages), 'next' ]; - }, - - full_numbers: function ( page, pages ) { - return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ]; - }, - - // For testing and plug-ins to use - _numbers: _numbers, - - // Number of number buttons (including ellipsis) to show. _Must be odd!_ - numbers_length: 7 - } ); - - - $.extend( true, DataTable.ext.renderer, { - pageButton: { - _: function ( settings, host, idx, buttons, page, pages ) { - var classes = settings.oClasses; - var lang = settings.oLanguage.oPaginate; - var aria = settings.oLanguage.oAria.paginate || {}; - var btnDisplay, btnClass, counter=0; - - var attach = function( container, buttons ) { - var i, ien, node, button; - var clickHandler = function ( e ) { - _fnPageChange( settings, e.data.action, true ); - }; - - for ( i=0, ien=buttons.length ; i' ) - .appendTo( container ); - attach( inner, button ); - } - else { - btnDisplay = null; - btnClass = ''; - - switch ( button ) { - case 'ellipsis': - container.append(''); - break; - - case 'first': - btnDisplay = lang.sFirst; - btnClass = button + (page > 0 ? - '' : ' '+classes.sPageButtonDisabled); - break; - - case 'previous': - btnDisplay = lang.sPrevious; - btnClass = button + (page > 0 ? - '' : ' '+classes.sPageButtonDisabled); - break; - - case 'next': - btnDisplay = lang.sNext; - btnClass = button + (page < pages-1 ? - '' : ' '+classes.sPageButtonDisabled); - break; - - case 'last': - btnDisplay = lang.sLast; - btnClass = button + (page < pages-1 ? - '' : ' '+classes.sPageButtonDisabled); - break; - - default: - btnDisplay = button + 1; - btnClass = page === button ? - classes.sPageButtonActive : ''; - break; - } - - if ( btnDisplay !== null ) { - node = $('', { - 'class': classes.sPageButton+' '+btnClass, - 'aria-controls': settings.sTableId, - 'aria-label': aria[ button ], - 'data-dt-idx': counter, - 'tabindex': settings.iTabIndex, - 'id': idx === 0 && typeof button === 'string' ? - settings.sTableId +'_'+ button : - null - } ) - .html( btnDisplay ) - .appendTo( container ); - - _fnBindAction( - node, {action: button}, clickHandler - ); - - counter++; - } - } - } - }; - - // IE9 throws an 'unknown error' if document.activeElement is used - // inside an iframe or frame. Try / catch the error. Not good for - // accessibility, but neither are frames. - var activeEl; - - try { - // Because this approach is destroying and recreating the paging - // elements, focus is lost on the select button which is bad for - // accessibility. So we want to restore focus once the draw has - // completed - activeEl = $(host).find(document.activeElement).data('dt-idx'); - } - catch (e) {} - - attach( $(host).empty(), buttons ); - - if ( activeEl ) { - $(host).find( '[data-dt-idx='+activeEl+']' ).focus(); - } - } - } - } ); - - - - // Built in type detection. See model.ext.aTypes for information about - // what is required from this methods. - $.extend( DataTable.ext.type.detect, [ - // Plain numbers - first since V8 detects some plain numbers as dates - // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...). - function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal ) ? 'num'+decimal : null; - }, - - // Dates (only those recognised by the browser's Date.parse) - function ( d, settings ) - { - // V8 will remove any unknown characters at the start and end of the - // expression, leading to false matches such as `$245.12` or `10%` being - // a valid date. See forum thread 18941 for detail. - if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) { - return null; - } - var parsed = Date.parse(d); - return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; - }, - - // Formatted numbers - function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null; - }, - - // HTML numeric - function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null; - }, - - // HTML numeric, formatted - function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null; - }, - - // HTML (this is strict checking - there must be html) - function ( d, settings ) - { - return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? - 'html' : null; - } - ] ); - - - - // Filter formatting functions. See model.ext.ofnSearch for information about - // what is required from these methods. - // - // Note that additional search methods are added for the html numbers and - // html formatted numbers by `_addNumericSort()` when we know what the decimal - // place is - - - $.extend( DataTable.ext.type.search, { - html: function ( data ) { - return _empty(data) ? - data : - typeof data === 'string' ? - data - .replace( _re_new_lines, " " ) - .replace( _re_html, "" ) : - ''; - }, - - string: function ( data ) { - return _empty(data) ? - data : - typeof data === 'string' ? - data.replace( _re_new_lines, " " ) : - data; - } - } ); - - - - var __numericReplace = function ( d, decimalPlace, re1, re2 ) { - if ( d !== 0 && (!d || d === '-') ) { - return -Infinity; - } - - // If a decimal place other than `.` is used, it needs to be given to the - // function so we can detect it and replace with a `.` which is the only - // decimal place Javascript recognises - it is not locale aware. - if ( decimalPlace ) { - d = _numToDecimal( d, decimalPlace ); - } - - if ( d.replace ) { - if ( re1 ) { - d = d.replace( re1, '' ); - } - - if ( re2 ) { - d = d.replace( re2, '' ); - } - } - - return d * 1; - }; - - - // Add the numeric 'deformatting' functions for sorting and search. This is done - // in a function to provide an easy ability for the language options to add - // additional methods if a non-period decimal place is used. - function _addNumericSort ( decimalPlace ) { - $.each( - { - // Plain numbers - "num": function ( d ) { - return __numericReplace( d, decimalPlace ); - }, - - // Formatted numbers - "num-fmt": function ( d ) { - return __numericReplace( d, decimalPlace, _re_formatted_numeric ); - }, - - // HTML numeric - "html-num": function ( d ) { - return __numericReplace( d, decimalPlace, _re_html ); - }, - - // HTML numeric, formatted - "html-num-fmt": function ( d ) { - return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric ); - } - }, - function ( key, fn ) { - // Add the ordering method - _ext.type.order[ key+decimalPlace+'-pre' ] = fn; - - // For HTML types add a search formatter that will strip the HTML - if ( key.match(/^html\-/) ) { - _ext.type.search[ key+decimalPlace ] = _ext.type.search.html; - } - } - ); - } - - - // Default sort methods - $.extend( _ext.type.order, { - // Dates - "date-pre": function ( d ) { - return Date.parse( d ) || 0; - }, - - // html - "html-pre": function ( a ) { - return _empty(a) ? - '' : - a.replace ? - a.replace( /<.*?>/g, "" ).toLowerCase() : - a+''; - }, - - // string - "string-pre": function ( a ) { - // This is a little complex, but faster than always calling toString, - // http://jsperf.com/tostring-v-check - return _empty(a) ? - '' : - typeof a === 'string' ? - a.toLowerCase() : - ! a.toString ? - '' : - a.toString(); - }, - - // string-asc and -desc are retained only for compatibility with the old - // sort methods - "string-asc": function ( x, y ) { - return ((x < y) ? -1 : ((x > y) ? 1 : 0)); - }, - - "string-desc": function ( x, y ) { - return ((x < y) ? 1 : ((x > y) ? -1 : 0)); - } - } ); - - - // Numeric sorting types - order doesn't matter here - _addNumericSort( '' ); - - - $.extend( true, DataTable.ext.renderer, { - header: { - _: function ( settings, cell, column, classes ) { - // No additional mark-up required - // Attach a sort listener to update on sort - note that using the - // `DT` namespace will allow the event to be removed automatically - // on destroy, while the `dt` namespaced event is the one we are - // listening for - $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { - if ( settings !== ctx ) { // need to check this this is the host - return; // table, not a nested one - } - - var colIdx = column.idx; - - cell - .removeClass( - column.sSortingClass +' '+ - classes.sSortAsc +' '+ - classes.sSortDesc - ) - .addClass( columns[ colIdx ] == 'asc' ? - classes.sSortAsc : columns[ colIdx ] == 'desc' ? - classes.sSortDesc : - column.sSortingClass - ); - } ); - }, - - jqueryui: function ( settings, cell, column, classes ) { - $('
    ') - .addClass( classes.sSortJUIWrapper ) - .append( cell.contents() ) - .append( $('') - .addClass( classes.sSortIcon+' '+column.sSortingClassJUI ) - ) - .appendTo( cell ); - - // Attach a sort listener to update on sort - $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { - if ( settings !== ctx ) { - return; - } - - var colIdx = column.idx; - - cell - .removeClass( classes.sSortAsc +" "+classes.sSortDesc ) - .addClass( columns[ colIdx ] == 'asc' ? - classes.sSortAsc : columns[ colIdx ] == 'desc' ? - classes.sSortDesc : - column.sSortingClass - ); - - cell - .find( 'span.'+classes.sSortIcon ) - .removeClass( - classes.sSortJUIAsc +" "+ - classes.sSortJUIDesc +" "+ - classes.sSortJUI +" "+ - classes.sSortJUIAscAllowed +" "+ - classes.sSortJUIDescAllowed - ) - .addClass( columns[ colIdx ] == 'asc' ? - classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ? - classes.sSortJUIDesc : - column.sSortingClassJUI - ); - } ); - } - } - } ); - - /* - * Public helper functions. These aren't used internally by DataTables, or - * called by any of the options passed into DataTables, but they can be used - * externally by developers working with DataTables. They are helper functions - * to make working with DataTables a little bit easier. - */ - - var __htmlEscapeEntities = function ( d ) { - return typeof d === 'string' ? - d.replace(//g, '>').replace(/"/g, '"') : - d; - }; - - /** - * Helpers for `columns.render`. - * - * The options defined here can be used with the `columns.render` initialisation - * option to provide a display renderer. The following functions are defined: - * - * * `number` - Will format numeric data (defined by `columns.data`) for - * display, retaining the original unformatted data for sorting and filtering. - * It takes 5 parameters: - * * `string` - Thousands grouping separator - * * `string` - Decimal point indicator - * * `integer` - Number of decimal points to show - * * `string` (optional) - Prefix. - * * `string` (optional) - Postfix (/suffix). - * * `text` - Escape HTML to help prevent XSS attacks. It has no optional - * parameters. - * - * @example - * // Column definition using the number renderer - * { - * data: "salary", - * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) - * } - * - * @namespace - */ - DataTable.render = { - number: function ( thousands, decimal, precision, prefix, postfix ) { - return { - display: function ( d ) { - if ( typeof d !== 'number' && typeof d !== 'string' ) { - return d; - } - - var negative = d < 0 ? '-' : ''; - var flo = parseFloat( d ); - - // If NaN then there isn't much formatting that we can do - just - // return immediately, escaping any HTML (this was supposed to - // be a number after all) - if ( isNaN( flo ) ) { - return __htmlEscapeEntities( d ); - } - - d = Math.abs( flo ); - - var intPart = parseInt( d, 10 ); - var floatPart = precision ? - decimal+(d - intPart).toFixed( precision ).substring( 2 ): - ''; - - return negative + (prefix||'') + - intPart.toString().replace( - /\B(?=(\d{3})+(?!\d))/g, thousands - ) + - floatPart + - (postfix||''); - } - }; - }, - - text: function () { - return { - display: __htmlEscapeEntities - }; - } - }; - - - /* - * This is really a good bit rubbish this method of exposing the internal methods - * publicly... - To be fixed in 2.0 using methods on the prototype - */ - - - /** - * Create a wrapper function for exporting an internal functions to an external API. - * @param {string} fn API function name - * @returns {function} wrapped function - * @memberof DataTable#internal - */ - function _fnExternApiFunc (fn) - { - return function() { - var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat( - Array.prototype.slice.call(arguments) - ); - return DataTable.ext.internal[fn].apply( this, args ); - }; - } - - - /** - * Reference to internal functions for use by plug-in developers. Note that - * these methods are references to internal functions and are considered to be - * private. If you use these methods, be aware that they are liable to change - * between versions. - * @namespace - */ - $.extend( DataTable.ext.internal, { - _fnExternApiFunc: _fnExternApiFunc, - _fnBuildAjax: _fnBuildAjax, - _fnAjaxUpdate: _fnAjaxUpdate, - _fnAjaxParameters: _fnAjaxParameters, - _fnAjaxUpdateDraw: _fnAjaxUpdateDraw, - _fnAjaxDataSrc: _fnAjaxDataSrc, - _fnAddColumn: _fnAddColumn, - _fnColumnOptions: _fnColumnOptions, - _fnAdjustColumnSizing: _fnAdjustColumnSizing, - _fnVisibleToColumnIndex: _fnVisibleToColumnIndex, - _fnColumnIndexToVisible: _fnColumnIndexToVisible, - _fnVisbleColumns: _fnVisbleColumns, - _fnGetColumns: _fnGetColumns, - _fnColumnTypes: _fnColumnTypes, - _fnApplyColumnDefs: _fnApplyColumnDefs, - _fnHungarianMap: _fnHungarianMap, - _fnCamelToHungarian: _fnCamelToHungarian, - _fnLanguageCompat: _fnLanguageCompat, - _fnBrowserDetect: _fnBrowserDetect, - _fnAddData: _fnAddData, - _fnAddTr: _fnAddTr, - _fnNodeToDataIndex: _fnNodeToDataIndex, - _fnNodeToColumnIndex: _fnNodeToColumnIndex, - _fnGetCellData: _fnGetCellData, - _fnSetCellData: _fnSetCellData, - _fnSplitObjNotation: _fnSplitObjNotation, - _fnGetObjectDataFn: _fnGetObjectDataFn, - _fnSetObjectDataFn: _fnSetObjectDataFn, - _fnGetDataMaster: _fnGetDataMaster, - _fnClearTable: _fnClearTable, - _fnDeleteIndex: _fnDeleteIndex, - _fnInvalidate: _fnInvalidate, - _fnGetRowElements: _fnGetRowElements, - _fnCreateTr: _fnCreateTr, - _fnBuildHead: _fnBuildHead, - _fnDrawHead: _fnDrawHead, - _fnDraw: _fnDraw, - _fnReDraw: _fnReDraw, - _fnAddOptionsHtml: _fnAddOptionsHtml, - _fnDetectHeader: _fnDetectHeader, - _fnGetUniqueThs: _fnGetUniqueThs, - _fnFeatureHtmlFilter: _fnFeatureHtmlFilter, - _fnFilterComplete: _fnFilterComplete, - _fnFilterCustom: _fnFilterCustom, - _fnFilterColumn: _fnFilterColumn, - _fnFilter: _fnFilter, - _fnFilterCreateSearch: _fnFilterCreateSearch, - _fnEscapeRegex: _fnEscapeRegex, - _fnFilterData: _fnFilterData, - _fnFeatureHtmlInfo: _fnFeatureHtmlInfo, - _fnUpdateInfo: _fnUpdateInfo, - _fnInfoMacros: _fnInfoMacros, - _fnInitialise: _fnInitialise, - _fnInitComplete: _fnInitComplete, - _fnLengthChange: _fnLengthChange, - _fnFeatureHtmlLength: _fnFeatureHtmlLength, - _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate, - _fnPageChange: _fnPageChange, - _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing, - _fnProcessingDisplay: _fnProcessingDisplay, - _fnFeatureHtmlTable: _fnFeatureHtmlTable, - _fnScrollDraw: _fnScrollDraw, - _fnApplyToChildren: _fnApplyToChildren, - _fnCalculateColumnWidths: _fnCalculateColumnWidths, - _fnThrottle: _fnThrottle, - _fnConvertToWidth: _fnConvertToWidth, - _fnGetWidestNode: _fnGetWidestNode, - _fnGetMaxLenString: _fnGetMaxLenString, - _fnStringToCss: _fnStringToCss, - _fnSortFlatten: _fnSortFlatten, - _fnSort: _fnSort, - _fnSortAria: _fnSortAria, - _fnSortListener: _fnSortListener, - _fnSortAttachListener: _fnSortAttachListener, - _fnSortingClasses: _fnSortingClasses, - _fnSortData: _fnSortData, - _fnSaveState: _fnSaveState, - _fnLoadState: _fnLoadState, - _fnSettingsFromNode: _fnSettingsFromNode, - _fnLog: _fnLog, - _fnMap: _fnMap, - _fnBindAction: _fnBindAction, - _fnCallbackReg: _fnCallbackReg, - _fnCallbackFire: _fnCallbackFire, - _fnLengthOverflow: _fnLengthOverflow, - _fnRenderer: _fnRenderer, - _fnDataSource: _fnDataSource, - _fnRowAttributes: _fnRowAttributes, - _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant - // in 1.10, so this dead-end function is - // added to prevent errors - } ); - - - // jQuery access - $.fn.dataTable = DataTable; - - // Provide access to the host jQuery object (circular reference) - DataTable.$ = $; - - // Legacy aliases - $.fn.dataTableSettings = DataTable.settings; - $.fn.dataTableExt = DataTable.ext; - - // With a capital `D` we return a DataTables API instance rather than a - // jQuery object - $.fn.DataTable = function ( opts ) { - return $(this).dataTable( opts ).api(); - }; - - // All properties that are available to $.fn.dataTable should also be - // available on $.fn.DataTable - $.each( DataTable, function ( prop, val ) { - $.fn.DataTable[ prop ] = val; - } ); - - - // Information about events fired by DataTables - for documentation. - /** - * Draw event, fired whenever the table is redrawn on the page, at the same - * point as fnDrawCallback. This may be useful for binding events or - * performing calculations when the table is altered at all. - * @name DataTable#draw.dt - * @event - * @param {event} e jQuery event object - * @param {object} o DataTables settings object {@link DataTable.models.oSettings} - */ - - /** - * Search event, fired when the searching applied to the table (using the - * built-in global search, or column filters) is altered. - * @name DataTable#search.dt - * @event - * @param {event} e jQuery event object - * @param {object} o DataTables settings object {@link DataTable.models.oSettings} - */ - - /** - * Page change event, fired when the paging of the table is altered. - * @name DataTable#page.dt - * @event - * @param {event} e jQuery event object - * @param {object} o DataTables settings object {@link DataTable.models.oSettings} - */ - - /** - * Order event, fired when the ordering applied to the table is altered. - * @name DataTable#order.dt - * @event - * @param {event} e jQuery event object - * @param {object} o DataTables settings object {@link DataTable.models.oSettings} - */ - - /** - * DataTables initialisation complete event, fired when the table is fully - * drawn, including Ajax data loaded, if Ajax data is required. - * @name DataTable#init.dt - * @event - * @param {event} e jQuery event object - * @param {object} oSettings DataTables settings object - * @param {object} json The JSON object request from the server - only - * present if client-side Ajax sourced data is used - */ - - /** - * State save event, fired when the table has changed state a new state save - * is required. This event allows modification of the state saving object - * prior to actually doing the save, including addition or other state - * properties (for plug-ins) or modification of a DataTables core property. - * @name DataTable#stateSaveParams.dt - * @event - * @param {event} e jQuery event object - * @param {object} oSettings DataTables settings object - * @param {object} json The state information to be saved - */ - - /** - * State load event, fired when the table is loading state from the stored - * data, but prior to the settings object being modified by the saved state - * - allowing modification of the saved state is required or loading of - * state for a plug-in. - * @name DataTable#stateLoadParams.dt - * @event - * @param {event} e jQuery event object - * @param {object} oSettings DataTables settings object - * @param {object} json The saved state information - */ - - /** - * State loaded event, fired when state has been loaded from stored data and - * the settings object has been modified by the loaded data. - * @name DataTable#stateLoaded.dt - * @event - * @param {event} e jQuery event object - * @param {object} oSettings DataTables settings object - * @param {object} json The saved state information - */ - - /** - * Processing event, fired when DataTables is doing some kind of processing - * (be it, order, searcg or anything else). It can be used to indicate to - * the end user that there is something happening, or that something has - * finished. - * @name DataTable#processing.dt - * @event - * @param {event} e jQuery event object - * @param {object} oSettings DataTables settings object - * @param {boolean} bShow Flag for if DataTables is doing processing or not - */ - - /** - * Ajax (XHR) event, fired whenever an Ajax request is completed from a - * request to made to the server for new data. This event is called before - * DataTables processed the returned data, so it can also be used to pre- - * process the data returned from the server, if needed. - * - * Note that this trigger is called in `fnServerData`, if you override - * `fnServerData` and which to use this event, you need to trigger it in you - * success function. - * @name DataTable#xhr.dt - * @event - * @param {event} e jQuery event object - * @param {object} o DataTables settings object {@link DataTable.models.oSettings} - * @param {object} json JSON returned from the server - * - * @example - * // Use a custom property returned from the server in another DOM element - * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { - * $('#status').html( json.status ); - * } ); - * - * @example - * // Pre-process the data returned from the server - * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { - * for ( var i=0, ien=json.aaData.length ; i tr > th, -table.dataTable thead > tr > td { - padding-right: 30px; -} -table.dataTable thead > tr > th:active, -table.dataTable thead > tr > td:active { - outline: none; -} -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: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 { - position: absolute; - bottom: 8px; - right: 8px; - display: block; - font-family: 'Glyphicons Halflings'; - opacity: 0.5; -} -table.dataTable thead .sorting:after { - opacity: 0.2; - content: "\e150"; - /* sort */ -} -table.dataTable thead .sorting_asc:after { - content: "\e155"; - /* sort-by-attributes */ -} -table.dataTable thead .sorting_desc:after { - content: "\e156"; - /* sort-by-attributes-alt */ -} -table.dataTable thead .sorting_asc_disabled:after, -table.dataTable thead .sorting_desc_disabled:after { - color: #eee; -} - -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: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 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; - } -} -table.dataTable.table-condensed > thead > tr > th { - padding-right: 20px; -} -table.dataTable.table-condensed .sorting:after, -table.dataTable.table-condensed .sorting_asc:after, -table.dataTable.table-condensed .sorting_desc:after { - top: 6px; - right: 6px; -} - -table.table-bordered.dataTable { - border-collapse: separate !important; -} -table.table-bordered.dataTable th, -table.table-bordered.dataTable td { - border-left-width: 0; -} -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: 0; -} -table.table-bordered.dataTable tbody th, -table.table-bordered.dataTable tbody td { - border-bottom-width: 0; -} - -div.dataTables_scrollHead table.table-bordered { - border-bottom-width: 0; -} diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/css/dataTables.bootstrap.min.css b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/css/dataTables.bootstrap.min.css deleted file mode 100644 index f473ba1d2..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/css/dataTables.bootstrap.min.css +++ /dev/null @@ -1 +0,0 @@ -table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}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:75px;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:0.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:8px;white-space:nowrap}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}table.dataTable thead>tr>th,table.dataTable thead>tr>td{padding-right:30px}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}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: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{position:absolute;bottom:8px;right:8px;display:block;font-family:'Glyphicons Halflings';opacity:0.5}table.dataTable thead .sorting:after{opacity:0.2;content:"\e150"}table.dataTable thead .sorting_asc:after{content:"\e155"}table.dataTable thead .sorting_desc:after{content:"\e156"}table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{color:#eee}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: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 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}}table.dataTable.table-condensed>thead>tr>th{padding-right:20px}table.dataTable.table-condensed .sorting:after,table.dataTable.table-condensed .sorting_asc:after,table.dataTable.table-condensed .sorting_desc:after{top:6px;right:6px}table.table-bordered.dataTable{border-collapse:separate !important}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}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:0}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0} diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/Sorting icons.psd b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/Sorting icons.psd deleted file mode 100644 index 53b2e0685..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/Sorting icons.psd and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/favicon.ico b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/favicon.ico deleted file mode 100644 index 6eeaa2a0d..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/favicon.ico and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc.png b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc.png deleted file mode 100644 index e1ba61a80..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc_disabled.png b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc_disabled.png deleted file mode 100644 index fb11dfe24..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_asc_disabled.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_both.png b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_both.png deleted file mode 100644 index af5bc7c5a..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_both.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc.png b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc.png deleted file mode 100644 index 0e156deb5..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc_disabled.png b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc_disabled.png deleted file mode 100644 index c9fdd8a15..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/images/sort_desc_disabled.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js b/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js deleted file mode 100644 index cce2d6e7a..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js +++ /dev/null @@ -1,206 +0,0 @@ -/*! DataTables Bootstrap 3 integration - * ©2011-2014 SpryMedia Ltd - datatables.net/license - */ - -/** - * DataTables integration for Bootstrap 3. This requires Bootstrap 3 and - * DataTables 1.10 or newer. - * - * This file sets the defaults and adds options to DataTables to style its - * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap - * for further information. - */ -(function(window, document, undefined){ - -var factory = function( $, DataTable ) { -"use strict"; - - -/* Set the defaults for DataTables initialisation */ -$.extend( true, DataTable.defaults, { - dom: - "<'row'<'col-sm-6'l><'col-sm-6'f>>" + - "<'row'<'col-sm-12'tr>>" + - "<'row'<'col-sm-5'i><'col-sm-7'p>>", - renderer: 'bootstrap' -} ); - - -/* Default class modification */ -$.extend( DataTable.ext.classes, { - sWrapper: "dataTables_wrapper form-inline dt-bootstrap", - sFilterInput: "form-control input-sm", - sLengthSelect: "form-control input-sm" -} ); - - -/* Bootstrap paging button renderer */ -DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) { - var api = new DataTable.Api( settings ); - var classes = settings.oClasses; - var lang = settings.oLanguage.oPaginate; - var btnDisplay, btnClass, counter=0; - - var attach = function( container, buttons ) { - var i, ien, node, button; - var clickHandler = function ( e ) { - e.preventDefault(); - if ( !$(e.currentTarget).hasClass('disabled') ) { - api.page( e.data.action ).draw( 'page' ); - } - }; - - for ( i=0, ien=buttons.length ; i 0 ? - '' : ' disabled'); - break; - - case 'previous': - btnDisplay = lang.sPrevious; - btnClass = button + (page > 0 ? - '' : ' disabled'); - break; - - case 'next': - btnDisplay = lang.sNext; - btnClass = button + (page < pages-1 ? - '' : ' disabled'); - break; - - case 'last': - btnDisplay = lang.sLast; - btnClass = button + (page < pages-1 ? - '' : ' disabled'); - break; - - default: - btnDisplay = button + 1; - btnClass = page === button ? - 'active' : ''; - break; - } - - if ( btnDisplay ) { - node = $('
  • ', { - 'class': classes.sPageButton+' '+btnClass, - 'id': idx === 0 && typeof button === 'string' ? - settings.sTableId +'_'+ button : - null - } ) - .append( $('', { - 'href': '#', - 'aria-controls': settings.sTableId, - 'data-dt-idx': counter, - 'tabindex': settings.iTabIndex - } ) - .html( btnDisplay ) - ) - .appendTo( container ); - - settings.oApi._fnBindAction( - node, {action: button}, clickHandler - ); - - counter++; - } - } - } - }; - - // IE9 throws an 'unknown error' if document.activeElement is used - // inside an iframe or frame. - var activeEl; - - try { - // Because this approach is destroying and recreating the paging - // elements, focus is lost on the select button which is bad for - // accessibility. So we want to restore focus once the draw has - // completed - activeEl = $(host).find(document.activeElement).data('dt-idx'); - } - catch (e) {} - - attach( - $(host).empty().html('
  • @@ -1054,10 +1055,8 @@ -
    -
    - ${tablePlayerlist} -
    +
    +
    @@ -1162,7 +1161,8 @@ - + @@ -1171,8 +1171,8 @@ - - + + @@ -1482,11 +1482,6 @@