From f85363303a151c4331b8d4c16df9984a726e2e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roar=20Br=C3=A6nden?= Date: Mon, 15 Apr 2024 15:17:05 +0200 Subject: [PATCH 01/31] [GEOS-11331] OAuth2 can throw a " java.lang.RuntimeException: Never should reach this point" --- .../OpenIdConnectAuthenticationFilter.java | 61 ++++++++++++------- ...oServerPreAuthenticatedUserNameFilter.java | 27 +++++--- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java b/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java index 6c0381eb5ba..0a371f2c410 100644 --- a/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java +++ b/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java @@ -4,14 +4,10 @@ */ package org.geoserver.security.oauth2; -import static org.geoserver.security.oauth2.OpenIdConnectFilterConfig.OpenIdRoleSource.AccessToken; -import static org.geoserver.security.oauth2.OpenIdConnectFilterConfig.OpenIdRoleSource.IdToken; -import static org.geoserver.security.oauth2.OpenIdConnectFilterConfig.OpenIdRoleSource.MSGraphAPI; -import static org.geoserver.security.oauth2.OpenIdConnectFilterConfig.OpenIdRoleSource.UserInfo; - import com.jayway.jsonpath.JsonPath; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.Collections; @@ -19,6 +15,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,6 +26,7 @@ import org.geoserver.security.filter.GeoServerLogoutFilter; import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.impl.RoleCalculator; +import org.geoserver.security.oauth2.OpenIdConnectFilterConfig.OpenIdRoleSource; import org.geoserver.security.oauth2.bearer.TokenValidator; import org.geoserver.security.oauth2.pkce.PKCERequestEnhancer; import org.geoserver.security.oauth2.services.OpenIdConnectTokenServices; @@ -149,31 +147,52 @@ protected String getPreAuthenticatedPrincipal(HttpServletRequest req, HttpServle protected Collection getRoles(HttpServletRequest request, String principal) throws IOException { RoleSource rs = getRoleSource(); + if (rs == null) { + LOGGER.log( + Level.WARNING, + "OIDC: None of the supported token claims [{}] have been set for delivering roles.", + Arrays.stream(OpenIdRoleSource.values()) + .map(v -> v.toString()) + .collect(Collectors.joining(", "))); + + return null; + } + if (!(rs instanceof OpenIdRoleSource)) { + super.getRoles(request, principal); + } + OpenIdRoleSource oirs = (OpenIdRoleSource) getRoleSource(); if (filterConfig.isAllowUnSecureLogging()) { String rolesAttributePath = ((OpenIdConnectFilterConfig) this.filterConfig).getTokenRolesClaim(); LOGGER.log( Level.FINE, - "OIDC: Getting Roles from " + rs + ", location=" + rolesAttributePath); + "OIDC: Getting Roles from {0}, location={1}", + new Object[] {oirs, rolesAttributePath}); } Collection result = null; - - if (AccessToken.equals(rs)) { - result = - getRolesFromToken( - (String) - request.getAttribute( - OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE)); - } else if (IdToken.equals(rs)) { - result = getRolesFromToken((String) request.getAttribute(ID_TOKEN_VALUE)); - } else if (UserInfo.equals(rs)) { - result = getRolesFromUserInfo(request); - } else if (MSGraphAPI.equals(rs)) { - result = getRolesFromMSGraphAPI(request); - } else { - result = super.getRoles(request, principal); + switch (oirs) { + case AccessToken: + result = + getRolesFromToken( + (String) + request.getAttribute( + OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE)); + break; + case IdToken: + result = getRolesFromToken((String) request.getAttribute(ID_TOKEN_VALUE)); + break; + case UserInfo: + result = getRolesFromUserInfo(request); + break; + case MSGraphAPI: + result = getRolesFromMSGraphAPI(request); + break; + default: + LOGGER.log(Level.FINE, "OIDC: Unknown OpenIdRoleSource = {0}", oirs); + result = super.getRoles(request, principal); } + if (filterConfig.isAllowUnSecureLogging()) { if (result == null) { LOGGER.log(Level.FINE, "OIDC: roles returned null (unexpected)"); diff --git a/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java b/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java index 31da21dd3f4..695b0a1a3fd 100644 --- a/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java +++ b/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java @@ -146,20 +146,29 @@ protected Collection getRoles(HttpServletRequest request, String throws IOException { Collection roles; - if (PreAuthenticatedUserNameRoleSource.RoleService.equals(getRoleSource())) { - roles = getRolesFromRoleService(request, principal); - } else if (PreAuthenticatedUserNameRoleSource.UserGroupService.equals(getRoleSource())) { - roles = getRolesFromUserGroupService(request, principal); - } else if (PreAuthenticatedUserNameRoleSource.Header.equals(getRoleSource())) { - roles = getRolesFromHttpAttribute(request, principal); - } else { - throw new RuntimeException("Never should reach this point"); + PreAuthenticatedUserNameRoleSource aurs = + (PreAuthenticatedUserNameRoleSource) getRoleSource(); + switch (aurs) { + case RoleService: + roles = getRolesFromRoleService(request, principal); + break; + case UserGroupService: + roles = getRolesFromUserGroupService(request, principal); + break; + case Header: + roles = getRolesFromHttpAttribute(request, principal); + break; + default: + throw new RuntimeException( + "Couldn't determine roles based on the specified role source [" + + aurs + + "]."); } LOGGER.log( Level.FINE, "Got roles {0} from {1} for principal {2}", - new Object[] {roles, getRoleSource(), principal}); + new Object[] {roles, aurs, principal}); return roles; } From fe995743af27f56b8f8cb49f225d9695334672a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roar=20Br=C3=A6nden?= Date: Mon, 15 Apr 2024 19:32:04 +0200 Subject: [PATCH 02/31] Revert switch usage --- ...oServerPreAuthenticatedUserNameFilter.java | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java b/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java index 695b0a1a3fd..b55a54bbc42 100644 --- a/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java +++ b/src/main/src/main/java/org/geoserver/security/filter/GeoServerPreAuthenticatedUserNameFilter.java @@ -146,29 +146,23 @@ protected Collection getRoles(HttpServletRequest request, String throws IOException { Collection roles; - PreAuthenticatedUserNameRoleSource aurs = - (PreAuthenticatedUserNameRoleSource) getRoleSource(); - switch (aurs) { - case RoleService: - roles = getRolesFromRoleService(request, principal); - break; - case UserGroupService: - roles = getRolesFromUserGroupService(request, principal); - break; - case Header: - roles = getRolesFromHttpAttribute(request, principal); - break; - default: - throw new RuntimeException( - "Couldn't determine roles based on the specified role source [" - + aurs - + "]."); + RoleSource rs = getRoleSource(); + + if (PreAuthenticatedUserNameRoleSource.RoleService.equals(rs)) { + roles = getRolesFromRoleService(request, principal); + } else if (PreAuthenticatedUserNameRoleSource.UserGroupService.equals(rs)) { + roles = getRolesFromUserGroupService(request, principal); + } else if (PreAuthenticatedUserNameRoleSource.Header.equals(rs)) { + roles = getRolesFromHttpAttribute(request, principal); + } else { + throw new RuntimeException( + "Couldn't determine roles based on the specified role source [" + rs + "]."); } LOGGER.log( Level.FINE, "Got roles {0} from {1} for principal {2}", - new Object[] {roles, aurs, principal}); + new Object[] {roles, rs, principal}); return roles; } From 96d6aef9b75110c982dda899b4f0d9ac89a6c10c Mon Sep 17 00:00:00 2001 From: Alessio Fabiani Date: Fri, 19 Apr 2024 14:59:48 +0200 Subject: [PATCH 03/31] Update OpenIdConnectAuthenticationFilter.java Avoid here to invoke the `getRoleSource()` method again --- .../security/oauth2/OpenIdConnectAuthenticationFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java b/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java index 0a371f2c410..e69b5ff9270 100644 --- a/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java +++ b/src/community/security/oauth2-openid-connect/oauth2-openid-connect-core/src/main/java/org/geoserver/security/oauth2/OpenIdConnectAuthenticationFilter.java @@ -160,7 +160,7 @@ protected Collection getRoles(HttpServletRequest request, String if (!(rs instanceof OpenIdRoleSource)) { super.getRoles(request, principal); } - OpenIdRoleSource oirs = (OpenIdRoleSource) getRoleSource(); + OpenIdRoleSource oirs = (OpenIdRoleSource) rs; if (filterConfig.isAllowUnSecureLogging()) { String rolesAttributePath = From 72eae5c319a874886551e221bd44a6c5a1c75e35 Mon Sep 17 00:00:00 2001 From: Joel M Date: Fri, 19 Apr 2024 16:49:44 -0400 Subject: [PATCH 04/31] Update index.rst added missing step in Creating a Data Store section --- doc/en/user/source/gettingstarted/postgis-quickstart/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/user/source/gettingstarted/postgis-quickstart/index.rst b/doc/en/user/source/gettingstarted/postgis-quickstart/index.rst index e4d656a7693..7067980065b 100644 --- a/doc/en/user/source/gettingstarted/postgis-quickstart/index.rst +++ b/doc/en/user/source/gettingstarted/postgis-quickstart/index.rst @@ -68,6 +68,8 @@ Creating a store Once the workspace is created, we are ready to add a new store. The store tells GeoServer how to connect to the database. #. Navigate to :menuselection:`Data-->Stores`. + +#. Click on ``Add new Store``. #. You should see a list of stores, including the type of store and the workspace that the store belongs to. From ea1cfe5bfed3f0f8cc9bf2973fee6aec5a439780 Mon Sep 17 00:00:00 2001 From: Peter Smythe Date: Mon, 22 Apr 2024 17:07:49 +0200 Subject: [PATCH 05/31] Update config.rst (#7566) Spelling --- doc/en/user/source/production/config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/user/source/production/config.rst b/doc/en/user/source/production/config.rst index 0941d928a69..9f48cce32bf 100644 --- a/doc/en/user/source/production/config.rst +++ b/doc/en/user/source/production/config.rst @@ -26,7 +26,7 @@ Suggestions: This message is be shown to visitors at the top of welcome page. The contact details and organisation information are included in the welcome page, and used to describe each web service in the capabilities documents. -* When setting up a workspace you can provide more more detailed service metadata and contact information. +* When setting up a workspace you can provide more detailed service metadata and contact information. * Serve your data with your own namespace (and provide a correct URI) * Remove default layers (such as ``topp:states``) @@ -358,7 +358,7 @@ GeoServer provides a number of facilities to control external entity resolution: Access is provided to the proxy base url from global settings. Access to local `file` references remains restricted. -* To allow all `http` and `https` entity resolution ise `*` wildcard:: +* To allow all `http` and `https` entity resolution use `*` wildcard:: -DENTITY_RESOLUTION_ALLOWLIST=* From 4d1191be1bfd9b4fc78d2ef47c04a77e83b77d32 Mon Sep 17 00:00:00 2001 From: Mark Prins <1165786+mprins@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:36:04 +0200 Subject: [PATCH 06/31] [GEOS-11374] Upgrade spring.version from 5.3.33 to 5.3.34 (#7567) --- src/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pom.xml b/src/pom.xml index 61289d929c5..d48fe9a2495 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -92,7 +92,7 @@ 32-SNAPSHOT 1.26-SNAPSHOT 1.19.0 - 5.3.33 + 5.3.34 5.7.12 5.5.18 2.5.2.RELEASE From 8c4e86804931be9d498e5461c81d426babe5b7bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:29:56 -0700 Subject: [PATCH 07/31] Bump net.minidev:json-smart from 2.4.7 to 2.4.9 in /src/community/jwt-headers (#7534) * Bump net.minidev:json-smart in /src/community/jwt-headers Bumps [net.minidev:json-smart](https://github.com/netplex/json-smart-v2) from 2.4.7 to 2.4.9. - [Release notes](https://github.com/netplex/json-smart-v2/releases) - [Commits](https://github.com/netplex/json-smart-v2/commits/2.4.9) --- updated-dependencies: - dependency-name: net.minidev:json-smart dependency-type: direct:production ... Signed-off-by: dependabot[bot] * update dependabot to PR to remove all the dependencies from the root project * change nimbus-jose-jwt to provided so GeoServer or GeoNetwork can provide * change name of jwt headers .zip to end in -plugin.zip as required by jenkins to publish --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: david.blasby --- .../jwt-headers/jwt-headers-util/pom.xml | 2 +- src/community/jwt-headers/pom.xml | 30 ++++--------------- src/community/release/ext-jwt-headers.xml | 2 +- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/community/jwt-headers/jwt-headers-util/pom.xml b/src/community/jwt-headers/jwt-headers-util/pom.xml index c3be1d031ef..204a97491a2 100644 --- a/src/community/jwt-headers/jwt-headers-util/pom.xml +++ b/src/community/jwt-headers/jwt-headers-util/pom.xml @@ -38,7 +38,7 @@ com.nimbusds nimbus-jose-jwt 9.37.3 - compile + provided com.jayway.jsonpath diff --git a/src/community/jwt-headers/pom.xml b/src/community/jwt-headers/pom.xml index f4ef941f428..beb0dc7c960 100644 --- a/src/community/jwt-headers/pom.xml +++ b/src/community/jwt-headers/pom.xml @@ -24,31 +24,11 @@ gs-jwt-headers - - com.nimbusds - nimbus-jose-jwt - 9.37.3 - compile - - - net.minidev - json-smart - 2.4.7 - compile - - - com.jayway.jsonpath - json-path - - - com.google.guava - guava - - - org.mockito - mockito-core - test - + + + diff --git a/src/community/release/ext-jwt-headers.xml b/src/community/release/ext-jwt-headers.xml index acdb9f0e47e..3993774c5c3 100644 --- a/src/community/release/ext-jwt-headers.xml +++ b/src/community/release/ext-jwt-headers.xml @@ -1,5 +1,5 @@ - jwt-headers + jwt-headers-plugin zip From 664f968a71ea5af0239b0c578262c5677c9ed09b Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Mon, 15 Apr 2024 11:52:26 +0200 Subject: [PATCH 08/31] [GEOS-11311] Show a full stack trace in the JVM stack dump panel --- .../web/system/status/ConsoleInfoUtils.java | 88 +++++++++++- .../status/ConsoleInfoUtilsStackTest.java | 133 ++++++++++++++++++ 2 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/web/core/src/test/java/org/geoserver/web/system/status/ConsoleInfoUtilsStackTest.java diff --git a/src/web/core/src/main/java/org/geoserver/web/system/status/ConsoleInfoUtils.java b/src/web/core/src/main/java/org/geoserver/web/system/status/ConsoleInfoUtils.java index 7e8236802a6..bee5df10043 100644 --- a/src/web/core/src/main/java/org/geoserver/web/system/status/ConsoleInfoUtils.java +++ b/src/web/core/src/main/java/org/geoserver/web/system/status/ConsoleInfoUtils.java @@ -7,7 +7,9 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.util.Arrays; import java.util.Optional; @@ -28,10 +30,94 @@ static String getThreadsInfo(boolean lockedMonitors, boolean lockedSynchronizers return Arrays.stream( ManagementFactory.getThreadMXBean() .dumpAllThreads(lockedMonitors, lockedSynchronizers)) - .map(ThreadInfo::toString) + .map(t -> getThreadStackTraces(t)) .collect(Collectors.joining("\n")); } + static String getThreadStackTraces(ThreadInfo ti) { + StringBuilder sb = + new StringBuilder( + "\"" + + ti.getThreadName() + + "\"" + + (ti.isDaemon() ? " daemon" : "") + + " prio=" + + ti.getPriority() + + " Id=" + + ti.getThreadId() + + " " + + ti.getThreadState()); + if (ti.getLockName() != null) { + sb.append(" on " + ti.getLockName()); + } + if (ti.getLockOwnerName() != null) { + sb.append(" owned by \"" + ti.getLockOwnerName() + "\" Id=" + ti.getLockOwnerId()); + } + if (ti.isSuspended()) { + sb.append(" (suspended)"); + } + if (ti.isInNative()) { + sb.append(" (in native)"); + } + sb.append('\n'); + StackTraceElement[] stackTrace = ti.getStackTrace(); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement ste = stackTrace[i]; + sb.append("\tat " + formatStackTraceElement(ste)); + sb.append('\n'); + LockInfo lockInfo = ti.getLockInfo(); + if (i == 0 && lockInfo != null) { + Thread.State ts = ti.getThreadState(); + switch (ts) { + case BLOCKED: + sb.append("\t- blocked on " + lockInfo); + sb.append('\n'); + break; + case WAITING: + sb.append("\t- waiting on " + lockInfo); + sb.append('\n'); + break; + case TIMED_WAITING: + sb.append("\t- waiting on " + lockInfo); + sb.append('\n'); + break; + default: + } + } + + for (MonitorInfo mi : ti.getLockedMonitors()) { + if (mi.getLockedStackDepth() == i) { + sb.append("\t- locked " + mi); + sb.append('\n'); + } + } + } + + LockInfo[] locks = ti.getLockedSynchronizers(); + if (locks.length > 0) { + sb.append("\n\tNumber of locked synchronizers = " + locks.length); + sb.append('\n'); + for (LockInfo li : locks) { + sb.append("\t- " + li); + sb.append('\n'); + } + } + sb.append('\n'); + return sb.toString(); + } + + private static String formatStackTraceElement(StackTraceElement s) { + return s.getClassName() + + "." + + s.getMethodName() + + "(" + + (s.isNativeMethod() + ? "Native Method)" + : (s.getFileName() != null && s.getLineNumber() >= 0) + ? s.getFileName() + ":" + s.getLineNumber() + ")" + : ""); + } + /** * To retrieve the PID of the Java process is used the method * ManagementFactory.getRuntimeMXBean().getName() The returned name usually is the format diff --git a/src/web/core/src/test/java/org/geoserver/web/system/status/ConsoleInfoUtilsStackTest.java b/src/web/core/src/test/java/org/geoserver/web/system/status/ConsoleInfoUtilsStackTest.java new file mode 100644 index 00000000000..cea29a294b4 --- /dev/null +++ b/src/web/core/src/test/java/org/geoserver/web/system/status/ConsoleInfoUtilsStackTest.java @@ -0,0 +1,133 @@ +/* (c) 2024 Open Source Geospatial Foundation - all rights reserved + * This code is licensed under the GPL 2.0 license, available at the root + * application directory. + */ +package org.geoserver.web.system.status; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertNotEquals; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.awaitility.Awaitility; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +/** + * Test class to ensure the JVM console dumps all the stack elements for each thread. A bit ugly to + * have name 10 methods named "methodN" but it's an easy way to have a predictable output. + */ +public class ConsoleInfoUtilsStackTest { + + @Test + public void testThreadsInfo() throws Exception { + + // named thread pool to ensure that the thread name is included in the stack trace + ExecutorService es = Executors.newFixedThreadPool(1, r -> new Thread(r, "myTestThread")); + // submit with a latch that will lock the innermost call + CountDownLatch latch = new CountDownLatch(2); + es.submit(() -> method1(latch)); + // wait for the innermost call to happen (will reduce the latch down to 1) + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(latch::getCount, CoreMatchers.is(1L)); + // now grab the jstack with the thread in WAITING state and predictable content + String info = ConsoleInfoUtils.getThreadsInfo(true, true); + // release the latch to allow the other thread to finish + latch.countDown(); + es.shutdown(); + + // look for the lines of the stack trace of "myTestThread" + AtomicBoolean inBlock = new AtomicBoolean(false); + List testStack; + Predicate traceFinder = + l -> { + if (!inBlock.get()) { + if (l.startsWith("\"myTestThread\"")) inBlock.set(true); + + } else if (l.trim().isEmpty()) { + inBlock.set(false); + } + return inBlock.get(); + }; + testStack = info.lines().filter(traceFinder).collect(Collectors.toList()); + + // we used to only have 8 entries plus the title, now we should have at least 10 + title + assertThat(testStack.size(), greaterThan(11)); + assertThat( + testStack.get(0), allOf(containsString("myTestThread"), containsString("WAITING"))); + + // look for line stack line containing "method1(...)" to avoid changes in the stack trace + // due to e.g. upgrades of Junit + int idxFirst = -1; + for (int i = 0; i < testStack.size(); i++) { + if (testStack.get(i).contains("method1(")) { + idxFirst = i; + break; + } + } + assertNotEquals(-1, idxFirst); + // now check all 10 methods have been called and are in the tracek + for (int i = 1; i <= 10; i++) { + assertThat( + testStack.get(idxFirst + 1 - i), + containsString( + "at org.geoserver.web.system.status.ConsoleInfoUtilsStackTest.method" + + i)); + } + } + + private static void method1(CountDownLatch latch) { + method2(latch); + } + + private static void method2(CountDownLatch latch) { + method3(latch); + } + + private static void method3(CountDownLatch latch) { + method4(latch); + } + + private static void method4(CountDownLatch latch) { + method5(latch); + } + + private static void method5(CountDownLatch latch) { + method6(latch); + } + + private static void method6(CountDownLatch latch) { + method7(latch); + } + + private static void method7(CountDownLatch latch) { + method8(latch); + } + + private static void method8(CountDownLatch latch) { + method9(latch); + } + + private static void method9(CountDownLatch latch) { + method10(latch); + } + + private static void method10(CountDownLatch latch) { + // signal we reached the innermost method + latch.countDown(); + try { + // wait for the other thread to finish + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} From b68e7d4433a216d7d117f9dfe883a80c43027cef Mon Sep 17 00:00:00 2001 From: Steve Ikeoka Date: Wed, 24 Apr 2024 12:48:11 -0700 Subject: [PATCH 09/31] Fixed ogcapi-changeset unit test error on Windows --- .../ogcapi/v1/changeset/ChangesetIndexProvider.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/community/ogcapi/ogcapi-changeset/src/main/java/org/geoserver/ogcapi/v1/changeset/ChangesetIndexProvider.java b/src/community/ogcapi/ogcapi-changeset/src/main/java/org/geoserver/ogcapi/v1/changeset/ChangesetIndexProvider.java index 1458a1c87ab..236766a7cf9 100644 --- a/src/community/ogcapi/ogcapi-changeset/src/main/java/org/geoserver/ogcapi/v1/changeset/ChangesetIndexProvider.java +++ b/src/community/ogcapi/ogcapi-changeset/src/main/java/org/geoserver/ogcapi/v1/changeset/ChangesetIndexProvider.java @@ -48,6 +48,8 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component @@ -68,6 +70,11 @@ public ChangesetIndexProvider(GeoServerDataDirectory dd, Catalog catalog) throws catalog.addListener(new IndexCatalogListener()); } + @EventListener + public void handleContextClosedEvent(ContextClosedEvent event) { + this.checkpointIndex.dispose(); + } + DataStore getCheckpointDataStore(GeoServerDataDirectory dd) throws IOException { // see if there is a configuration file Resource properties = dd.get("changeset-store.properties"); From df6e7e406bb5f686777ebd43d60b536e0e466790 Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Wed, 24 Apr 2024 10:54:11 -0700 Subject: [PATCH 10/31] GSIP 224 Individual contributor clarification A quick update to reflect GeoServer commit access is granted to individuals (not a group or company). --- doc/en/developer/source/policies/committing.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/en/developer/source/policies/committing.rst b/doc/en/developer/source/policies/committing.rst index 3da398886f0..1709ad2e048 100644 --- a/doc/en/developer/source/policies/committing.rst +++ b/doc/en/developer/source/policies/committing.rst @@ -9,8 +9,15 @@ Getting commit access All contributors are asked to provide an assignment agreement for working on the project: * `corporate_contributor `__ + + Individual contributor agreement. + * `individual_contributor `__ + Corporate contributor agreement to authorize employees to work on project. May also be used as a software grant to donate software to the project. + +GeoServer is grateful that organizations of all shapes and sizes support our project with in-kind participation of their employees. Extending commit access is made to individuals directly based on their expertise demonstrated over time. + This agreement can be printed, signed, scanned and emailed to `info@osgeo.org `_ at Open Source Geospatial Foundation (OSGeo). `OSGeo `_ is the non-profit which holds the GeoServer codebase for the community. The `contribution licenses `_ are used by OSGeo projects seeking to assign copyright directly to the foundation. These licenses are directly derived from the Apache code contribution licenses (CLA V2.0 and CCLA v r190612). From 467186fe8f60f2f0dd247e95412593212facc4fc Mon Sep 17 00:00:00 2001 From: Peter Smythe Date: Thu, 25 Apr 2024 10:39:26 +0200 Subject: [PATCH 11/31] Update committing.rst The links and "headings" were swapped around. The order is now according to https://github.com/geoserver/geoserver/wiki/GSIP-224 --- doc/en/developer/source/policies/committing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/developer/source/policies/committing.rst b/doc/en/developer/source/policies/committing.rst index 1709ad2e048..d0e969cb3af 100644 --- a/doc/en/developer/source/policies/committing.rst +++ b/doc/en/developer/source/policies/committing.rst @@ -8,11 +8,11 @@ Getting commit access All contributors are asked to provide an assignment agreement for working on the project: -* `corporate_contributor `__ +* `individual_contributor `__ Individual contributor agreement. -* `individual_contributor `__ +* `corporate_contributor `__ Corporate contributor agreement to authorize employees to work on project. May also be used as a software grant to donate software to the project. From 21137d291571ab3a5b00cc1fe47f3629d618bc02 Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Fri, 26 Apr 2024 11:09:11 +0200 Subject: [PATCH 12/31] [GEOS-11376] Graduate Raster Attribute Table to extension --- doc/en/user/source/community/index.rst | 1 - doc/en/user/source/extensions/index.rst | 1 + .../rat/images/rat_map.png | Bin .../rat/images/rat_panel.png | Bin .../source/{community => extensions}/rat/index.rst | 0 .../{community => extensions}/rat/installing.rst | 4 +++- .../source/{community => extensions}/rat/using.rst | 0 src/community/pom.xml | 8 -------- src/community/release/pom.xml | 5 ----- src/extension/pom.xml | 8 ++++++++ src/{community => extension}/rat/pom.xml | 4 ++-- .../main/java/org/geoserver/rat/CoverageRATs.java | 0 .../org/geoserver/rat/RasterAttributeTable.java | 0 .../java/org/geoserver/rat/rest/PAMController.java | 0 .../rat/web/RasterAttributeTableConfig.html | 0 .../rat/web/RasterAttributeTableConfig.java | 0 .../java/org/geoserver/rat/web/RowProvider.java | 0 .../main/resources/GeoServerApplication.properties | 0 .../rat/src/main/resources/applicationContext.xml | 0 .../org/geoserver/rat/RasterAttributeTableTest.java | 0 .../org/geoserver/rat/rest/PAMControllerTest.java | 0 .../rat/web/RasterAttributeTableConfigTest.java | 0 .../src/test/resources/org/geoserver/rat/norat.xml | 0 .../resources/org/geoserver/rat/rangeColors.xml | 0 .../resources/org/geoserver/rat/rangeNoColor.xml | 0 .../resources/org/geoserver/rat/valueColors.xml | 0 .../resources/org/geoserver/rat/valueNoColors.xml | 0 .../test/resources/org/geoserver/rat/web/rat.tiff | Bin .../org/geoserver/rat/web/rat.tiff.aux.xml | 0 src/pom.xml | 1 + src/{community => }/release/ext-rat.xml | 0 src/release/pom.xml | 5 +++++ src/web/app/pom.xml | 2 +- 33 files changed, 21 insertions(+), 18 deletions(-) rename doc/en/user/source/{community => extensions}/rat/images/rat_map.png (100%) rename doc/en/user/source/{community => extensions}/rat/images/rat_panel.png (100%) rename doc/en/user/source/{community => extensions}/rat/index.rst (100%) rename doc/en/user/source/{community => extensions}/rat/installing.rst (52%) rename doc/en/user/source/{community => extensions}/rat/using.rst (100%) rename src/{community => extension}/rat/pom.xml (95%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/CoverageRATs.java (100%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/RasterAttributeTable.java (100%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/rest/PAMController.java (100%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.html (100%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.java (100%) rename src/{community => extension}/rat/src/main/java/org/geoserver/rat/web/RowProvider.java (100%) rename src/{community => extension}/rat/src/main/resources/GeoServerApplication.properties (100%) rename src/{community => extension}/rat/src/main/resources/applicationContext.xml (100%) rename src/{community => extension}/rat/src/test/java/org/geoserver/rat/RasterAttributeTableTest.java (100%) rename src/{community => extension}/rat/src/test/java/org/geoserver/rat/rest/PAMControllerTest.java (100%) rename src/{community => extension}/rat/src/test/java/org/geoserver/rat/web/RasterAttributeTableConfigTest.java (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/norat.xml (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/rangeColors.xml (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/rangeNoColor.xml (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/valueColors.xml (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/valueNoColors.xml (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/web/rat.tiff (100%) rename src/{community => extension}/rat/src/test/resources/org/geoserver/rat/web/rat.tiff.aux.xml (100%) rename src/{community => }/release/ext-rat.xml (100%) diff --git a/doc/en/user/source/community/index.rst b/doc/en/user/source/community/index.rst index 7ef02247fd8..41408604850 100644 --- a/doc/en/user/source/community/index.rst +++ b/doc/en/user/source/community/index.rst @@ -63,7 +63,6 @@ officially part of the GeoServer releases. They are however built along with the opensearch-eo/index pgraster/pgraster proxy-base-ext/index - rat/index remote-wps/index s3-geotiff/index schemaless-features/index diff --git a/doc/en/user/source/extensions/index.rst b/doc/en/user/source/extensions/index.rst index 888d61d9030..d8dbce265b9 100644 --- a/doc/en/user/source/extensions/index.rst +++ b/doc/en/user/source/extensions/index.rst @@ -43,3 +43,4 @@ This section describes most of the extensions available for GeoServer. Other dat csw-iso/index metadata/index iau/index + rat/index diff --git a/doc/en/user/source/community/rat/images/rat_map.png b/doc/en/user/source/extensions/rat/images/rat_map.png similarity index 100% rename from doc/en/user/source/community/rat/images/rat_map.png rename to doc/en/user/source/extensions/rat/images/rat_map.png diff --git a/doc/en/user/source/community/rat/images/rat_panel.png b/doc/en/user/source/extensions/rat/images/rat_panel.png similarity index 100% rename from doc/en/user/source/community/rat/images/rat_panel.png rename to doc/en/user/source/extensions/rat/images/rat_panel.png diff --git a/doc/en/user/source/community/rat/index.rst b/doc/en/user/source/extensions/rat/index.rst similarity index 100% rename from doc/en/user/source/community/rat/index.rst rename to doc/en/user/source/extensions/rat/index.rst diff --git a/doc/en/user/source/community/rat/installing.rst b/doc/en/user/source/extensions/rat/installing.rst similarity index 52% rename from doc/en/user/source/community/rat/installing.rst rename to doc/en/user/source/extensions/rat/installing.rst index 4d8c4f2e17d..ac30f183861 100644 --- a/doc/en/user/source/community/rat/installing.rst +++ b/doc/en/user/source/extensions/rat/installing.rst @@ -5,7 +5,9 @@ Installing the RAT module To install the Raster Attribute Table support: -#. Download the **rat** community extension from the appropriate `nightly build `_. The file name is called :file:`geoserver-*-rat-plugin.zip`, where ``*`` matches the version number of GeoServer you are using. +#. From the :website:`website download ` page, locate your release, and download: :download_extension:`rat` + + .. warning:: Make sure to match the version of the extension to the version of GeoServer. #. Extract this these files and place the JARs in ``WEB-INF/lib``. diff --git a/doc/en/user/source/community/rat/using.rst b/doc/en/user/source/extensions/rat/using.rst similarity index 100% rename from doc/en/user/source/community/rat/using.rst rename to doc/en/user/source/extensions/rat/using.rst diff --git a/src/community/pom.xml b/src/community/pom.xml index b24d6a0ffed..83df5a9ba4f 100644 --- a/src/community/pom.xml +++ b/src/community/pom.xml @@ -99,7 +99,6 @@ release/ext-xslt.xml release/ext-datadir-catalog-loader.xml release/ext-wps-longitudinal-profile.xml - release/ext-rat.xml release/ext-monitor-kafka.xml release/ext-graticule.xml release/ext-wfs-freemarker.xml @@ -252,7 +251,6 @@ imagemap datadir-catalog-loader wps-longitudinal-profile - rat monitor-kafka graticule wfs-freemarker @@ -718,12 +716,6 @@ wps-longitudinal-profile - - rat - - rat - - graticule diff --git a/src/community/release/pom.xml b/src/community/release/pom.xml index 549c1cc23b3..35270b82560 100644 --- a/src/community/release/pom.xml +++ b/src/community/release/pom.xml @@ -447,11 +447,6 @@ gs-wps-longitudinal-profile ${project.version} - - org.geoserver.community - gs-rat - ${project.version} - org.geoserver.community gs-monitor-kafka diff --git a/src/extension/pom.xml b/src/extension/pom.xml index 2d2d3060fc9..261b4ccb71f 100644 --- a/src/extension/pom.xml +++ b/src/extension/pom.xml @@ -394,6 +394,12 @@ metadata + + rat + + rat + + @@ -443,6 +449,7 @@ mapml geopkg-output metadata + rat @@ -494,6 +501,7 @@ mapml geopkg-output metadata + rat diff --git a/src/community/rat/pom.xml b/src/extension/rat/pom.xml similarity index 95% rename from src/community/rat/pom.xml rename to src/extension/rat/pom.xml index 32daae67326..9a443ec8845 100644 --- a/src/community/rat/pom.xml +++ b/src/extension/rat/pom.xml @@ -3,11 +3,11 @@ 4.0.0 org.geoserver - community + extension 2.26-SNAPSHOT - org.geoserver.community + org.geoserver.extension gs-rat Raster Attribute Table support diff --git a/src/community/rat/src/main/java/org/geoserver/rat/CoverageRATs.java b/src/extension/rat/src/main/java/org/geoserver/rat/CoverageRATs.java similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/CoverageRATs.java rename to src/extension/rat/src/main/java/org/geoserver/rat/CoverageRATs.java diff --git a/src/community/rat/src/main/java/org/geoserver/rat/RasterAttributeTable.java b/src/extension/rat/src/main/java/org/geoserver/rat/RasterAttributeTable.java similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/RasterAttributeTable.java rename to src/extension/rat/src/main/java/org/geoserver/rat/RasterAttributeTable.java diff --git a/src/community/rat/src/main/java/org/geoserver/rat/rest/PAMController.java b/src/extension/rat/src/main/java/org/geoserver/rat/rest/PAMController.java similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/rest/PAMController.java rename to src/extension/rat/src/main/java/org/geoserver/rat/rest/PAMController.java diff --git a/src/community/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.html b/src/extension/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.html similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.html rename to src/extension/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.html diff --git a/src/community/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.java b/src/extension/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.java similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.java rename to src/extension/rat/src/main/java/org/geoserver/rat/web/RasterAttributeTableConfig.java diff --git a/src/community/rat/src/main/java/org/geoserver/rat/web/RowProvider.java b/src/extension/rat/src/main/java/org/geoserver/rat/web/RowProvider.java similarity index 100% rename from src/community/rat/src/main/java/org/geoserver/rat/web/RowProvider.java rename to src/extension/rat/src/main/java/org/geoserver/rat/web/RowProvider.java diff --git a/src/community/rat/src/main/resources/GeoServerApplication.properties b/src/extension/rat/src/main/resources/GeoServerApplication.properties similarity index 100% rename from src/community/rat/src/main/resources/GeoServerApplication.properties rename to src/extension/rat/src/main/resources/GeoServerApplication.properties diff --git a/src/community/rat/src/main/resources/applicationContext.xml b/src/extension/rat/src/main/resources/applicationContext.xml similarity index 100% rename from src/community/rat/src/main/resources/applicationContext.xml rename to src/extension/rat/src/main/resources/applicationContext.xml diff --git a/src/community/rat/src/test/java/org/geoserver/rat/RasterAttributeTableTest.java b/src/extension/rat/src/test/java/org/geoserver/rat/RasterAttributeTableTest.java similarity index 100% rename from src/community/rat/src/test/java/org/geoserver/rat/RasterAttributeTableTest.java rename to src/extension/rat/src/test/java/org/geoserver/rat/RasterAttributeTableTest.java diff --git a/src/community/rat/src/test/java/org/geoserver/rat/rest/PAMControllerTest.java b/src/extension/rat/src/test/java/org/geoserver/rat/rest/PAMControllerTest.java similarity index 100% rename from src/community/rat/src/test/java/org/geoserver/rat/rest/PAMControllerTest.java rename to src/extension/rat/src/test/java/org/geoserver/rat/rest/PAMControllerTest.java diff --git a/src/community/rat/src/test/java/org/geoserver/rat/web/RasterAttributeTableConfigTest.java b/src/extension/rat/src/test/java/org/geoserver/rat/web/RasterAttributeTableConfigTest.java similarity index 100% rename from src/community/rat/src/test/java/org/geoserver/rat/web/RasterAttributeTableConfigTest.java rename to src/extension/rat/src/test/java/org/geoserver/rat/web/RasterAttributeTableConfigTest.java diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/norat.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/norat.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/norat.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/norat.xml diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/rangeColors.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/rangeColors.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/rangeColors.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/rangeColors.xml diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/rangeNoColor.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/rangeNoColor.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/rangeNoColor.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/rangeNoColor.xml diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/valueColors.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/valueColors.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/valueColors.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/valueColors.xml diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/valueNoColors.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/valueNoColors.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/valueNoColors.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/valueNoColors.xml diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/web/rat.tiff b/src/extension/rat/src/test/resources/org/geoserver/rat/web/rat.tiff similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/web/rat.tiff rename to src/extension/rat/src/test/resources/org/geoserver/rat/web/rat.tiff diff --git a/src/community/rat/src/test/resources/org/geoserver/rat/web/rat.tiff.aux.xml b/src/extension/rat/src/test/resources/org/geoserver/rat/web/rat.tiff.aux.xml similarity index 100% rename from src/community/rat/src/test/resources/org/geoserver/rat/web/rat.tiff.aux.xml rename to src/extension/rat/src/test/resources/org/geoserver/rat/web/rat.tiff.aux.xml diff --git a/src/pom.xml b/src/pom.xml index d48fe9a2495..f25d0d94a8e 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -1874,6 +1874,7 @@ release/ext-wps-jdbc.xml release/ext-wps.xml release/ext-ysld.xml + release/ext-rat.xml ${project.build.directory}/release diff --git a/src/community/release/ext-rat.xml b/src/release/ext-rat.xml similarity index 100% rename from src/community/release/ext-rat.xml rename to src/release/ext-rat.xml diff --git a/src/release/pom.xml b/src/release/pom.xml index a68016eb198..84a15041f7a 100644 --- a/src/release/pom.xml +++ b/src/release/pom.xml @@ -407,6 +407,11 @@ gs-metadata ${project.version} + + org.geoserver.extension + gs-rat + ${project.version} + diff --git a/src/web/app/pom.xml b/src/web/app/pom.xml index ff639d6497c..e0cd793ed55 100644 --- a/src/web/app/pom.xml +++ b/src/web/app/pom.xml @@ -1876,7 +1876,7 @@ rat - org.geoserver.community + org.geoserver.extension gs-rat ${gs.version} From 3b73d677b8815146249f7ccfd525f08d0e9efd4a Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Tue, 30 Apr 2024 06:18:40 -0400 Subject: [PATCH 13/31] [GEOS-11378] Update MapML viewer to release 0.13.3 (#7585) --- .../mapml/src/main/resources/viewer/widget/layer.js | 2 +- .../mapml/src/main/resources/viewer/widget/leaflet.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-area.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-caption.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-extent.js | 4 ++-- .../mapml/src/main/resources/viewer/widget/map-feature.js | 4 ++-- .../mapml/src/main/resources/viewer/widget/map-input.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-link.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-select.js | 2 +- .../mapml/src/main/resources/viewer/widget/map-style.js | 2 +- .../src/main/resources/viewer/widget/mapml-viewer.js | 2 +- .../mapml/src/main/resources/viewer/widget/mapml.js | 8 ++++---- .../mapml/src/main/resources/viewer/widget/web-map.js | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/extension/mapml/src/main/resources/viewer/widget/layer.js b/src/extension/mapml/src/main/resources/viewer/widget/layer.js index e739bbf5f1d..330284117e0 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/layer.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/layer.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ import"./leaflet.js";import"./mapml.js";class MapLayer extends HTMLElement{static get observedAttributes(){return["src","label","checked","hidden","opacity"]}#hasConnected;get src(){return this.hasAttribute("src")?this.getAttribute("src"):""}set src(e){e&&this.setAttribute("src",e)}get label(){return this._layer?this._layer.getName():this.hasAttribute("label")?this.getAttribute("label"):""}set label(e){e&&this.setAttribute("label",e)}get checked(){return this.hasAttribute("checked")}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}get hidden(){return this.hasAttribute("hidden")}set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}get opacity(){return+(this._opacity??this.getAttribute("opacity"))}set opacity(e){1<+e||+e<0||this.setAttribute("opacity",e)}get extent(){return this._layer&&!this._layer.bounds&&this._layer._calculateBounds(),this._layer?Object.assign(M._convertAndFormatPCRS(this._layer.bounds,M[this.getProjection()],this.getProjection()),{zoom:this._layer.zoomBounds}):null}attributeChangedCallback(e,t,r){if(this.#hasConnected)switch(e){case"label":this._layer.setName(r);break;case"checked":"string"==typeof r?this.parentElement._map.addLayer(this._layer):this.parentElement._map.removeLayer(this._layer),this._layerControlCheckbox.checked=this.checked,this.dispatchEvent(new CustomEvent("map-change"));break;case"hidden":"string"==typeof r?this._layerControl.removeLayer(this._layer):(this._layerControl.addOrUpdateOverlay(this._layer,this.label),this._validateDisabled());break;case"opacity":t!==r&&this._layer&&(this._opacity=r,this._layer.changeOpacity(r));break;case"src":t!==r&&(this._onRemove(),this.isConnected&&this._onAdd())}}constructor(){super(),this._opacity=this.opacity||1,this._renderingMapContent=M.options.contentPreference,this.attachShadow({mode:"open"})}disconnectedCallback(){this.hasAttribute("data-moving")||this._onRemove()}_onRemove(){this._observer&&this._observer.disconnect();let e=this._layer,t=this._layerControl;this._layerControlHTML;delete this._layer,delete this._layerControl,delete this._layerControlHTML,delete this._fetchError,this.shadowRoot.innerHTML="",this.src&&(this.innerHTML=""),e&&e.off(),e&&e._map&&e._map.removeLayer(e),t&&!this.hidden&&t.removeLayer(e)}connectedCallback(){if(!this.hasAttribute("data-moving")){this.#hasConnected=!0,this._createLayerControlHTML=M._createLayerControlHTML.bind(this);const e=this._onAdd.bind(this),t=this._onRemove.bind(this);this.parentElement.whenReady().then(()=>{t(),e()}).catch(e=>{throw new Error("Map never became ready: "+e)})}}_onAdd(){new Promise((e,a)=>{this.addEventListener("changestyle",function(e){e.stopPropagation(),e.detail&&(this._renderingMapContent=e.detail._renderingMapContent,this.src=e.detail.src)},{once:!0}),this.addEventListener("zoomchangesrc",function(e){e.stopPropagation(),this.src=e.detail.href},{once:!0});let t=this.baseURI||document.baseURI;const r=new Headers;if(r.append("Accept","text/mapml"),this.src)fetch(this.src,{headers:r}).then(e=>{if(!e.ok)throw new Error("HTTP error! Status: "+e.status);return e.text()}).then(e=>{let t=(new DOMParser).parseFromString(e,"text/xml");if(t.querySelector("parsererror")||!t.querySelector("mapml-"))throw this._fetchError=!0,console.log("Error fetching layer content:\n\n"+e+"\n"),new Error("Parser error");return t}).then(e=>{this.copyRemoteContentToShadowRoot(e.querySelector("mapml-"));let t=this.shadowRoot.querySelectorAll("*"),r=[];for(let e=0;e{this.selectAlternateOrChangeProjection(),this.checkForPreferredContent()}).then(()=>{this._layer=M.mapMLLayer(new URL(this.src,t).href,this,{projection:this.getProjection(),opacity:this.opacity}),this._createLayerControlHTML(),this._attachedToMap(),this._runMutationObserver(this.shadowRoot.children),this._bindMutationObserver(),this._validateDisabled(),this.dispatchEvent(new CustomEvent("loadedmetadata",{detail:{target:this}})),e()}).catch(e=>{a(e)});else{let t=this.querySelectorAll("*"),r=[];for(let e=0;e{this.selectAlternateOrChangeProjection(),this.checkForPreferredContent()}).then(()=>{this._layer=M.mapMLLayer(null,this,{projection:this.getProjection(),opacity:this.opacity}),this._createLayerControlHTML(),this._attachedToMap(),this._runMutationObserver(this.children),this._bindMutationObserver(),this._validateDisabled(),this.dispatchEvent(new CustomEvent("loadedmetadata",{detail:{target:this}})),e()}).catch(e=>{a(e)})}}).catch(e=>{"changeprojection"===e.message?e.cause.href?(console.log("Changing layer src to: "+e.cause.href),this.src=e.cause.href):e.cause.mapprojection&&(console.log("Changing map projection to match layer: "+e.cause.mapprojection),this.parentElement.projection=e.cause.mapprojection):"findmatchingpreferredcontent"===e.message?e.cause.href&&(console.log("Changing layer to matching preferred content at: "+e.cause.href),this.src=e.cause.href):"Failed to fetch"===e.message?this._fetchError=!0:(console.log(e),this.dispatchEvent(new CustomEvent("error",{detail:{target:this}})))})}selectAlternateOrChangeProjection(){let e=this.src?this.shadowRoot:this,t=this.getProjection()!==this.parentElement.projection&&e.querySelector("map-link[rel=alternate][projection="+this.parentElement.projection+"][href]");if(t){var r=new URL(t.getAttribute("href"),t.getBase()).href;throw new Error("changeprojection",{cause:{href:r}})}r=this.getProjection();if(r!==this.parentElement.projection&&1===this.parentElement.layers.length)throw new Error("changeprojection",{cause:{mapprojection:r}})}checkForPreferredContent(){let e=this.src?this.shadowRoot:this,t=e.querySelector(`map-link[rel="style"][media="prefers-map-content=${this._renderingMapContent}"][href]`);if(t){var r=new URL(t.getAttribute("href"),t.getBase()).href;throw new Error("findmatchingpreferredcontent",{cause:{href:r}})}}copyRemoteContentToShadowRoot(e){let t=this.shadowRoot,r=document.createDocumentFragment();var a=e.querySelectorAll("map-head > *, map-body > *");for(let e=0;e{var r=t[0].attributes.units.value;let a=!0;for(let e=0;e{this.whenReady().then(()=>{delete this._layer.bounds,e.addFeature(this._layer._mapmlvectors)})},i=e=>{this.whenReady().then(()=>{this._layer.appendStyleLink(e)})},s=e=>{this.whenReady().then(()=>{this._layer.appendStyleElement(e)})},o=e=>{this.whenReady().then(()=>{delete this._layer.bounds,this._validateDisabled()})};let n=this.src?this.shadowRoot:this,h=n instanceof ShadowRoot?":host":":scope";var l=e=>{this.whenReady().then(()=>{this._layer._calculateBounds(),this._validateDisabled()})};for(let t=0;t [name=${e.getAttribute("name")}]`)&&e.hasAttribute("content")&&l(e)}}}_bindMutationObserver(){this._observer=new MutationObserver(e=>{for(var t of e)"childList"===t.type&&this._runMutationObserver(t.addedNodes)}),this._observer.observe(this.src?this.shadowRoot:this,{childList:!0})}_attachedToMap(){for(var e=0,t=1,r=this.parentNode.children;e{let s=this._layer,e=s?._map;if(e){this._validateLayerZoom({zoom:e.getZoom()});const o=(this.src?this.shadowRoot:this).querySelectorAll("map-extent");let t=[];for(let e=0;e{let t=0,r=0,a=["_staticTileLayer","_mapmlvectors","_extentLayer"];for(let e=0;e{console.log(e)})}},0)}_validateLayerZoom(e){var t=e.zoom,r=this.extent.zoom.minZoom,a=this.extent.zoom.maxZoom,i=(this.src?this.shadowRoot:this).querySelector("map-link[rel=zoomin]"),e=(this.src?this.shadowRoot:this).querySelector("map-link[rel=zoomout]");let s;r<=t&&t<=a||(i&&a{e.disabled=!0}))):(e.disabled=!1,a.disabled=!1,t.style.fontStyle="normal",r.style.fontStyle="normal",i&&(i.style.fontStyle="normal",i.querySelectorAll("input").forEach(e=>{e.disabled=!1})))}queryable(){let e=this.src?this.shadowRoot:this;return e.querySelector("map-extent[checked] > map-link[rel=query]")&&this.checked&&this._layer&&!this.hidden}getAlternateStyles(e){if(1{e.hasAttribute("href")?e.setAttribute("href",decodeURI(new URL(e.attributes.href.value,this.baseURI||document.baseURI).href)):e.hasAttribute("tref")&&e.setAttribute("tref",decodeURI(new URL(e.attributes.tref.value,this.baseURI||document.baseURI).href))})}var e=t.outerHTML;return t.remove(),e}zoomTo(){this.whenReady().then(()=>{let e=this.parentElement._map,t=this.extent,r=t.topLeft.pcrs,a=t.bottomRight.pcrs,i=L.bounds(L.point(r.horizontal,r.vertical),L.point(a.horizontal,a.vertical)),s=e.options.crs.unproject(i.getCenter(!0));var o=t.zoom.maxZoom,n=t.zoom.minZoom;e.setView(s,M.getMaxZoom(i,e,n,o),{animate:!1})})}mapml2geojson(e={}){return M.mapml2geojson(this,e)}pasteFeature(e){switch(typeof e){case"string":e.trim(),""===e.slice(-14)&&this.insertAdjacentHTML("beforeend",e);break;case"object":"MAP-FEATURE"===e.nodeName.toUpperCase()&&this.appendChild(e)}}whenReady(){return new Promise((t,r)=>{let a,i;this._layer&&this._layerControlHTML&&(!this.src||this.shadowRoot?.childNodes.length)?t():(a=setInterval(function(e){e._layer&&e._layerControlHTML&&(!e.src||e.shadowRoot?.childNodes.length)?(clearInterval(a),clearTimeout(i),t()):e._fetchError&&(clearInterval(a),clearTimeout(i),r("Error fetching layer content"))},200,this),i=setTimeout(function(){clearInterval(a),clearTimeout(i),r("Timeout reached waiting for layer to be ready")},5e3))})}whenElemsReady(){let e=[],t=this.src?this.shadowRoot:this;for(var r of[...t.querySelectorAll("map-extent"),...t.querySelectorAll("map-feature")])e.push(r.whenReady());return Promise.allSettled(e)}}export{MapLayer}; //# sourceMappingURL=layer.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/leaflet.js b/src/extension/mapml/src/main/resources/viewer/widget/leaflet.js index bb97c1c3795..485c29f06cc 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/leaflet.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/leaflet.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){"use strict";function l(t){for(var i,e,s=1,n=arguments.length;s=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=B(t);var i=this.min,e=this.max,s=t.min,n=t.max,t=n.x>=i.x&&s.x<=e.x,e=n.y>=i.y&&s.y<=e.y;return t&&e},overlaps:function(t){t=B(t);var i=this.min,e=this.max,s=t.min,n=t.max,t=n.x>i.x&&s.xi.y&&s.y=s.lat&&e.lat<=n.lat&&i.lng>=s.lng&&e.lng<=n.lng},intersects:function(t){t=R(t);var i=this._southWest,e=this._northEast,s=t.getSouthWest(),n=t.getNorthEast(),t=n.lat>=i.lat&&s.lat<=e.lat,e=n.lng>=i.lng&&s.lng<=e.lng;return t&&e},overlaps:function(t){t=R(t);var i=this._southWest,e=this._northEast,s=t.getSouthWest(),n=t.getNorthEast(),t=n.lat>i.lat&&s.lati.lng&&s.lng","http://www.w3.org/2000/svg"===(Tt.firstChild&&Tt.firstChild.namespaceURI));function St(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var Et={ie:Y,ielt9:$,edge:tt,webkit:it,android:et,android23:st,androidStock:ot,opera:at,chrome:rt,gecko:ht,safari:lt,phantom:ct,opera12:ut,win:dt,ie3d:_t,webkit3d:pt,gecko3d:mt,any3d:ft,mobile:gt,mobileWebkit:vt,mobileWebkit3d:yt,msPointer:Mt,pointer:xt,touch:bt,touchNative:wt,mobileOpera:Pt,mobileGecko:F,retina:Lt,passiveEvents:Ct,canvas:nt,svg:Q,vml:!Q&&function(){try{var t=document.createElement("div");t.innerHTML='';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),inlineSvg:Tt,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},kt=Et.msPointer?"MSPointerDown":"pointerdown",zt=Et.msPointer?"MSPointerMove":"pointermove",At=Et.msPointer?"MSPointerUp":"pointerup",Ot=Et.msPointer?"MSPointerCancel":"pointercancel",It={touchstart:kt,touchmove:zt,touchend:At,touchcancel:Ot},Zt={touchstart:function(t,i){i.MSPOINTER_TYPE_TOUCH&&i.pointerType===i.MSPOINTER_TYPE_TOUCH&&Oi(i);Gt(t,i)},touchmove:Gt,touchend:Gt,touchcancel:Gt},Bt={},Nt=!1;function Rt(t,i,e){return"touchstart"===i&&(Nt||(document.addEventListener(kt,jt,!0),document.addEventListener(zt,Dt,!0),document.addEventListener(At,qt,!0),document.addEventListener(Ot,qt,!0),Nt=!0)),Zt[i]?(e=Zt[i].bind(this,e),t.addEventListener(It[i],e,!1),e):(console.warn("wrong event specified:",i),c)}function jt(t){Bt[t.pointerId]=t}function Dt(t){Bt[t.pointerId]&&(Bt[t.pointerId]=t)}function qt(t){delete Bt[t.pointerId]}function Gt(t,i){if(i.pointerType!==(i.MSPOINTER_TYPE_MOUSE||"mouse")){for(var e in i.touches=[],Bt)i.touches.push(Bt[e]);i.changedTouches=[i],t(i)}}var Ft=200;function Ht(t,e){t.addEventListener("dblclick",e);var s,n=0;function i(t){var i;1===t.detail?"mouse"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((i=Zi(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!i.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((i=Date.now())-n<=Ft?2===++s&&e(function(t){var i,e,s={};for(e in t)i=t[e],s[e]=i&&i.bind?i.bind(t):i;return(t=s).type="dblclick",s.detail=2,s.isTrusted=!1,s._simulated=!0,s}(t)):s=1,n=i)):s=t.detail}return t.addEventListener("click",i),{dblclick:e,simDblclick:i}}var Ut,Wt,Vt,Kt,Xt,Jt,Qt=_i(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),Yt=_i(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),$t="webkitTransition"===Yt||"OTransition"===Yt?Yt+"End":"transitionend";function ti(t){return"string"==typeof t?document.getElementById(t):t}function ii(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];return"auto"===(e=(!e||"auto"===e)&&document.defaultView?(t=document.defaultView.getComputedStyle(t,null))?t[i]:null:e)?null:e}function ei(t,i,e){t=document.createElement(t);return t.className=i||"",e&&e.appendChild(t),t}function si(t){var i=t.parentNode;i&&i.removeChild(t)}function ni(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function oi(t){var i=t.parentNode;i&&i.lastChild!==t&&i.appendChild(t)}function ai(t){var i=t.parentNode;i&&i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function ri(t,i){if(void 0!==t.classList)return t.classList.contains(i);t=ui(t);return 0this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),t=this._limitCenter(e,this._zoom,R(t));return e.equals(t)||this.panTo(t,i),this._enforcingBounds=!1,this},panInside:function(t,i){var e=I((i=i||{}).paddingTopLeft||i.padding||[0,0]),s=I(i.paddingBottomRight||i.padding||[0,0]),n=this.project(this.getCenter()),o=this.project(t),t=this.getPixelBounds(),e=B([t.min.add(e),t.max.subtract(s)]),t=e.getSize();return e.contains(o)||(this._enforcingBounds=!0,s=o.subtract(e.getCenter()),t=e.extend(o).getSize().subtract(t),n.x+=s.x<0?-t.x:t.x,n.y+=s.y<0?-t.y:t.y,this.panTo(this.unproject(n),i),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var i=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var e=this.getSize(),s=i.divideBy(2).round(),n=e.divideBy(2).round(),n=s.subtract(n);return n.x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(r(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:i,newSize:e})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=l({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var i=r(this._handleGeolocationResponse,this),e=r(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(i,e,t):navigator.geolocation.getCurrentPosition(i,e,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i;this._container._leaflet_id&&(i=t.code,t=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var i,e=new j(t.coords.latitude,t.coords.longitude),s=e.toBounds(2*t.coords.accuracy),n=this._locateOptions;n.setView&&(i=this.getBoundsZoom(s),this.setView(e,n.maxZoom?Math.min(i,n.maxZoom):i));var o,a={latlng:e,bounds:s,timestamp:t.timestamp};for(o in t.coords)"number"==typeof t.coords[o]&&(a[o]=t.coords[o]);this.fire("locationfound",a)}},addHandler:function(t,i){if(!i)return this;i=this[t]=new i(this);return this._handlers.push(i),this.options[t]&&i.enable(),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),si(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(T(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)si(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){i=ei("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=i),i},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new N(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=R(t),e=I(e||[0,0]);var s=this.getZoom()||0,n=this.getMinZoom(),o=this.getMaxZoom(),a=t.getNorthWest(),r=t.getSouthEast(),t=this.getSize().subtract(e),e=B(this.project(r,s),this.project(a,s)).getSize(),r=Et.any3d?this.options.zoomSnap:1,a=t.x/e.x,e=t.y/e.y,e=i?Math.max(a,e):Math.min(a,e),s=this.getScaleZoom(e,s);return r&&(s=Math.round(s/(r/100))*(r/100),s=i?Math.ceil(s/r)*r:Math.floor(s/r)*r),Math.max(n,Math.min(o,s))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new A(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){i=this._getTopLeftPoint(t,i);return new Z(i,i.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;i=e.zoom(t*e.scale(i));return isNaN(i)?1/0:i},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(D(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(I(t),i)},layerPointToLatLng:function(t){t=I(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(D(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(D(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(R(t))},distance:function(t,i){return this.options.crs.distance(D(t),D(i))},containerPointToLayerPoint:function(t){return I(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return I(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(I(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(D(t)))},mouseEventToContainerPoint:function(t){return Bi(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){t=this._container=ti(t);if(!t)throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");bi(t,"scroll",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Et.any3d,hi(t,"leaflet-container"+(Et.touch?" leaflet-touch":"")+(Et.retina?" leaflet-retina":"")+(Et.ielt9?" leaflet-oldie":"")+(Et.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=ii(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&"sticky"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),mi(this._mapPane,new A(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(hi(t.markerPane,"leaflet-zoom-hide"),hi(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i,e){mi(this._mapPane,new A(0,0));var s=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,e)._move(t,i)._moveEnd(n),this.fire("viewreset"),s&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e,s){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),s?e&&e.pinch&&this.fire("zoom",e):((n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return T(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){mi(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={};var i=t?Li:bi;i((this._targets[h(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Et.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){T(this._resizeRequest),this._resizeRequest=C(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,s=[],n="mouseout"===i||"mouseover"===i,o=t.target||t.srcElement,a=!1;o;){if((e=this._targets[h(o)])&&("click"===i||"preclick"===i)&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(n&&!ji(o,t))break;if(s.push(e),n)break}if(o===this._container)break;o=o.parentNode}return s=!(s.length||a||n)&&this.listens(i,!0)?[this]:s},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var i,e=t.target||t.srcElement;!this._loaded||e._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(e)||("mousedown"===(i=t.type)&&yi(e),this._fireDOMEvent(t,i))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,i,e){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,e));var s=this._findEventTargets(t,i);if(e){for(var n=[],o=0;othis.options.zoomAnimationThreshold)return!1;var s=this.getZoomScale(i),s=this._getCenterOffset(t)._divideBy(1-1/s);return!(!0!==e.animate&&!this.getSize().contains(s))&&(C(function(){this._moveStart(!0,e.noMoveStart||!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,e,s){this._mapPane&&(e&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,hi(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:s}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(r(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&li(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Gi(t){return new Fi(t)}var Fi=E.extend({options:{position:"topright"},initialize:function(t){_(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),t=t._controlCorners[e];return hi(i,"leaflet-control"),-1!==e.indexOf("bottom")?t.insertBefore(i,t.firstChild):t.appendChild(i),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(si(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",i=document.createElement("div");return i.innerHTML=t,i.firstChild},_addItem:function(t){var i,e=document.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers_"+h(this),s),this._layerControlInputs.push(i),i.layerId=h(t.layer),bi(i,"click",this._onInputClick,this);var n=document.createElement("span");n.innerHTML=" "+t.name;s=document.createElement("span");return e.appendChild(s),s.appendChild(i),s.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){if(!this._preventClick){var t,i,e=this._layerControlInputs,s=[],n=[];this._handlingClick=!0;for(var o=e.length-1;0<=o;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.checked?s.push(i):t.checked||n.push(i);for(o=0;oi.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section;this._preventClick=!0,bi(t,"click",Oi),this.expand();var i=this;setTimeout(function(){Li(t,"click",Oi),i._preventClick=!1})}}),Ui=Fi.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ei("div",i+" leaflet-bar"),s=this.options;return this._zoomInButton=this._createButton(s.zoomInText,s.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(s.zoomOutText,s.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,s,n){s=ei("a",e,s);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),Ai(s),bi(s,"click",Ii),bi(s,"click",n,this),bi(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";li(this._zoomInButton,i),li(this._zoomOutButton,i),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(hi(this._zoomOutButton,i),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(hi(this._zoomInButton,i),this._zoomInButton.setAttribute("aria-disabled","true"))}});qi.mergeOptions({zoomControl:!0}),qi.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Ui,this.addControl(this.zoomControl))});var Wi=Fi.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i="leaflet-control-scale",e=ei("div",i),s=this.options;return this._addScales(s,i+"-line",e),t.on(s.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),e},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ei("div",i,e)),t.imperial&&(this._iScale=ei("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,i=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(i)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t);this._updateScale(this._mScale,i<1e3?i+" m":i/1e3+" km",i/t)},_updateImperial:function(t){var i,e=3.2808399*t;5280'+(Et.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){_(this,t),this._attributions={}},onAdd:function(t){for(var i in(t.attributionControl=this)._container=ei("div","leaflet-control-attribution"),Ai(this._container),t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,i=[];for(t in this._attributions)this._attributions[t]&&i.push(t);var e=[];this.options.prefix&&e.push(this.options.prefix),i.length&&e.push(i.join(", ")),this._container.innerHTML=e.join(' ')}}});qi.mergeOptions({attributionControl:!0}),qi.addInitHook(function(){this.options.attributionControl&&(new Vi).addTo(this)});Fi.Layers=Hi,Fi.Zoom=Ui,Fi.Scale=Wi,Fi.Attribution=Vi,Gi.layers=function(t,i,e){return new Hi(t,i,e)},Gi.zoom=function(t){return new Ui(t)},Gi.scale=function(t){return new Wi(t)},Gi.attribution=function(t){return new Vi(t)};xt=E.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}});xt.addTo=function(t,i){return t.addHandler(i,this),this};var bt={Events:k},Ki=Et.touch?"touchstart mousedown":"mousedown",Xi=z.extend({options:{clickTolerance:3},initialize:function(t,i,e,s){_(this,s),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(bi(this._dragStartTarget,Ki,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Xi._dragging===this&&this.finishDrag(!0),Li(this._dragStartTarget,Ki,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var i,e;this._enabled&&(this._moved=!1,ri(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Xi._dragging===this&&this.finishDrag():Xi._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Xi._dragging=this)._preventOutline&&yi(this._element),gi(),Vt(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=xi(this._element),this._startPoint=new A(i.clientX,i.clientY),this._startPos=fi(this._element),this._parentScale=wi(e),t="mousedown"===t.type,bi(document,t?"mousemove":"touchmove",this._onMove,this),bi(document,t?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var i;this._enabled&&(t.touches&&1i&&(e.push(t[s]),n=s);ni.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function oe(t,i,e,s){var n=i.x,o=i.y,a=e.x-n,r=e.y-o,i=a*a+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=s.y>t.y&&t.x<(s.x-e.x)*(t.y-e.y)/(s.y-e.y)+e.x&&(l=!l);return l||ye.prototype._containsPoint.call(this,t,!0)}});var xe=ue.extend({initialize:function(t,i){_(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,s,n=g(t)?t:t.features;if(n){for(i=0,e=n.length;io.x&&(a=e.x+r-o.x+n.x),e.x-a-s.x<(r=0)&&(a=e.x-s.x),e.y+i+n.y>o.y&&(r=e.y+i-o.y+n.y),e.y-r-s.y<0&&(r=e.y-s.y),(a||r)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([a,r]))))},_getAnchor:function(){return I(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});qi.mergeOptions({closePopupOnClick:!0}),qi.include({openPopup:function(t,i,e){return this._initOverlay(Ze,t,i,e).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),k.include({bindPopup:function(t,i){return this._popup=this._initOverlay(Ze,this._popup,t,i),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ue||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i;this._popup&&this._map&&(Ii(t),i=t.layer||t.target,this._popup._source!==i||i instanceof fe?(this._popup._source=i,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var Be=Ie.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Ie.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){Ie.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=Ie.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ei("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i,e=this._map,s=this._container,n=e.latLngToContainerPoint(e.getCenter()),o=e.layerPointToContainerPoint(t),a=this.options.direction,r=s.offsetWidth,h=s.offsetHeight,l=I(this.options.offset),e=this._getAnchor(),h="top"===a?(i=r/2,h):"bottom"===a?(i=r/2,0):(i="center"===a?r/2:"right"===a?0:"left"===a?r:o.xthis.options.maxZoom||sthis.options.maxZoom||void 0!==this.options.minZoom&&ne.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;t=this._tileCoordsToBounds(t);return R(this.options.bounds).overlaps(t)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),s=t.scaleBy(e),e=s.add(e);return[i.unproject(s,t.z),i.unproject(e,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new N(t[0],t[1]);return t=!this.options.noWrap?this._map.wrapLatLngBounds(t):t},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),t=new A(+i[0],+i[1]);return t.z=+i[2],t},_removeTile:function(t){var i=this._tiles[t];i&&(si(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){hi(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=c,t.onmousemove=c,Et.ielt9&&this.options.opacity<1&&di(t,this.options.opacity)},_addTile:function(t,i){var e=this._getTilePos(t),s=this._tileCoordsToKey(t),n=this.createTile(this._wrapCoords(t),r(this._tileReady,this,t));this._initTile(n),this.createTile.length<2&&C(r(this._tileReady,this,t,null,n)),mi(n,e),this._tiles[s]={el:n,coords:t,current:!0},i.appendChild(n),this.fire("tileloadstart",{tile:n,coords:t})},_tileReady:function(t,i,e){i&&this.fire("tileerror",{error:i,tile:e,coords:t});var s=this._tileCoordsToKey(t);(e=this._tiles[s])&&(e.loaded=+new Date,this._map._fadeAnimated?(di(e.el,0),T(this._fadeFrame),this._fadeFrame=C(this._updateOpacity,this)):(e.active=!0,this._pruneTiles()),i||(hi(e.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:e.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Et.ielt9||!this._map._fadeAnimated?C(this._pruneTiles,this):setTimeout(r(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new A(this._wrapX?n(t.x,this._wrapX):t.x,this._wrapY?n(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new Z(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var je=Re.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,i){this._url=t,(i=_(this,i)).detectRetina&&Et.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),nt={_initContainer:function(){this._container=ei("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Ge.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=Ue("shape");hi(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=Ue("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;si(i),t.removeInteractiveTarget(i),delete this._layers[h(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,s=t.options,n=t._container;n.stroked=!!s.stroke,n.filled=!!s.fill,s.stroke?(i=i||(t._stroke=Ue("stroke")),n.appendChild(i),i.weight=s.weight+"px",i.color=s.color,i.opacity=s.opacity,s.dashArray?i.dashStyle=g(s.dashArray)?s.dashArray.join(" "):s.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=s.lineCap.replace("butt","flat"),i.joinstyle=s.lineJoin):i&&(n.removeChild(i),t._stroke=null),s.fill?(e=e||(t._fill=Ue("fill")),n.appendChild(e),e.color=s.fillColor||s.color,e.opacity=s.fillOpacity):e&&(n.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),s=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+s+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){oi(t._container)},_bringToBack:function(t){ai(t._container)}},We=Et.vml?Ue:X,Ve=Ge.extend({_initContainer:function(){this._container=We("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=We("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){si(this._container),Li(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,i,e;this._map._animatingZoom&&this._bounds||(Ge.prototype._update.call(this),i=(t=this._bounds).getSize(),e=this._container,this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),mi(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update"))},_initPath:function(t){var i=t._path=We("path");t.options.className&&hi(i,t.options.className),t.options.interactive&&hi(i,"leaflet-interactive"),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){si(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,t=t.options;i&&(t.stroke?(i.setAttribute("stroke",t.color),i.setAttribute("stroke-opacity",t.opacity),i.setAttribute("stroke-width",t.weight),i.setAttribute("stroke-linecap",t.lineCap),i.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?i.setAttribute("stroke-dasharray",t.dashArray):i.removeAttribute("stroke-dasharray"),t.dashOffset?i.setAttribute("stroke-dashoffset",t.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),t.fill?(i.setAttribute("fill",t.fillColor||t.color),i.setAttribute("fill-opacity",t.fillOpacity),i.setAttribute("fill-rule",t.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,J(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),s="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+s+2*e+",0 "+s+2*-e+",0 ";this._setPath(t,e)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){oi(t._path)},_bringToBack:function(t){ai(t._path)}});function Ke(t){return Et.svg||Et.vml?new Ve(t):null}Et.vml&&Ve.include(nt),qi.include({getRenderer:function(t){t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=this._createRenderer({pane:t}),this._paneRenderers[t]=i),i},_createRenderer:function(t){return this.options.preferCanvas&&He(t)||Ke(t)}});var Xe=Me.extend({initialize:function(t,i){Me.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=R(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Ve.create=We,Ve.pointsToPath=J,xe.geometryToLayer=we,xe.coordsToLatLng=Pe,xe.coordsToLatLngs=Le,xe.latLngToCoords=Ce,xe.latLngsToCoords=Te,xe.getFeature=Se,xe.asFeature=Ee,qi.mergeOptions({boxZoom:!0});F=xt.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){bi(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){Li(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){si(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),Vt(),gi(),this._startPoint=this._map.mouseEventToContainerPoint(t),bi(document,{contextmenu:Ii,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ei("div","leaflet-zoom-box",this._container),hi(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new Z(this._point,this._startPoint),t=i.getSize();mi(this._box,i.min),this._box.style.width=t.x+"px",this._box.style.height=t.y+"px"},_finish:function(){this._moved&&(si(this._box),li(this._container,"leaflet-crosshair")),Kt(),vi(),Li(document,{contextmenu:Ii,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(r(this._resetState,this),0),t=new N(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire("boxzoomend",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}});qi.addInitHook("addHandler","boxZoom",F),qi.mergeOptions({doubleClickZoom:!0});Q=xt.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),s=i.options.zoomDelta,s=t.originalEvent.shiftKey?e-s:e+s;"center"===i.options.doubleClickZoom?i.setZoom(s):i.setZoomAround(t.containerPoint,s)}});qi.addInitHook("addHandler","doubleClickZoom",Q),qi.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});V=xt.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Xi(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),hi(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){li(this._map._container,"leaflet-grab"),li(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,i=this._map;i._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=R(this._map.options.maxBounds),this._offsetLimit=B(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,i.fire("movestart").fire("dragstart"),i.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var i,e;this._map.options.inertia&&(i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(e),this._times.push(i),this._prunePositions(i)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,s=this._draggable._newPos.x,n=(s-i+e)%t+i-e,i=(s+i+e)%t-i-e,i=Math.abs(n+e)i.getMaxZoom()&&1=this.text.length)return;t=this.text[this.place++]}switch(this.state){case 1:return this.neutral(t);case 2:return this.keyword(t);case 4:return this.quoted(t);case 5:return this.afterquote(t);case 3:return this.number(t);case-1:return}},C.prototype.afterquote=function(t){if('"'===t)return this.word+='"',void(this.state=4);if(d.test(t))return this.word=this.word.trim(),void this.afterItem(t);throw new Error("havn't handled \""+t+'" in afterquote yet, index '+this.place)},C.prototype.afterItem=function(t){if(","===t)return null!==this.word&&this.currentObject.push(this.word),this.word=null,void(this.state=1);"]"===t&&(this.level--,null!==this.word&&(this.currentObject.push(this.word),this.word=null),this.state=1,this.currentObject=this.stack.pop(),this.currentObject||(this.state=-1))},C.prototype.number=function(t){if(!L.test(t)){if(d.test(t))return this.word=parseFloat(this.word),void this.afterItem(t);throw new Error("havn't handled \""+t+'" in number yet, index '+this.place)}this.word+=t},C.prototype.quoted=function(t){'"'!==t?this.word+=t:this.state=5},C.prototype.keyword=function(t){if(u.test(t))this.word+=t;else{if("["===t){var i=[];return i.push(this.word),this.level++,null===this.root?this.root=i:this.currentObject.push(i),this.stack.push(this.currentObject),this.currentObject=i,void(this.state=1)}if(!d.test(t))throw new Error("havn't handled \""+t+'" in keyword yet, index '+this.place);this.afterItem(t)}},C.prototype.neutral=function(t){if(s.test(t))return this.word=t,void(this.state=2);if('"'===t)return this.word="",void(this.state=4);if(L.test(t))return this.word=t,void(this.state=3);if(!d.test(t))throw new Error("havn't handled \""+t+'" in neutral yet, index '+this.place);this.afterItem(t)},C.prototype.output=function(){for(;this.placeMath.PI&&(s-=2*Math.PI),t=Math.sin(n),n=Math.cos(n),{x:((e=e/Math.sqrt(1-i*(t*t)))+o)*n*Math.cos(s),y:(e+o)*n*Math.sin(s),z:(e*(1-i)+o)*t}}function Y(t,i,e,s){var n,o,a,r,h,l,c,u,d,_,p,m=1e-12,f=t.x,g=t.y,v=t.z||0,y=Math.sqrt(f*f+g*g),M=Math.sqrt(f*f+g*g+v*v);if(y/ex?Math.tan(e):0,c=Math.pow(i,2),u=Math.pow(c,2),d=1-this.es*Math.pow(n,2);a/=Math.sqrt(d);i=qt(e,n,o,this.en),d=this.a*(this.k0*a*(1+r/6*(1-c+h+r/20*(5-18*c+u+14*h-58*c*h+r/42*(61+179*u-u*c-479*c)))))+this.x0,u=this.a*(this.k0*(i-this.ml0+n*s*a/2*(1+r/12*(5-c+9*h+4*l+r/30*(61+u-58*c+270*h-330*c*h+r/56*(1385+543*u-u*c-3111*c))))))+this.y0}else{c=o*Math.sin(s);if(Math.abs(Math.abs(c)-1)x?Math.tan(i):0,s=this.ep2*Math.pow(e,2),l=Math.pow(s,2),n=Math.pow(r,2),o=Math.pow(n,2),h=1-this.es*Math.pow(a,2),a=c*Math.sqrt(h)/this.k0,l=i-(h*=r)*(r=Math.pow(a,2))/(1-this.es)*.5*(1-r/12*(5+3*n-9*s*n+s-4*l-r/30*(61+90*n-252*s*n+45*o+46*s-r/56*(1385+3633*n+4095*o+1574*o*n)))),q(this.long0+a*(1-r/6*(1+2*n+s-r/20*(5+28*n+24*o+8*s*n+6*s-r/42*(61+662*n+1320*o+720*o*n))))/e)):(l=g*D(u),0)):(c=.5*((d=Math.exp(c/this.k0))-1/d),d=this.lat0+u/this.k0,d=Math.cos(d),h=Math.sqrt((1-Math.pow(d,2))/(1+Math.pow(c,2))),l=Math.asin(h),u<0&&(l=-l),0==c&&0===d?0:q(Math.atan2(c,d)+this.long0));return t.x=d,t.y=l,t},names:["Transverse_Mercator","Transverse Mercator","tmerc"]},Wt=function(t){var t=Math.exp(t);return t=(t-1/t)/2},Vt=function(t,i){t=Math.abs(t),i=Math.abs(i);var e=Math.max(t,i),i=Math.min(t,i)/(e||1);return e*Math.sqrt(1+Math.pow(i,2))},Kt=function(t){var i=1+t,e=i-1;return 0==e?t:t*Math.log(i)/e},Xt=function(t){var t=Math.exp(t);return t=(t+1/t)/2};var Jt={init:function(){if(void 0===this.es||this.es<=0)throw new Error("incorrect elliptical usage");this.x0=void 0!==this.x0?this.x0:0,this.y0=void 0!==this.y0?this.y0:0,this.long0=void 0!==this.long0?this.long0:0,this.lat0=void 0!==this.lat0?this.lat0:0,this.cgb=[],this.cbg=[],this.utg=[],this.gtu=[];var t=(i=this.es/(1+Math.sqrt(1-this.es)))/(2-i),i=t;this.cgb[0]=t*(2+t*(-2/3+t*(t*(116/45+t*(26/45+-2854/675*t))-2))),this.cbg[0]=t*(t*(2/3+t*(4/3+t*(-82/45+t*(32/45+4642/4725*t))))-2),this.cgb[1]=(i*=t)*(7/3+t*(t*(-227/45+t*(2704/315+2323/945*t))-1.6)),this.cbg[1]=i*(5/3+t*(-16/15+t*(-13/9+t*(904/315+-1522/945*t)))),this.cgb[2]=(i*=t)*(56/15+t*(-136/35+t*(-1262/105+73814/2835*t))),this.cbg[2]=i*(-26/15+t*(34/21+t*(1.6+-12686/2835*t))),this.cgb[3]=(i*=t)*(4279/630+t*(-332/35+-399572/14175*t)),this.cbg[3]=i*(1237/630+t*(-24832/14175*t-2.4)),this.cgb[4]=(i*=t)*(4174/315+-144838/6237*t),this.cbg[4]=i*(-734/315+109598/31185*t),this.cgb[5]=601676/22275*(i*=t),this.cbg[5]=444337/155925*i,i=Math.pow(t,2),this.Qn=this.k0/(1+t)*(1+i*(.25+i*(1/64+i/256))),this.utg[0]=t*(t*(2/3+t*(-37/96+t*(1/360+t*(81/512+-96199/604800*t))))-.5),this.gtu[0]=t*(.5+t*(-2/3+t*(5/16+t*(41/180+t*(-127/288+7891/37800*t))))),this.utg[1]=i*(-1/48+t*(-1/15+t*(437/1440+t*(-46/105+1118711/3870720*t)))),this.gtu[1]=i*(13/48+t*(t*(557/1440+t*(281/630+-1983433/1935360*t))-.6)),this.utg[2]=(i*=t)*(-17/480+t*(37/840+t*(209/4480+-5569/90720*t))),this.gtu[2]=i*(61/240+t*(-103/140+t*(15061/26880+167603/181440*t))),this.utg[3]=(i*=t)*(-4397/161280+t*(11/504+830251/7257600*t)),this.gtu[3]=i*(49561/161280+t*(-179/168+6601661/7257600*t)),this.utg[4]=(i*=t)*(-4583/161280+108847/3991680*t),this.gtu[4]=i*(34729/80640+-3418889/1995840*t),this.utg[5]=-.03233083094085698*(i*=t),this.gtu[5]=.6650675310896665*i,i=Ft(this.cbg,this.lat0),this.Zb=-this.Qn*(i+function(t,i){for(var e,s=2*Math.cos(i),n=t.length-1,o=t[n],a=0;0<=--n;)e=s*o-a+t[n],a=o,o=e;return Math.sin(i)*e}(this.gtu,2*i))},forward:function(t){var i=q(t.x-this.long0),e=t.y,e=Ft(this.cbg,e),s=Math.sin(e),n=Math.cos(e),o=Math.sin(i),a=Math.cos(i);e=Math.atan2(s,a*n),i=Math.atan2(o*n,Vt(s,n*a)),n=Math.tan(i),a=Math.abs(n),a=Kt(a*(1+a/(Vt(1,a)+1)));var r,a=Ht(this.gtu,2*e,2*(i=n<0?-a:a));return e+=a[0],i+=a[1],e=Math.abs(i)<=2.623395162778?(r=this.a*(this.Qn*i)+this.x0,this.a*(this.Qn*e+this.Zb)+this.y0):r=1/0,t.x=r,t.y=e,t},inverse:function(t){var i,e,s,n,o=(t.x-this.x0)*(1/this.a),a=(t.y-this.y0)*(1/this.a);return a=(a-this.Zb)/this.Qn,o/=this.Qn,a=Math.abs(o)<=2.623395162778?(a+=(n=Ht(this.utg,2*a,2*o))[0],o+=n[1],o=Math.atan(Wt(o)),i=Math.sin(a),e=Math.cos(a),s=Math.sin(o),n=Math.cos(o),a=Math.atan2(i*n,Vt(s,n*e)),o=Math.atan2(s,n*e),e=q(o+this.long0),Ft(this.cgb,a)):e=1/0,t.x=e,t.y=a,t},names:["Extended_Transverse_Mercator","Extended Transverse Mercator","etmerc"]};function Qt(t,i){return Math.pow((1-t)/(1+t),i)}var Yt={init:function(){var t=function(t,i){if(void 0===t){if((t=Math.floor(30*(q(i)+Math.PI)/Math.PI)+1)<0)return 0;if(60x?this.ns=Math.log(i/s)/Math.log(e/n):this.ns=t,isNaN(this.ns)&&(this.ns=t),this.f0=i/(this.ns*Math.pow(e,this.ns)),this.rh=this.a*this.f0*Math.pow(o,this.ns),this.title||(this.title="Lambert Conformal Conic"))},forward:function(t){var i=t.x,e=t.y;Math.abs(2*Math.abs(e)-Math.PI)<=x&&(e=D(e)*(g-2*x));var s,n=Math.abs(Math.abs(e)-g);if(xx?this.ns0=(this.ms1*this.ms1-this.ms2*this.ms2)/(this.qs2-this.qs1):this.ns0=this.con,this.c=this.ms1*this.ms1+this.ns0*this.qs1,this.rh=this.a*Math.sqrt(this.c-this.ns0*this.qs0)/this.ns0)},forward:function(t){var i=t.x,e=t.y;this.sin_phi=Math.sin(e),this.cos_phi=Math.cos(e);var s=pi(this.e3,this.sin_phi,this.cos_phi),e=this.a*Math.sqrt(this.c-this.ns0*s)/this.ns0,s=this.ns0*q(i-this.long0),i=e*Math.sin(s)+this.x0,s=this.rh-e*Math.cos(s)+this.y0;return t.x=i,t.y=s,t},inverse:function(t){var i,e,s,n;return t.x-=this.x0,t.y=this.rh-t.y+this.y0,e=0<=this.ns0?(i=Math.sqrt(t.x*t.x+t.y*t.y),1):(i=-Math.sqrt(t.x*t.x+t.y*t.y),-1),(s=0)!==i&&(s=Math.atan2(e*t.x,e*t.y)),e=i*this.ns0/this.a,n=this.sphere?Math.asin((this.c-e*e)/(2*this.ns0)):(n=(this.c-e*e)/this.ns0,this.phi1z(this.e3,n)),s=q(s/this.ns0+this.long0),t.x=s,t.y=n,t},names:["Albers_Conic_Equal_Area","Albers","aea"],phi1z:function(t,i){var e,s,n,o=wi(.5*i);if(tMath.PI&&(e=Math.PI),s=(2*i+Math.sin(2*i))/Math.PI,12*g*this.a?void 0:(r=i/this.a,h=Math.sin(r),a=Math.cos(r),e=this.long0,Math.abs(i)<=x?s=this.lat0:(s=wi(a*this.sin_p12+t.y*h*this.cos_p12/i),o=Math.abs(this.lat0)-g,e=Math.abs(o)<=x?0<=this.lat0?q(this.long0+Math.atan2(t.x,-t.y)):q(this.long0-Math.atan2(-t.x,t.y)):q(this.long0+Math.atan2(t.x*h,i*this.cos_p12*a-t.y*this.sin_p12*h))),t.x=e,t.y=s,t):(r=ai(this.es),o=ri(this.es),a=hi(this.es),h=li(this.es),Math.abs(this.sin_p12-1)<=x?(n=this.a*oi(r,o,a,h,g),i=Math.sqrt(t.x*t.x+t.y*t.y),s=di((n-i)/this.a,r,o,a,h),e=q(this.long0+Math.atan2(t.x,-1*t.y))):Math.abs(this.sin_p12+1)<=x?(n=this.a*oi(r,o,a,h,g),i=Math.sqrt(t.x*t.x+t.y*t.y),s=di((i-n)/this.a,r,o,a,h),e=q(this.long0+Math.atan2(t.x,t.y))):(i=Math.sqrt(t.x*t.x+t.y*t.y),n=Math.atan2(t.x,t.y),r=ci(this.a,this.e,this.sin_p12),o=Math.cos(n),h=-(a=this.e*this.cos_p12*o)*a/(1-this.es),a=3*this.es*(1-h)*this.sin_p12*this.cos_p12*o/(1-this.es),r=1-h*(h=(r=i/r)-h*(1+h)*Math.pow(r,3)/6-a*(1+3*h)*Math.pow(r,4)/24)*h/2-r*h*h*h/6,o=Math.asin(this.sin_p12*Math.cos(h)+this.cos_p12*Math.sin(h)*o),e=q(this.long0+Math.asin(Math.sin(n)*Math.sin(h)/Math.cos(o))),h=Math.sin(o),s=Math.atan2((h-this.es*r*this.sin_p12)*Math.tan(o),h*(1-this.es))),t.x=e,t.y=s,t)},names:["Azimuthal_Equidistant","aeqd"]};var Bi={init:function(){this.sin_p14=Math.sin(this.lat0),this.cos_p14=Math.cos(this.lat0)},forward:function(t){var i,e,s=t.x,n=t.y,o=q(s-this.long0),a=Math.sin(n),r=Math.cos(n),s=Math.cos(o);return(0<(n=this.sin_p14*a+this.cos_p14*r*s)||Math.abs(n)<=x)&&(i=+this.a*r*Math.sin(o),e=this.y0+ +this.a*(this.cos_p14*a-this.sin_p14*r*s)),t.x=i,t.y=e,t},inverse:function(t){var i,e,s,n,o,a;return t.x-=this.x0,t.y-=this.y0,i=Math.sqrt(t.x*t.x+t.y*t.y),n=wi(i/this.a),e=Math.sin(n),s=Math.cos(n),o=this.long0,Math.abs(i)<=x?(a=this.lat0,t.x=o,t.y=a,t):(a=wi(s*this.sin_p14+t.y*e*this.cos_p14/i),n=Math.abs(this.lat0)-g,o=Math.abs(n)<=x?0<=this.lat0?q(this.long0+Math.atan2(t.x,-t.y)):q(this.long0-Math.atan2(-t.x,t.y)):q(this.long0+Math.atan2(t.x*e,i*this.cos_p14*s-t.y*this.sin_p14*e)),t.x=o,t.y=a,t)},names:["ortho"]},Ni=1,Ri=2,ji=3,Di=4,qi=5,Gi=6,Fi={AREA_0:1,AREA_1:2,AREA_2:3,AREA_3:4};function Hi(t,i,e,s){var n;return t=g-w/2?this.face=qi:this.lat0<=-(g-w/2)?this.face=Gi:Math.abs(this.long0)<=w?this.face=Ni:Math.abs(this.long0)<=g+w?this.face=0=Math.abs(t.y)?l.value=Fi.AREA_0:0<=t.y&&t.y>=Math.abs(t.x)?(l.value=Fi.AREA_1,i-=g):t.x<0&&-t.x>=Math.abs(t.y)?(l.value=Fi.AREA_2,i=i<0?i+c:i-c):(l.value=Fi.AREA_3,i+=g),n=c/12*Math.tan(i),o=Math.sin(n)/(Math.cos(n)-1/Math.sqrt(2)),o=Math.atan(o),(e=1-(i=Math.cos(i))*i*(e=Math.tan(e))*e*(1-Math.cos(Math.atan(1/Math.cos(o)))))<-1?e=-1:1i.y)--e;else{if(!(Xi[e+1][0]<=i.y))break;++e}var s=Xi[e],n=function(t,i,e,s){for(var n=i;s;--s){var o=t(n);if(n-=o,Math.abs(o){(t=t.split(" ")).forEach(function(t){r.DomUtil[i].call(this,e,t)})},i=(t,i)=>e("addClass",t,i),s=(t,i)=>e("removeClass",t,i),t=r.Marker.extend({initialize(t,i){r.Util.setOptions(this,i),this._latlng=t,this.createIcon()},createIcon(){var t=this.options;let i="";void 0!==t.color&&(i+=`stroke:${t.color};`),void 0!==t.weight&&(i+=`stroke-width:${t.weight};`),void 0!==t.fillColor&&(i+=`fill:${t.fillColor};`),void 0!==t.fillOpacity&&(i+=`fill-opacity:${t.fillOpacity};`),void 0!==t.opacity&&(i+=`opacity:${t.opacity};`);t=this._getIconSVG(t,i);this._locationIcon=r.divIcon({className:t.className,html:t.svg,iconSize:[t.w,t.h]}),this.setIcon(this._locationIcon)},_getIconSVG(t,i){var e=t.radius,s=e+t.weight,t=2*s;return{className:"leaflet-control-locate-location",svg:``+'',w:t,h:t}},setStyle(t){r.Util.setOptions(this,t),this.createIcon()}});var n=t.extend({initialize(t,i,e){r.Util.setOptions(this,e),this._latlng=t,this._heading=i,this.createIcon()},setHeading(t){this._heading=t},_getIconSVG(t,i){var e=t.radius,s=t.width+t.weight,e=2*(e+t.depth+t.weight),t=`M0,0 l${t.width/2},${t.depth} l-${s},0 z`;return{className:"leaflet-control-locate-heading",svg:``+'',w:s,h:e}}}),n=r.Control.extend({options:{position:"topleft",layer:void 0,setView:"untilPanOrZoom",keepCurrentZoomLevel:!1,initialZoomLevel:!1,getLocationBounds(t){return t.bounds},flyTo:!1,clickBehavior:{inView:"stop",outOfView:"setView",inViewNotFollowing:"inView"},returnToPrevBounds:!1,cacheLocation:!0,drawCircle:!0,drawMarker:!0,showCompass:!0,markerClass:t,compassClass:n,circleStyle:{className:"leaflet-control-locate-circle",color:"#136AEC",fillColor:"#136AEC",fillOpacity:.15,weight:0},markerStyle:{className:"leaflet-control-locate-marker",color:"#fff",fillColor:"#2A93EE",fillOpacity:1,weight:3,opacity:1,radius:9},compassStyle:{fillColor:"#2A93EE",fillOpacity:1,weight:0,color:"#fff",opacity:1,radius:9,width:9,depth:6},followCircleStyle:{},followMarkerStyle:{},followCompassStyle:{},icon:"leaflet-control-locate-location-arrow",iconLoading:"leaflet-control-locate-spinner",iconElementTag:"span",textElementTag:"small",circlePadding:[0,0],metric:!0,createButtonCallback(t,i){const e=r.DomUtil.create("a","leaflet-bar-part leaflet-bar-part-single",t);e.title=i.strings.title,e.href="#",e.setAttribute("role","button");const s=r.DomUtil.create(i.iconElementTag,i.icon,e);if(void 0!==i.strings.text){const n=r.DomUtil.create(i.textElementTag,"leaflet-locate-text",e);n.textContent=i.strings.text,e.classList.add("leaflet-locate-text-active"),e.parentNode.style.display="flex",0new r.Control.Locate(t),n},window); //# sourceMappingURL=leaflet.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-area.js b/src/extension/mapml/src/main/resources/viewer/widget/map-area.js index 1b63d05027e..71e2895e080 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-area.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-area.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ import"./leaflet.js";import"./mapml.js";class MapArea extends HTMLAreaElement{static get observedAttributes(){return["coords","alt","href","shape","rel","type","target"]}get alt(){return this.hasAttribute("alt")?this.getAttribute("alt"):""}set alt(t){this.setAttribute("controls",t)}get coords(){return this.hasAttribute("coords")?this.getAttribute("coords"):""}set coords(t){}get href(){return this.hasAttribute("href")?this.getAttribute("href"):""}set href(t){this.href=t}get shape(){return this.hasAttribute("shape")?this.getAttribute("shape"):"default"}set shape(t){(t=t.toLowerCase()).search(/default|circle|rect|poly/)&&(this.shape=t)}get rel(){return this.hasAttribute("rel")?this.getAttribute("rel"):""}set rel(t){this.rel=t}get type(){return this.hasAttribute("type")?this.getAttribute("type"):""}set type(t){this.type=t}get target(){return this.hasAttribute("target")?this.getAttribute("target"):""}constructor(){super()}attributeChangedCallback(t,e,r){}connectedCallback(){this.parentElement.whenReady().then(()=>{this._attachedToMap()})}_attachedToMap(){this._map=this.parentElement._map;var t=this.parentElement._map;if(!this._feature){var e,r,s=this._styleToPathOptions(window.getComputedStyle(this)),i=this.coords?this._coordsToArray(this.coords):null;if(i&&this.parentElement.poster)for(var a=(this.parentElement.poster.width-this.parentElement.width)/2,h=(this.parentElement.poster.height-this.parentElement.height)/2,o=0;o{this.parentElement.querySelector("map-caption").textContent!==t&&this.parentElement.setAttribute("aria-label",this.parentElement.querySelector("map-caption").textContent)}),this.observer.observe(this,{characterData:!0,subtree:!0,attributes:!0,childList:!0}),this.parentElement.hasAttribute("aria-label")||(e=this.textContent,this.parentElement.setAttribute("aria-label",e))}}disconnectedCallback(){this.observer.disconnect()}}export{MapCaption}; //# sourceMappingURL=map-caption.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-extent.js b/src/extension/mapml/src/main/resources/viewer/widget/map-extent.js index 7fc67eec89d..5082467b071 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-extent.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-extent.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ -class MapExtent extends HTMLElement{static get observedAttributes(){return["checked","label","opacity","hidden"]}#hasConnected;get units(){return this.getAttribute("units")||M.FALLBACK_PROJECTION}get checked(){return this.hasAttribute("checked")}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}get label(){return this.hasAttribute("label")?this.getAttribute("label"):M.options.locale.dfExtent}set label(e){e&&this.setAttribute("label",e)}get opacity(){return+(this._opacity??this.getAttribute("opacity"))}set opacity(e){1<+e||+e<0||this.setAttribute("opacity",e)}get hidden(){return this.hasAttribute("hidden")}set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}get extent(){const e=e=>Object.assign(M._convertAndFormatPCRS(e._extentLayer.bounds,M[e.units],e.units),{zoom:e._extentLayer.zoomBounds});var t;return this._extentLayer.bounds?e(this):((t=this)._calculateBounds(),e(t))}getOuterHTML(){let t=this.cloneNode(!0);if(this.querySelector("map-link")){let e=t.querySelectorAll("map-link");e.forEach(e=>{e.hasAttribute("href")?e.setAttribute("href",decodeURI(new URL(e.attributes.href.value,this.baseURI||document.baseURI).href)):e.hasAttribute("tref")&&e.setAttribute("tref",decodeURI(new URL(e.attributes.tref.value,this.baseURI||document.baseURI).href))})}var e=t.outerHTML;return t.remove(),e}zoomTo(){var e=this.extent;let t=this.getMapEl()._map,a=e.topLeft.pcrs.horizontal,r=e.bottomRight.pcrs.horizontal,n=e.bottomRight.pcrs.vertical,i=e.topLeft.pcrs.vertical,o=L.bounds(L.point(a,n),L.point(r,i)),s=t.options.crs.unproject(o.getCenter(!0)),l=e.zoom.maxZoom,h=e.zoom.minZoom;t.setView(s,M.getMaxZoom(o,t,h,l),{animate:!1})}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(e,t,a){if(this.#hasConnected)switch(e){case"units":break;case"label":t!==a&&(this._layerControlHTML.querySelector(".mapml-extent-item-name").innerHTML=a||M.options.locale.dfExtent);break;case"checked":this.parentLayer.whenReady().then(()=>{this._handleChange(),this._calculateBounds(),this._layerControlCheckbox.checked=null!==a}).catch(e=>{console.log("Error while waiting on parentLayer for map-extent checked callback: "+e)});break;case"opacity":t!==a&&(this._opacity=a,this._extentLayer&&this._extentLayer.changeOpacity(a));break;case"hidden":t!==a&&this.parentLayer.whenReady().then(()=>{let e=this.parentLayer._propertiesGroupAnatomy;var t=Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])")).indexOf(this);null!==a?this._layerControlHTML.remove():0===t?e.insertAdjacentElement("afterbegin",this._layerControlHTML):0 map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])"))[t-1]._layerControlHTML.insertAdjacentElement("afterend",this._layerControlHTML),this._validateLayerControlContainerHidden()}).catch(()=>{console.log("Error while waiting on parentLayer for map-extent hidden callback")})}}constructor(){super(),this._createLayerControlExtentHTML=M._createLayerControlExtentHTML.bind(this),this._changeHandler=this._handleChange.bind(this)}async connectedCallback(){this.parentLayer=this.getLayerEl(),this.hasAttribute("data-moving")||this.parentLayer.hasAttribute("data-moving")||(this.mapEl=this.getMapEl(),await this.mapEl.whenProjectionDefined(this.units).catch(()=>{throw new Error("Undefined projection:"+this.units)}),this.isConnected&&(this.#hasConnected=!0,this._map=this.mapEl._map,this.parentLayer.addEventListener("map-change",this._changeHandler),this.mapEl.addEventListener("map-projectionchange",this._changeHandler),this._opacity=this.opacity||1,this._extentLayer=M.extentLayer({opacity:this.opacity,crs:M[this.units],extentZIndex:Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent"):this.parentLayer.querySelectorAll(":scope > map-extent")).indexOf(this),extentEl:this}),this._layerControlHTML=this._createLayerControlExtentHTML(),this._calculateBounds(),this._runMutationObserver(this.children),this._bindMutationObserver()))}_bindMutationObserver(){this._observer=new MutationObserver(e=>{for(var t of e)"childList"===t.type&&this._runMutationObserver(t.addedNodes)}),this._observer.observe(this,{childList:!0})}_runMutationObserver(a){var r=e=>{this.whenReady().then(()=>{this._calculateBounds(),this._validateDisabled()})},n=e=>{this.whenReady().then(()=>{this._extentLayer.appendStyleLink(e)})},i=e=>{this.whenReady().then(()=>{this._extentLayer.appendStyleElement(e)})};for(let t=0;t{let t=r.length,a=0;for(let e=0;e map-meta[name=${e}]`)||this.parentLayer.shadowRoot.querySelector(`:host > map-meta[name=${e}]`):this.querySelector(`:scope > map-meta[name=${e}]`)||this.parentLayer.querySelector(`:scope > map-meta[name=${e}]`)}toggleLayerControlDisabled(){let e=this._layerControlCheckbox,t=this._layerControlLabel,a=this._opacityControl,r=this._opacitySlider,n=this._selectdetails;this.disabled?(e.disabled=!0,r.disabled=!0,t.style.fontStyle="italic",a.style.fontStyle="italic",n&&n.forEach(e=>{e.querySelectorAll("select").forEach(e=>{e.disabled=!0,e.style.fontStyle="italic"}),e.style.fontStyle="italic"})):(e.disabled=!1,r.disabled=!1,t.style.fontStyle="normal",a.style.fontStyle="normal",n&&n.forEach(e=>{e.querySelectorAll("select").forEach(e=>{e.disabled=!1,e.style.fontStyle="normal"}),e.style.fontStyle="normal"}))}_handleChange(){this.checked&&!this.disabled?(this._extentLayer.addTo(this.parentLayer._layer),this._extentLayer.setZIndex(Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent"):this.parentLayer.querySelectorAll(":scope > map-extent")).indexOf(this))):this.parentLayer._layer?.removeLayer(this._extentLayer)}_validateLayerControlContainerHidden(){let e=this.parentLayer._propertiesGroupAnatomy;e&&(0===(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])")).length?e.setAttribute("hidden",""):e.removeAttribute("hidden"))}disconnectedCallback(){this.hasAttribute("data-moving")||this.parentLayer.hasAttribute("data-moving")||!this._extentLayer||(this._validateLayerControlContainerHidden(),this._layerControlHTML.remove(),this.parentLayer._layer&&this.parentLayer._layer.removeLayer(this._extentLayer),this.parentLayer.removeEventListener("map-change",this._changeHandler),this.mapEl.removeEventListener("map-projectionchange",this._changeHandler),delete this._extentLayer,this.parentLayer._layer&&delete this.parentLayer._layer.bounds)}_calculateBounds(){delete this._extentLayer.bounds,delete this._extentLayer.zoomBounds,this.parentLayer._layer&&delete this.parentLayer._layer.bounds;let t=this.querySelectorAll("map-link[rel=image],map-link[rel=tile],map-link[rel=features],map-link[rel=query]"),a=this.querySelector(":scope > map-meta[name=extent][content]")?M.getBoundsFromMeta(this):void 0,r=this.querySelector(":scope > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this):void 0;for(let e=0;e{let r,n;this._extentLayer?t():(r=setInterval(function(e){e._extentLayer?(clearInterval(r),clearTimeout(n),t()):e.isConnected||(clearInterval(r),clearTimeout(n),a("map-extent was disconnected while waiting to be ready"))},300,this),n=setTimeout(function(){clearInterval(r),clearTimeout(n),a("Timeout reached waiting for extent to be ready")},1e4))})}whenLinksReady(){var e;let t=[];for(e of[...this.querySelectorAll("map-link[rel=image],map-link[rel=tile],map-link[rel=features],map-link[rel=query]")])t.push(e.whenReady());return Promise.allSettled(t)}}export{MapExtent}; +class MapExtent extends HTMLElement{static get observedAttributes(){return["checked","label","opacity","hidden"]}#hasConnected;get units(){return this.getAttribute("units")||M.FALLBACK_PROJECTION}get checked(){return this.hasAttribute("checked")}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}get label(){return this.hasAttribute("label")?this.getAttribute("label"):M.options.locale.dfExtent}set label(e){e&&this.setAttribute("label",e)}get opacity(){return+(this._opacity??this.getAttribute("opacity"))}set opacity(e){1<+e||+e<0||this.setAttribute("opacity",e)}get hidden(){return this.hasAttribute("hidden")}set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}get extent(){const e=e=>Object.assign(M._convertAndFormatPCRS(e._extentLayer.bounds,M[e.units],e.units),{zoom:e._extentLayer.zoomBounds});var t;return this._extentLayer.bounds?e(this):((t=this)._calculateBounds(),e(t))}getOuterHTML(){let t=this.cloneNode(!0);if(this.querySelector("map-link")){let e=t.querySelectorAll("map-link");e.forEach(e=>{e.hasAttribute("href")?e.setAttribute("href",decodeURI(new URL(e.attributes.href.value,this.baseURI||document.baseURI).href)):e.hasAttribute("tref")&&e.setAttribute("tref",decodeURI(new URL(e.attributes.tref.value,this.baseURI||document.baseURI).href))})}var e=t.outerHTML;return t.remove(),e}zoomTo(){var e=this.extent;let t=this.getMapEl()._map,a=e.topLeft.pcrs.horizontal,r=e.bottomRight.pcrs.horizontal,n=e.bottomRight.pcrs.vertical,i=e.topLeft.pcrs.vertical,o=L.bounds(L.point(a,n),L.point(r,i)),s=t.options.crs.unproject(o.getCenter(!0)),l=e.zoom.maxZoom,h=e.zoom.minZoom;t.setView(s,M.getMaxZoom(o,t,h,l),{animate:!1})}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(e,t,a){if(this.#hasConnected)switch(e){case"units":break;case"label":t!==a&&(this._layerControlHTML.querySelector(".mapml-extent-item-name").innerHTML=a||M.options.locale.dfExtent);break;case"checked":this.parentLayer.whenReady().then(()=>{this._handleChange(),this._calculateBounds(),this._layerControlCheckbox.checked=null!==a}).catch(e=>{console.log("Error while waiting on parentLayer for map-extent checked callback: "+e)});break;case"opacity":t!==a&&(this._opacity=a,this._extentLayer&&this._extentLayer.changeOpacity(a));break;case"hidden":t!==a&&this.parentLayer.whenReady().then(()=>{let e=this.parentLayer._propertiesGroupAnatomy;var t=Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])")).indexOf(this);null!==a?this._layerControlHTML.remove():0===t?e.insertAdjacentElement("afterbegin",this._layerControlHTML):0 map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])"))[t-1]._layerControlHTML.insertAdjacentElement("afterend",this._layerControlHTML),this._validateLayerControlContainerHidden()}).catch(()=>{console.log("Error while waiting on parentLayer for map-extent hidden callback")})}}constructor(){super(),this._createLayerControlExtentHTML=M._createLayerControlExtentHTML.bind(this),this._changeHandler=this._handleChange.bind(this)}async connectedCallback(){this.parentLayer=this.getLayerEl(),this.hasAttribute("data-moving")||this.parentLayer.hasAttribute("data-moving")||(this.mapEl=this.getMapEl(),await this.mapEl.whenProjectionDefined(this.units).catch(()=>{throw new Error("Undefined projection:"+this.units)}),this.isConnected&&(this.#hasConnected=!0,this._map=this.mapEl._map,this.parentLayer.addEventListener("map-change",this._changeHandler),this.mapEl.addEventListener("map-projectionchange",this._changeHandler),this._opacity=this.opacity||1,this._extentLayer=M.extentLayer({opacity:this.opacity,crs:M[this.units],extentZIndex:Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent"):this.parentLayer.querySelectorAll(":scope > map-extent")).indexOf(this),extentEl:this}),this._layerControlHTML=this._createLayerControlExtentHTML(),this._calculateBounds(),this._runMutationObserver(this.children),this._bindMutationObserver()))}_bindMutationObserver(){this._observer=new MutationObserver(e=>{for(var t of e)"childList"===t.type&&this._runMutationObserver(t.addedNodes)}),this._observer.observe(this,{childList:!0})}_runMutationObserver(a){var r=e=>{this.whenReady().then(()=>{this._calculateBounds(),this._validateDisabled()})},n=e=>{this.whenReady().then(()=>{this._extentLayer.appendStyleLink(e)})},i=e=>{this.whenReady().then(()=>{this._extentLayer.appendStyleElement(e)})};for(let t=0;t{let t=r.length,a=0;for(let e=0;e map-meta[name=${e}]`)||this.parentLayer.shadowRoot.querySelector(`:host > map-meta[name=${e}]`):this.querySelector(`:scope > map-meta[name=${e}]`)||this.parentLayer.querySelector(`:scope > map-meta[name=${e}]`)}toggleLayerControlDisabled(){let e=this._layerControlCheckbox,t=this._layerControlLabel,a=this._opacityControl,r=this._opacitySlider,n=this._selectdetails;this.disabled?(e.disabled=!0,r.disabled=!0,t.style.fontStyle="italic",a.style.fontStyle="italic",n&&n.forEach(e=>{e.querySelectorAll("select").forEach(e=>{e.disabled=!0,e.style.fontStyle="italic"}),e.style.fontStyle="italic"})):(e.disabled=!1,r.disabled=!1,t.style.fontStyle="normal",a.style.fontStyle="normal",n&&n.forEach(e=>{e.querySelectorAll("select").forEach(e=>{e.disabled=!1,e.style.fontStyle="normal"}),e.style.fontStyle="normal"}))}_handleChange(){this.checked&&!this.disabled?(this._extentLayer.addTo(this.parentLayer._layer),this._extentLayer.setZIndex(Array.from(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent"):this.parentLayer.querySelectorAll(":scope > map-extent")).indexOf(this))):this.parentLayer._layer?.removeLayer(this._extentLayer)}_validateLayerControlContainerHidden(){let e=this.parentLayer._propertiesGroupAnatomy;e&&(0===(this.parentLayer.src?this.parentLayer.shadowRoot.querySelectorAll(":host > map-extent:not([hidden])"):this.parentLayer.querySelectorAll(":scope > map-extent:not([hidden])")).length?e.setAttribute("hidden",""):e.removeAttribute("hidden"))}disconnectedCallback(){this.hasAttribute("data-moving")||this.parentLayer.hasAttribute("data-moving")||!this._extentLayer||(this._validateLayerControlContainerHidden(),this._layerControlHTML.remove(),this.parentLayer._layer&&this.parentLayer._layer.removeLayer(this._extentLayer),this.parentLayer.removeEventListener("map-change",this._changeHandler),this.mapEl.removeEventListener("map-projectionchange",this._changeHandler),delete this._extentLayer,this.parentLayer._layer&&delete this.parentLayer._layer.bounds)}_calculateBounds(){delete this._extentLayer.bounds,delete this._extentLayer.zoomBounds,this.parentLayer._layer&&delete this.parentLayer._layer.bounds;let t=this.querySelectorAll("map-link[rel=image],map-link[rel=tile],map-link[rel=features],map-link[rel=query]"),a=this.querySelector(":scope > map-meta[name=extent][content]")?M.getBoundsFromMeta(this):void 0,r=this.querySelector(":scope > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this):void 0;for(let e=0;e{let r,n;this._extentLayer?t():(r=setInterval(function(e){e._extentLayer?(clearInterval(r),clearTimeout(n),t()):e.isConnected||(clearInterval(r),clearTimeout(n),a("map-extent was disconnected while waiting to be ready"))},300,this),n=setTimeout(function(){clearInterval(r),clearTimeout(n),a("Timeout reached waiting for extent to be ready")},1e4))})}whenLinksReady(){var e;let t=[];for(e of[...this.querySelectorAll("map-link[rel=image],map-link[rel=tile],map-link[rel=features],map-link[rel=query]")])t.push(e.whenReady());return Promise.allSettled(t)}}export{MapExtent}; //# sourceMappingURL=map-extent.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-feature.js b/src/extension/mapml/src/main/resources/viewer/widget/map-feature.js index 824b7cc18b3..8a2cd40cfaf 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-feature.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-feature.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ -class MapFeature extends HTMLElement{static get observedAttributes(){return["zoom","min","max"]}#hasConnected;get zoom(){let t={},e=this.getMeta("zoom");return e&&(t=M._metaContentToObject(e.getAttribute("content"))),"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("zoom")?this.getAttribute("zoom"):t.value||t.max||this._initialZoom):+(this.hasAttribute("zoom")?this.getAttribute("zoom"):this._initialZoom)}set zoom(t){t=parseInt(t,10);!isNaN(t)&&t>=this.min&&t<=this.max&&this.setAttribute("zoom",t)}get min(){let t={},e=this.getMeta("zoom");e&&(t=M._metaContentToObject(e.getAttribute("content")));return"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("min")?this.getAttribute("min"):t.min||this._parentEl.getZoomBounds().minZoom):+(this.hasAttribute("min")?this.getAttribute("min"):t.min||0)}set min(t){var e=parseInt(t,10),t=this.getLayerEl().extent.zoom;isNaN(e)||(e>=t.minZoom&&e<=t.maxZoom?this.setAttribute("min",e):this.setAttribute("min",t.minZoom))}get max(){let t={},e=this.getMeta("zoom");e&&(t=M._metaContentToObject(e.getAttribute("content")));var o=this.getMapEl()._map.options.crs.options.resolutions.length-1;return"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("max")?this.getAttribute("max"):t.max||this._parentEl.getZoomBounds().maxZoom):+(this.hasAttribute("max")?this.getAttribute("max"):t.max||o)}set max(t){var e=parseInt(t,10),t=this.getLayerEl().extent.zoom;isNaN(e)||(e>=t.minZoom&&e<=t.maxZoom?this.setAttribute("max",e):this.setAttribute("max",t.maxZoom))}get extent(){if(this.isConnected)return this._getFeatureExtent||(this._getFeatureExtent=this._memoizeExtent()),this._getFeatureExtent()}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(t,e,o){if(this.#hasConnected)switch(t){case"min":case"max":case"zoom":e!==o&&this.reRender(this._featureLayer)}}constructor(){super()}connectedCallback(){this.#hasConnected=!0,this._initialZoom=this.getMapEl().zoom,this._parentEl="LAYER-"===this.parentNode.nodeName.toUpperCase()||"MAP-LINK"===this.parentNode.nodeName.toUpperCase()?this.parentNode:this.parentNode.host,this.getLayerEl().hasAttribute("data-moving")||this._parentEl.parentElement?.hasAttribute("data-moving")||(this._observer=new MutationObserver(t=>{for(var e of t){if("attributes"===e.type&&e.target===this)return;this.reRender(this._featureLayer)}}),this._observer.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,characterData:!0}))}disconnectedCallback(){this.getLayerEl()?.hasAttribute("data-moving")||this._parentEl.parentElement?.hasAttribute("data-moving")||(this._observer.disconnect(),this._featureLayer&&this.removeFeature(this._featureLayer))}reRender(e){if(this._groupEl.isConnected){var o=this._getFallbackCS();let t=document.createElement("span");this._groupEl.insertAdjacentElement("beforebegin",t),e._staticFeature&&e._removeFromFeaturesList(this._geometry),e.removeLayer(this._geometry),this._geometry=e.createGeometry(this,o).addTo(e),t.replaceWith(this._geometry.options.group),e._validateRendering(),delete this._getFeatureExtent,this._setUpEvents()}}removeFeature(t){t.removeLayer(this._geometry),t._staticFeature&&t._removeFromFeaturesList(this._geometry),t.options.properties=null,delete this._geometry,this._getFeatureExtent&&delete this._getFeatureExtent}addFeature(t){this._featureLayer=t;var e,o=this.getLayerEl();this.querySelector("map-geometry")&&(e=this._getFallbackCS(),o.src&&o.shadowRoot,this._geometry=t.createGeometry(this,e),this._geometry&&(t.addLayer(this._geometry),this._setUpEvents()))}_setUpEvents(){["click","focus","blur","keyup","keydown"].forEach(o=>{this._groupEl.addEventListener(o,e=>{if("click"===o){let t=new PointerEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}else if("keyup"===o||"keydown"===o){let t=new KeyboardEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}else{let t=new FocusEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}})})}_getFallbackCS(){let e;if("MAP-LINK"===this._parentEl.nodeName)e=this._parentEl.shadowRoot.querySelector("map-meta[name=cs]")||this._parentEl.parentElement.getMeta("cs");else{let t=this.getLayerEl();e=(t.src?t.shadowRoot:t).querySelector("map-meta[name=cs]")}return e&&M._metaContentToObject(e.getAttribute("content")).content||"gcrs"}_memoizeExtent(){let p;return function(){if(p&&this._getFeatureExtent)return p;{let i=this.getMapEl()._map,t=this.querySelector("map-geometry"),e=t.getAttribute("cs")||this._getFallbackCS(),o=this.zoom,r=t.querySelectorAll("map-point, map-linestring, map-polygon, map-multipoint, map-multilinestring"),s=[1/0,1/0,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];for(var n of r){var m=n.querySelectorAll("map-coordinates");for(let t=0;t]+>/g,"").replace(/\s+/g," ").split(/[<>\ ]/g);switch(t.tagName.toUpperCase()){case"MAP-POINT":o=M._updateExtent(o,+r[0],+r[1]);break;case"MAP-LINESTRING":case"MAP-POLYGON":case"MAP-MULTIPOINT":case"MAP-MULTILINESTRING":for(let t=0;ti&&(i=this.min)),ir&&(i=r),i}getMeta(t){var e=t.toLowerCase();if("cs"===e||"zoom"===e||"projection"===e){var o=this._parentEl.shadowRoot.querySelector(`map-meta[name=${e}][content]`);return"MAP-LINK"===this._parentEl.nodeName?o||this._parentEl.parentElement.getMeta(t):(this._parentEl.src?this._parentEl.shadowRoot:this._parentEl).querySelector(`map-meta[name=${e}][content]`)}}mapml2geojson(t){t=Object.assign({},{propertyFunction:null,transform:!0},t);let e={type:"Feature",properties:{},geometry:{}},o=this.querySelector("map-properties");o?"function"==typeof t.propertyFunction?e.properties=t.propertyFunction(o):o.querySelector("table")?(n=o.querySelector("table").cloneNode(!0),e.properties=M._table2properties(n)):e.properties={prop0:o.innerHTML.replace(/(<([^>]+)>)/gi,"").replace(/\s/g,"")}:e.properties=null;let r=null,i=null,s=this.getMapEl()._map;t.transform&&(r=new proj4.Proj(s.options.crs.code),i=new proj4.Proj("EPSG:4326"),"EPSG:3857"!==s.options.crs.code&&"EPSG:4326"!==s.options.crs.code||(t.transform=!1));var a=this.querySelector("map-geometry").querySelector("map-geometrycollection"),n=this.querySelector("map-geometry").querySelectorAll("map-point, map-polygon, map-linestring, map-multipoint, map-multipolygon, map-multilinestring");if(a){e.geometry.type="GeometryCollection",e.geometry.geometries=[];for(var m of n)e.geometry.geometries.push(M._geometry2geojson(m,r,i,t.transform))}else e.geometry=M._geometry2geojson(n[0],r,i,t.transform);return e}click(){let t=this._groupEl,e=t.getBoundingClientRect();var o=new MouseEvent("click",{clientX:e.x+e.width/2,clientY:e.y+e.height/2,button:0}),r=this.querySelector("map-properties");if("link"===t.getAttribute("role"))for(var i of t.children)i.mousedown.call(this._geometry,o),i.mouseup.call(this._geometry,o);let s=new PointerEvent("click",{cancelable:!0});if(s.originalEvent=o,this.dispatchEvent(s),r&&this.isConnected){let t=this._geometry,e=t._layers;for(var a in e)e[a].isPopupOpen()&&e[a].closePopup();t.isPopupOpen()?t.closePopup():s.originalEvent.cancelBubble||t.openPopup()}}focus(t){this._groupEl.focus(t)}blur(){document.activeElement.shadowRoot?.activeElement!==this._groupEl&&document.activeElement.shadowRoot?.activeElement.parentNode!==this._groupEl||(this._groupEl.blur(),this.getMapEl()._map.getContainer().focus())}zoomTo(){let t=this.extent,e=this.getMapEl()._map,o=t.topLeft.pcrs,r=t.bottomRight.pcrs,i=L.bounds(L.point(o.horizontal,o.vertical),L.point(r.horizontal,r.vertical)),s=e.options.crs.unproject(i.getCenter(!0));e.setView(s,this.getZoomToZoom(),{animate:!1})}whenReady(){return new Promise((e,t)=>{let o,r;this.isConnected?e():(o=setInterval(function(t){t.isConnected&&(clearInterval(o),clearTimeout(r),e())},200,this),r=setTimeout(function(){clearInterval(o),clearTimeout(r),t("Timeout reached waiting for feature to be ready")},5e3))})}}export{MapFeature}; +class MapFeature extends HTMLElement{static get observedAttributes(){return["zoom","min","max"]}#hasConnected;get zoom(){let t={},e=this.getMeta("zoom");return e&&(t=M._metaContentToObject(e.getAttribute("content"))),"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("zoom")?this.getAttribute("zoom"):t.value||t.max||this._initialZoom):+(this.hasAttribute("zoom")?this.getAttribute("zoom"):this._initialZoom)}set zoom(t){t=parseInt(t,10);!isNaN(t)&&t>=this.min&&t<=this.max&&this.setAttribute("zoom",t)}get min(){let t={},e=this.getMeta("zoom");e&&(t=M._metaContentToObject(e.getAttribute("content")));return"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("min")?this.getAttribute("min"):t.min||this._parentEl.getZoomBounds().minZoom):+(this.hasAttribute("min")?this.getAttribute("min"):t.min||0)}set min(t){var e=parseInt(t,10),t=this.getLayerEl().extent.zoom;isNaN(e)||(e>=t.minZoom&&e<=t.maxZoom?this.setAttribute("min",e):this.setAttribute("min",t.minZoom))}get max(){let t={},e=this.getMeta("zoom");e&&(t=M._metaContentToObject(e.getAttribute("content")));var o=this.getMapEl()._map.options.crs.options.resolutions.length-1;return"MAP-LINK"===this._parentEl.nodeName?+(this.hasAttribute("max")?this.getAttribute("max"):t.max||this._parentEl.getZoomBounds().maxZoom):+(this.hasAttribute("max")?this.getAttribute("max"):t.max||o)}set max(t){var e=parseInt(t,10),t=this.getLayerEl().extent.zoom;isNaN(e)||(e>=t.minZoom&&e<=t.maxZoom?this.setAttribute("max",e):this.setAttribute("max",t.maxZoom))}get extent(){if(this.isConnected)return this._getFeatureExtent||(this._getFeatureExtent=this._memoizeExtent()),this._getFeatureExtent()}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(t,e,o){if(this.#hasConnected)switch(t){case"min":case"max":case"zoom":e!==o&&this.reRender(this._featureLayer)}}constructor(){super()}connectedCallback(){this.#hasConnected=!0,this._initialZoom=this.getMapEl().zoom,this._parentEl="LAYER-"===this.parentNode.nodeName.toUpperCase()||"MAP-LINK"===this.parentNode.nodeName.toUpperCase()?this.parentNode:this.parentNode.host,this.getLayerEl().hasAttribute("data-moving")||this._parentEl.parentElement?.hasAttribute("data-moving")||(this._observer=new MutationObserver(t=>{for(var e of t){if("attributes"===e.type&&e.target===this)return;this.reRender(this._featureLayer)}}),this._observer.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,characterData:!0}))}disconnectedCallback(){this.getLayerEl()?.hasAttribute("data-moving")||this._parentEl.parentElement?.hasAttribute("data-moving")||(this._observer.disconnect(),this._featureLayer&&this.removeFeature(this._featureLayer))}reRender(e){if(this._groupEl.isConnected){var o=this._getFallbackCS();let t=document.createElement("span");this._groupEl.insertAdjacentElement("beforebegin",t),e._staticFeature&&e._removeFromFeaturesList(this._geometry),e.removeLayer(this._geometry),this._geometry=e.createGeometry(this,o).addTo(e),t.replaceWith(this._geometry.options.group),e._validateRendering(),delete this._getFeatureExtent,this._setUpEvents()}}removeFeature(t){t.removeLayer(this._geometry),t._staticFeature&&t._removeFromFeaturesList(this._geometry),t.options.properties=null,delete this._geometry,this._getFeatureExtent&&delete this._getFeatureExtent}addFeature(t){this._featureLayer=t;var e,o=this.getLayerEl();this.querySelector("map-geometry")&&(e=this._getFallbackCS(),o.src&&o.shadowRoot,this._geometry=t.createGeometry(this,e),this._geometry&&(t.addLayer(this._geometry),this._setUpEvents()))}_setUpEvents(){["click","focus","blur","keyup","keydown"].forEach(o=>{this._groupEl.addEventListener(o,e=>{if("click"===o){let t=new PointerEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}else if("keyup"===o||"keydown"===o){let t=new KeyboardEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}else{let t=new FocusEvent(o,{cancelable:!0});t.originalEvent=e,this.dispatchEvent(t)}})})}_getFallbackCS(){let e;if("MAP-LINK"===this._parentEl.nodeName)e=this._parentEl.shadowRoot.querySelector("map-meta[name=cs][content]")||this._parentEl.parentElement.getMeta("cs");else{let t=this.getLayerEl();e=(t.src?t.shadowRoot:t).querySelector("map-meta[name=cs][content]")}return e?M._metaContentToObject(e.getAttribute("content")).content:"gcrs"}_memoizeExtent(){let p;return function(){if(p&&this._getFeatureExtent)return p;{let i=this.getMapEl()._map,t=this.querySelector("map-geometry"),e=t.getAttribute("cs")||this._getFallbackCS(),o=this.zoom,r=t.querySelectorAll("map-point, map-linestring, map-polygon, map-multipoint, map-multilinestring"),n=[1/0,1/0,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];for(var a of r){var m=a.querySelectorAll("map-coordinates");for(let t=0;t]+>/g,"").replace(/\s+/g," ").split(/[<>\ ]/g);switch(t.tagName.toUpperCase()){case"MAP-POINT":o=M._updateExtent(o,+r[0],+r[1]);break;case"MAP-LINESTRING":case"MAP-POLYGON":case"MAP-MULTIPOINT":case"MAP-MULTILINESTRING":for(let t=0;ti&&(i=this.min)),ir&&(i=r),i}getMeta(t){var e=t.toLowerCase();if("cs"===e||"zoom"===e||"projection"===e){var o=this._parentEl.shadowRoot.querySelector(`map-meta[name=${e}][content]`);return"MAP-LINK"===this._parentEl.nodeName?o||this._parentEl.parentElement.getMeta(t):(this._parentEl.src?this._parentEl.shadowRoot:this._parentEl).querySelector(`map-meta[name=${e}][content]`)}}mapml2geojson(t){t=Object.assign({},{propertyFunction:null,transform:!0},t);let e={type:"Feature",properties:{},geometry:{}},o=this.querySelector("map-properties");o?"function"==typeof t.propertyFunction?e.properties=t.propertyFunction(o):o.querySelector("table")?(a=o.querySelector("table").cloneNode(!0),e.properties=M._table2properties(a)):e.properties={prop0:o.innerHTML.replace(/(<([^>]+)>)/gi,"").replace(/\s/g,"")}:e.properties=null;let r=null,i=null,n=this.getMapEl()._map;t.transform&&(r=new proj4.Proj(n.options.crs.code),i=new proj4.Proj("EPSG:4326"),"EPSG:3857"!==n.options.crs.code&&"EPSG:4326"!==n.options.crs.code||(t.transform=!1));var s=this.querySelector("map-geometry").querySelector("map-geometrycollection"),a=this.querySelector("map-geometry").querySelectorAll("map-point, map-polygon, map-linestring, map-multipoint, map-multipolygon, map-multilinestring");if(s){e.geometry.type="GeometryCollection",e.geometry.geometries=[];for(var m of a)e.geometry.geometries.push(M._geometry2geojson(m,r,i,t.transform))}else e.geometry=M._geometry2geojson(a[0],r,i,t.transform);return e}click(){let t=this._groupEl,e=t.getBoundingClientRect();var o=new MouseEvent("click",{clientX:e.x+e.width/2,clientY:e.y+e.height/2,button:0}),r=this.querySelector("map-properties");if("link"===t.getAttribute("role"))for(var i of t.children)i.mousedown.call(this._geometry,o),i.mouseup.call(this._geometry,o);let n=new PointerEvent("click",{cancelable:!0});if(n.originalEvent=o,this.dispatchEvent(n),r&&this.isConnected){let t=this._geometry,e=t._layers;for(var s in e)e[s].isPopupOpen()&&e[s].closePopup();t.isPopupOpen()?t.closePopup():n.originalEvent.cancelBubble||t.openPopup()}}focus(t){this._groupEl.focus(t)}blur(){document.activeElement.shadowRoot?.activeElement!==this._groupEl&&document.activeElement.shadowRoot?.activeElement.parentNode!==this._groupEl||(this._groupEl.blur(),this.getMapEl()._map.getContainer().focus())}zoomTo(){let t=this.extent,e=this.getMapEl()._map,o=t.topLeft.pcrs,r=t.bottomRight.pcrs,i=L.bounds(L.point(o.horizontal,o.vertical),L.point(r.horizontal,r.vertical)),n=e.options.crs.unproject(i.getCenter(!0));e.setView(n,this.getZoomToZoom(),{animate:!1})}whenReady(){return new Promise((e,t)=>{let o,r;this.isConnected?e():(o=setInterval(function(t){t.isConnected&&(clearInterval(o),clearTimeout(r),e())},200,this),r=setTimeout(function(){clearInterval(o),clearTimeout(r),t("Timeout reached waiting for feature to be ready")},5e3))})}}export{MapFeature}; //# sourceMappingURL=map-feature.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-input.js b/src/extension/mapml/src/main/resources/viewer/widget/map-input.js index 71a902661fe..c28e2d5a151 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-input.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-input.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ class MapInput extends HTMLElement{static get observedAttributes(){return["name","type","value","axis","units","position","rel","min","max","step"]}get name(){return this.getAttribute("name")}set name(t){t&&this.setAttribute("name",t)}get type(){return this.getAttribute("type")}set type(t){["location"].includes(t)&&this.setAttribute("type",t)}get value(){return this.input.getValue()}set value(t){t&&this.setAttribute("value",t)}get axis(){return this.getAttribute("axis")}set axis(t){t&&this.setAttribute("axis",t)}get units(){return this.getAttribute("units")}set units(t){t&&this.setAttribute("units",t)}get position(){return this.getAttribute("position")}set position(t){t&&this.setAttribute("position",t)}get rel(){return this.getAttribute("rel")}set rel(t){t&&this.setAttribute("rel",t)}get min(){if("zoom"===this.type)return this.hasAttribute("min")?this.getAttribute("min"):this.parentElement.querySelector("map-meta[name=zoom]")?M._metaContentToObject(this.parentElement.querySelector("map-meta[name=zoom]").getAttribute("content")).min:this.getLayerEl().extent?.zoom.minZoom.toString()}set min(t){t&&this.setAttribute("min",t)}get max(){if("zoom"===this.type)return this.hasAttribute("max")?this.getAttribute("max"):this.parentElement.querySelector("map-meta[name=zoom]")?M._metaContentToObject(this.parentElement.querySelector("map-meta[name=zoom]").getAttribute("content")).max:this.getLayerEl().extent?.zoom.maxZoom.toString()}set max(t){t&&this.setAttribute("max",t)}get step(){return"zoom"!==this.type?null:this.getAttribute("step")||"1"}set step(t){t&&this.setAttribute("step",t)}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(e,t,i){this.whenReady().then(()=>{switch(e){case"name":t!==i&&null!==t&&(this.input.name=i);break;case"type":break;case"value":t!==i&&null!==t&&(this.input.value=i);break;case"axis":t!==i&&this.input&&(this.input.axis=i);break;case"units":t!==i&&this.input&&(this.input.units=i);break;case"position":t!==i&&this.input&&(this.input.position=i);break;case"rel":t!==i&&this.input&&(this.input.rel=i);break;case"min":t!==i&&this.input&&(this.input.min=i);break;case"max":t!==i&&this.input&&(this.input.max=i);break;case"step":t!==i&&this.input&&(this.input.step=i)}}).catch(t=>{console.log(t,` in mapInput.attributeChangeCallback when changing attribute `+e)})}constructor(){super()}connectedCallback(){this.parentElement.whenReady().then(()=>{switch("MAP-EXTENT"===this.parentElement.nodeName&&(this._layer=this.parentElement._layer),this.type){case"zoom":this.initialValue=+this.getAttribute("value"),this.input=new M.ZoomInput(this.name,this.min,this.max,this.initialValue,this.step,this._layer);break;case"location":this.input=new M.LocationInput(this.name,this.position,this.axis,this.units,this.min,this.max,this.rel,this._layer);break;case"width":this.input=new M.WidthInput(this.name,this._layer);break;case"height":this.input=new M.HeightInput(this.name,this._layer);break;case"hidden":this.input=new M.HiddenInput(this.name,this.initialValue)}}).catch(t=>{console.log(t,"\nin mapInput.connectedCallback")})}disconnectedCallback(){}checkValidity(){if(this.input.validateInput())return!0;var t=new Event("invalid",{bubbles:!0,cancelable:!0,composed:!0});return this.dispatchEvent(t),!1}reportValidity(){if(this.input.validateInput())return!0;var t=new Event("invalid",{bubbles:!0,cancelable:!0,composed:!0});return this.dispatchEvent(t),console.log("Input type='"+this.type+"' is not valid!"),!1}whenReady(){return new Promise((e,i)=>{let n,s;this.input?e():(n=setInterval(function(t){t.input?(clearInterval(n),clearTimeout(s),e()):t.isConnected||(clearInterval(n),clearTimeout(s),i("map-input was disconnected while waiting to be ready"))},300,this),s=setTimeout(function(){clearInterval(n),clearTimeout(s),i("Timeout reached waiting for input to be ready")},1e4))})}}export{MapInput}; diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-link.js b/src/extension/mapml/src/main/resources/viewer/widget/map-link.js index d56a487cd37..8ff6f506738 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-link.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-link.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ class MapLink extends HTMLElement{static get observedAttributes(){return["type","rel","media","href","hreflang","tref","tms","projection"]}#hasConnected;get type(){return this.getAttribute("type")||"image/*"}set type(t){"text/mapml"!==t&&!t.startsWith("image/")||this.setAttribute("type",t)}get rel(){return this.getAttribute("rel")}set rel(t){["license","alternate","self","style","tile","image","features","zoomin","zoomout","legend","query","stylesheet"].includes(t)&&this.setAttribute("type",t)}get href(){return this.hasAttribute("href")?new URL(this.getAttribute("href"),this.getBase()).href:this.hasAttribute("tref")?this.resolve():void 0}set href(t){t&&this.setAttribute("href",t)}get hreflang(){return this.getAttribute("hreflang")}set hreflang(t){t&&this.setAttribute("hreflang",t)}get tref(){return this.hasAttribute("tref")?this.getAttribute("tref"):M.BLANK_TT_TREF}set tref(t){t&&this.setAttribute("tref",t)}get media(){return M._metaContentToObject(this.getAttribute("media"))}set media(t){this.setAttribute("media",t)}get tms(){return this.hasAttribute("tms")}set tms(t){t&&this.setAttribute("tms","")}get projection(){return this.getAttribute("projection")}set projection(t){["OSMTILE","CBMTILE","WGS84","APSTILE"].includes(t)&&this.setAttribute("projection",t)}get extent(){return this._templateVars?Object.assign(M._convertAndFormatPCRS(this.getBounds(),M[this.parentExtent.units],this.parentExtent.units),{zoom:this.getZoomBounds()}):null}zoomTo(){var h=this.extent;if(h){let t=this.getMapEl()._map,e=h.topLeft.pcrs.horizontal,i=h.bottomRight.pcrs.horizontal,s=h.bottomRight.pcrs.vertical,a=h.topLeft.pcrs.vertical,r=L.bounds(L.point(e,s),L.point(i,a)),o=t.options.crs.unproject(r.getCenter(!0)),n=h.zoom.maxZoom,l=h.zoom.minZoom;t.setView(o,M.getMaxZoom(r,t,l,n),{animate:!1})}}getMapEl(){return M.getClosest(this,"mapml-viewer,map[is=web-map]")}getLayerEl(){return M.getClosest(this,"layer-")}attributeChangedCallback(t,e,i){if(this.#hasConnected)switch(t){case"type":case"rel":case"href":case"hreflang":break;case"tref":e!==i&&this._initTemplateVars()}}constructor(){super()}connectedCallback(){if(this.#hasConnected=!0,!(this.getLayerEl().hasAttribute("data-moving")||this.parentExtent&&this.parentExtent.hasAttribute("data-moving")))switch(this.rel.toLowerCase()){case"tile":case"image":case"features":case"query":this._initTemplateVars(),this._createTemplatedLink();break;case"style":case"self":case"style self":case"self style":this._createSelfOrStyleLink();break;case"zoomin":case"zoomout":case"legend":break;case"stylesheet":this._createStylesheetLink();break;case"alternate":this._createAlternateLink()}}disconnectedCallback(){"stylesheet"===this.rel.toLowerCase()&&this._stylesheetHost&&this.link.remove()}_createAlternateLink(t){this.href&&this.projection&&(this._alternate=!0)}_createStylesheetLink(){var t,e;this._stylesheetHost=this.getRootNode()instanceof ShadowRoot?this.getRootNode().host:this.parentElement,void 0!==this._stylesheetHost&&(this.link=document.createElement("link"),(this.link.mapLink=this).link.setAttribute("href",new URL(this.href,this.getBase()).href),e=(t=this).link,Array.from(t.attributes).forEach(t=>{"href"!==t.nodeName&&e.setAttribute(t.nodeName,t.nodeValue)}),this._stylesheetHost._layer?this._stylesheetHost._layer.appendStyleLink(this):this._stylesheetHost._templatedLayer?this._stylesheetHost._templatedLayer.appendStyleLink(this):this._stylesheetHost._extentLayer&&this._stylesheetHost._extentLayer.appendStyleLink(this))}async _createTemplatedLink(){if(this.parentExtent="MAP-EXTENT"===this.parentNode.nodeName.toUpperCase()?this.parentNode:this.parentNode.host,this.tref&&this.parentExtent){try{await this.parentExtent.whenReady(),await this._templateVars.inputsReady}catch(t){return void console.log("Error while creating templated link: "+t)}this.mapEl=this.getMapEl(),this.zIndex=Array.from(this.parentExtent.querySelectorAll("map-link[rel=image],map-link[rel=tile],map-link[rel=features]")).indexOf(this),"tile"===this.rel?this._templatedLayer=M.templatedTileLayer(this._templateVars,{zoomBounds:this.getZoomBounds(),extentBounds:this.getBounds(),crs:M[this.parentExtent.units],errorTileUrl:"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",zIndex:this.zIndex,pane:this.parentExtent._extentLayer.getContainer(),linkEl:this}).addTo(this.parentExtent._extentLayer):"image"===this.rel?this._templatedLayer=M.templatedImageLayer(this._templateVars,{zoomBounds:this.getZoomBounds(),extentBounds:this.getBounds(),zIndex:this.zIndex,pane:this.parentExtent._extentLayer.getContainer(),linkEl:this}).addTo(this.parentExtent._extentLayer):"features"===this.rel?(this.attachShadow({mode:"open"}),this._templatedLayer=M.templatedFeaturesLayer(this._templateVars,{zoomBounds:this.getZoomBounds(),extentBounds:this.getBounds(),zIndex:this.zIndex,pane:this.parentExtent._extentLayer.getContainer(),linkEl:this}).addTo(this.parentExtent._extentLayer)):"query"===this.rel&&(this.attachShadow({mode:"open"}),L.extend(this._templateVars,this._setupQueryVars(this._templateVars)),L.extend(this._templateVars,{extentBounds:this.getBounds()}))}}_setupQueryVars(t){for(var e={query:{}},i=t.values,s=0;s{let s,a,r;switch(this.rel.toLowerCase()){case"tile":case"image":case"features":r="_templatedLayer";break;case"style":case"self":case"style self":case"self style":r="_styleOption";break;case"query":r="shadowRoot";break;case"alternate":r="_alternate";break;default:e()}this[r]&&e(),s=setInterval(function(t){t[r]?(clearInterval(s),clearTimeout(a),e()):t.isConnected||(clearInterval(s),clearTimeout(a),i("map-link was disconnected while waiting to be ready"))},300,this),a=setTimeout(function(){clearInterval(s),clearTimeout(a),i("Timeout reached waiting for link to be ready")},1e4)})}}export{MapLink}; //# sourceMappingURL=map-link.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-select.js b/src/extension/mapml/src/main/resources/viewer/widget/map-select.js index c64f4c792e0..1d451a9dcd2 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-select.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-select.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ class MapSelect extends HTMLElement{static get observedAttributes(){return["name","id"]}get name(){return this.getAttribute("name")}set name(e){this.setAttribute("name",e)}get id(){return this.getAttribute("id")}set id(e){this.setAttribute("id",e)}attributeChangedCallback(e,t,r){e}constructor(){super()}connectedCallback(){this._extentEl=this.parentElement,this._createLayerControlForSelect()}disconnectedCallback(){}_createLayerControlForSelect(){this.htmlselect=this.transcribe();var e=L.DomUtil.create("details","mapml-layer-item-details mapml-control-layers"),t=L.DomUtil.create("summary"),r=L.DomUtil.create("label");r.innerText=this.getAttribute("name"),r.setAttribute("for",this.getAttribute("id")),t.appendChild(r),e.appendChild(t),e.appendChild(this.htmlselect),this.selectdetails=e;e=function(){this.parentElement._extentLayer.redraw()}.bind(this);this.htmlselect.addEventListener("change",e)}transcribe(){var r=document.createElement("select"),t=this.getAttributeNames();for(let e=0;e{let i,a;this.selectdetails?t():(i=setInterval(function(e){e.selectdetails?(clearInterval(i),clearTimeout(a),t()):e.isConnected||(clearInterval(i),clearTimeout(a),r("map-select was disconnected while waiting to be ready"))},300,this),a=setTimeout(function(){clearInterval(i),clearTimeout(a),r("Timeout reached waiting for map-select to be ready")},1e4))})}}export{MapSelect}; //# sourceMappingURL=map-select.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/map-style.js b/src/extension/mapml/src/main/resources/viewer/widget/map-style.js index ad01a1afbf7..54d68a1a273 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/map-style.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/map-style.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ class MapStyle extends HTMLElement{static get observedAttributes(){}attributeChangedCallback(t,e,s){}constructor(){super()}connectedCallback(){var t,e;this._stylesheetHost=this.getRootNode()instanceof ShadowRoot?this.getRootNode().host:this.parentElement,void 0!==this._stylesheetHost&&(this.styleElement=document.createElement("style"),(this.styleElement.mapStyle=this).styleElement.textContent=this.textContent,e=(t=this).styleElement,Array.from(t.attributes).forEach(t=>{e.setAttribute(t.nodeName,t.nodeValue)}),this._stylesheetHost._layer?this._stylesheetHost._layer.appendStyleElement(this):this._stylesheetHost._templatedLayer?this._stylesheetHost._templatedLayer.appendStyleElement(this):this._stylesheetHost._extentLayer&&this._stylesheetHost._extentLayer.appendStyleElement(this),this._observer=new MutationObserver(()=>{this.styleElement.textContent=this.textContent}),this._observer.observe(this,{childList:!0,subtree:!0,characterData:!0}))}disconnectedCallback(){this._stylesheetHost&&this.styleElement.remove()}}export{MapStyle}; //# sourceMappingURL=map-style.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/mapml-viewer.js b/src/extension/mapml/src/main/resources/viewer/widget/mapml-viewer.js index 9917f976bed..94286b711c1 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/mapml-viewer.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/mapml-viewer.js @@ -1,4 +1,4 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ import"./leaflet.js";import"./mapml.js";import{MapLayer}from"./layer.js";import{MapCaption}from"./map-caption.js";import{MapFeature}from"./map-feature.js";import{MapExtent}from"./map-extent.js";import{MapInput}from"./map-input.js";import{MapSelect}from"./map-select.js";import{MapLink}from"./map-link.js";import{MapStyle}from"./map-style.js";class MapViewer extends HTMLElement{static get observedAttributes(){return["lat","lon","zoom","projection","width","height","controls","static","controlslist"]}get controls(){return this.hasAttribute("controls")}set controls(t){Boolean(t)?this.setAttribute("controls",""):this.removeAttribute("controls")}get controlsList(){return this._controlsList}set controlsList(t){this._controlsList.value=t,this.setAttribute("controlslist",t)}get width(){return+window.getComputedStyle(this).width.replace("px","")}set width(t){this.setAttribute("width",t)}get height(){return+window.getComputedStyle(this).height.replace("px","")}set height(t){this.setAttribute("height",t)}get lat(){return+(this.hasAttribute("lat")?this.getAttribute("lat"):0)}set lat(t){t&&this.setAttribute("lat",t)}get lon(){return+(this.hasAttribute("lon")?this.getAttribute("lon"):0)}set lon(t){t&&this.setAttribute("lon",t)}get projection(){return this.hasAttribute("projection")?this.getAttribute("projection"):"OSMTILE"}set projection(t){t&&this.whenProjectionDefined(t).then(()=>{this.setAttribute("projection",t)}).catch(()=>{throw new Error("Undefined projection:"+t)})}get zoom(){return+(this.hasAttribute("zoom")?this.getAttribute("zoom"):0)}set zoom(t){t=parseInt(t,10);!isNaN(t)&&0<=t&&t<=25&&this.setAttribute("zoom",t)}get layers(){return this.getElementsByTagName("layer-")}get extent(){let t=this._map,e=M.pixelToPCRSBounds(t.getPixelBounds(),t.getZoom(),t.options.projection),o=M._convertAndFormatPCRS(e,t.options.crs,this.projection),i=1/0,n=-1/0;for(let t=0;tn&&(n=this.layers[t].extent.zoom.maxZoom));return o.zoom={minZoom:i!==1/0?i:t.getMinZoom(),maxZoom:n!==-1/0?n:t.getMaxZoom()},o}get static(){return this.hasAttribute("static")}set static(t){Boolean(t)?this.setAttribute("static",""):this.removeAttribute("static")}constructor(){super(),this._source=this.outerHTML,this._history=[],this._historyIndex=-1,this._traversalCall=!1}connectedCallback(){this.whenProjectionDefined(this.projection).then(()=>{this._initShadowRoot(),this._controlsList=new M.DOMTokenList(this.getAttribute("controlslist"),this,"controlslist",["noreload","nofullscreen","nozoom","nolayer","noscale","geolocation"]);var t=window.getComputedStyle(this),e=t.width,t=t.height,e=this.hasAttribute("width")?this.getAttribute("width"):parseInt(e.replace("px","")),t=this.hasAttribute("height")?this.getAttribute("height"):parseInt(t.replace("px",""));this._changeWidth(e),this._changeHeight(t),this._createMap(),this.setAttribute("role","application"),this._toggleStatic();let o=this.querySelector("map-caption");null!==o&&setTimeout(()=>{this.getAttribute("aria-label")===o.innerHTML&&(this.mapCaptionObserver=new MutationObserver(t=>{this.querySelector("map-caption")!==o&&this.removeAttribute("aria-label")}),this.mapCaptionObserver.observe(this,{childList:!0}))},0)}).catch(()=>{throw new Error("Projection not defined")})}_initShadowRoot(){this.shadowRoot||this.attachShadow({mode:"open"});let t=document.createElement("template");t.innerHTML=``;let e=this.shadowRoot;this._container=document.createElement("div");this._container.insertAdjacentHTML("beforeend","");let o=document.createElement("style");o.innerHTML=':host {all: initial;contain: layout size;display: inline-block;height: 150px;width: 300px;border-width: 2px;border-style: inset;}:host([frameborder="0"]) {border-width: 0;}:host([hidden]) {display: none!important;}:host .leaflet-control-container {visibility: hidden!important;}';let i=document.createElement("style");i.innerHTML="mapml-viewer > * {display: none!important;}",this.appendChild(i),this._container.setAttribute("role","region"),this._container.setAttribute("aria-label","Interactive map"),e.appendChild(o),e.appendChild(t.content.cloneNode(!0)),e.appendChild(this._container)}_createMap(){this._map||(this._map=L.map(this._container,{center:new L.LatLng(this.lat,this.lon),minZoom:0,maxZoom:M[this.projection].options.resolutions.length-1,projection:this.projection,query:!0,contextMenu:!0,announceMovement:M.options.announceMovement,featureIndex:!0,mapEl:this,crs:M[this.projection],zoom:this.zoom,zoomControl:!1}),this._addToHistory(),this._createControls(),this._toggleControls(),this._crosshair=M.crosshair().addTo(this._map),M.options.featureIndexOverlayOption&&(this._featureIndexOverlay=M.featureIndexOverlay().addTo(this._map)),this._setUpEvents())}disconnectedCallback(){for(this._removeEvents();this.shadowRoot.firstChild;)this.shadowRoot.removeChild(this.shadowRoot.firstChild);delete this._map,this._deleteControls()}adoptedCallback(){}attributeChangedCallback(t,e,s){switch(t){case"controlslist":this._controlsList&&(!1===this._controlsList.valueSet&&(this._controlsList.value=s),this._toggleControls());break;case"controls":null!==e&&null===s?this._hideControls():null===e&&null!==s&&this._showControls();break;case"height":e!==s&&this._changeHeight(s);break;case"width":e!==s&&this._changeWidth(s);break;case"static":this._toggleStatic();break;case"projection":if(s&&this._map&&this._map.options.projection!==s){const o=(()=>{let t=this.lat,e=this.lon,o=this.zoom;this._map.options.crs=M[s],this._map.options.projection=s;let i=[];this._map.announceMovement.disable();for(var n of this.querySelectorAll("layer-")){n.removeAttribute("disabled");let t=this.removeChild(n);this.appendChild(t),i.push(t.whenReady())}return Promise.allSettled(i).then(()=>{this.zoomTo(t,e,o),M.options.announceMovement&&this._map.announceMovement.enable(),setTimeout(()=>{this.dispatchEvent(new CustomEvent("map-projectionchange"))},0)})}).bind(this);o().then(()=>{if(this._map&&this._map.options.projection!==e&&this._resetHistory(),this._debug)for(let t=0;t<2;t++)this.toggleDebug()})}}}_createControls(){let t=this._map.getSize().y,e=0;this._layerControl=M.layerControl(null,{collapsed:!0,mapEl:this}).addTo(this._map),this._map.on("movestart",this._layerControl.collapse,this._layerControl);let o=M.options.announceScale;"metric"===o&&(o={metric:!0,imperial:!1}),"imperial"===o&&(o={metric:!1,imperial:!0}),this._scaleBar||(this._scaleBar=M.scaleBar(o).addTo(this._map)),!this._zoomControl&&e+93<=t&&(e+=93,this._zoomControl=L.control.zoom().addTo(this._map)),!this._reloadButton&&e+49<=t&&(e+=49,this._reloadButton=M.reloadButton().addTo(this._map)),!this._fullScreenControl&&e+49<=t&&(e+=49,this._fullScreenControl=M.fullscreenButton().addTo(this._map)),this._geolocationButton||(this._geolocationButton=M.geolocationButton().addTo(this._map))}_toggleControls(){!1===this.controls?(this._hideControls(),this._map.contextMenu.toggleContextMenuItem("Controls","disabled")):(this._showControls(),this._map.contextMenu.toggleContextMenuItem("Controls","enabled"))}_hideControls(){this._setControlsVisibility("fullscreen",!0),this._setControlsVisibility("layercontrol",!0),this._setControlsVisibility("reload",!0),this._setControlsVisibility("zoom",!0),this._setControlsVisibility("geolocation",!0),this._setControlsVisibility("scale",!0)}_showControls(){this._setControlsVisibility("fullscreen",!1),this._setControlsVisibility("layercontrol",!1),this._setControlsVisibility("reload",!1),this._setControlsVisibility("zoom",!1),this._setControlsVisibility("geolocation",!0),this._setControlsVisibility("scale",!1),this._controlsList&&this._controlsList.forEach(t=>{switch(t.toLowerCase()){case"nofullscreen":this._setControlsVisibility("fullscreen",!0);break;case"nolayer":this._setControlsVisibility("layercontrol",!0);break;case"noreload":this._setControlsVisibility("reload",!0);break;case"nozoom":this._setControlsVisibility("zoom",!0);break;case"geolocation":this._setControlsVisibility("geolocation",!1);break;case"noscale":this._setControlsVisibility("scale",!0)}}),this._layerControl&&0===this._layerControl._layers.length&&this._layerControl._container.setAttribute("hidden","")}_deleteControls(){delete this._layerControl,delete this._zoomControl,delete this._reloadButton,delete this._fullScreenControl,delete this._geolocationButton,delete this._scaleBar}_setControlsVisibility(t,e){let o;switch(t){case"zoom":this._zoomControl&&(o=this._zoomControl._container);break;case"reload":this._reloadButton&&(o=this._reloadButton._container);break;case"fullscreen":this._fullScreenControl&&(o=this._fullScreenControl._container);break;case"layercontrol":this._layerControl&&(o=this._layerControl._container);break;case"geolocation":this._geolocationButton&&(o=this._geolocationButton._container);break;case"scale":this._scaleBar&&(o=this._scaleBar._container)}o&&(e?([...o.children].forEach(t=>{t.setAttribute("hidden","")}),o.setAttribute("hidden","")):([...o.children].forEach(t=>{t.removeAttribute("hidden")}),o.removeAttribute("hidden")))}_toggleStatic(){var t=this.hasAttribute("static");this._map&&(t?(this._map.dragging.disable(),this._map.touchZoom.disable(),this._map.doubleClickZoom.disable(),this._map.scrollWheelZoom.disable(),this._map.boxZoom.disable(),this._map.keyboard.disable(),this._zoomControl.disable()):(this._map.dragging.enable(),this._map.touchZoom.enable(),this._map.doubleClickZoom.enable(),this._map.scrollWheelZoom.enable(),this._map.boxZoom.enable(),this._map.keyboard.enable(),this._zoomControl.enable()))}_dropHandler(t){t.preventDefault();t=t.dataTransfer.getData("text");M._pasteLayer(this,t)}_dragoverHandler(t){t.preventDefault(),t.dataTransfer.dropEffect="copy"}_removeEvents(){this._map&&(this._map.off(),this.removeEventListener("drop",this._dropHandler,!1),this.removeEventListener("dragover",this._dragoverHandler,!1))}_setUpEvents(){this.addEventListener("drop",this._dropHandler,!1),this.addEventListener("dragover",this._dragoverHandler,!1),this.addEventListener("change",function(t){"LAYER-"===t.target.tagName&&this.dispatchEvent(new CustomEvent("layerchange",{details:{target:this,originalEvent:t}}))},!1);let t=this.getRootNode()instanceof ShadowRoot?this.getRootNode().host:this.parentElement;t.addEventListener("keyup",function(t){9===t.keyCode&&"MAPML-VIEWER"===document.activeElement.nodeName&&document.activeElement.dispatchEvent(new CustomEvent("mapfocused",{detail:{target:this}}))}),this.addEventListener("keydown",function(t){86===t.keyCode&&t.ctrlKey?navigator.clipboard.readText().then(t=>{M._pasteLayer(this,t)}):32===t.keyCode&&"INPUT"!==this.shadowRoot.activeElement.nodeName&&(t.preventDefault(),this._map.fire("keypress",{originalEvent:t}))}),t.addEventListener("mousedown",function(t){"MAPML-VIEWER"===document.activeElement.nodeName&&document.activeElement.dispatchEvent(new CustomEvent("mapfocused",{detail:{target:this}}))}),this._map.on("locationfound",function(t){this.dispatchEvent(new CustomEvent("maplocationfound",{detail:{latlng:t.latlng,accuracy:t.accuracy}}))},this),this._map.on("locationerror",function(t){this.dispatchEvent(new CustomEvent("locationerror",{detail:{error:t.message}}))},this),this._map.on("load",function(){this.dispatchEvent(new CustomEvent("load",{detail:{target:this}}))},this),this._map.on("preclick",function(t){this.dispatchEvent(new CustomEvent("preclick",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("click",function(t){this.dispatchEvent(new CustomEvent("click",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("dblclick",function(t){this.dispatchEvent(new CustomEvent("dblclick",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("mousemove",function(t){this.dispatchEvent(new CustomEvent("mousemove",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("mouseover",function(t){this.dispatchEvent(new CustomEvent("mouseover",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("mouseout",function(t){this.dispatchEvent(new CustomEvent("mouseout",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("mousedown",function(t){this.dispatchEvent(new CustomEvent("mousedown",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("mouseup",function(t){this.dispatchEvent(new CustomEvent("mouseup",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("contextmenu",function(t){this.dispatchEvent(new CustomEvent("contextmenu",{detail:{lat:t.latlng.lat,lon:t.latlng.lng,x:t.containerPoint.x,y:t.containerPoint.y}}))},this),this._map.on("movestart",function(){this.dispatchEvent(new CustomEvent("movestart",{detail:{target:this}}))},this),this._map.on("move",function(){this.dispatchEvent(new CustomEvent("move",{detail:{target:this}}))},this),this._map.on("moveend",function(){this._updateMapCenter(),this._addToHistory(),this.dispatchEvent(new CustomEvent("map-moveend",{detail:{target:this}}))},this),this._map.on("zoomstart",function(){this.dispatchEvent(new CustomEvent("zoomstart",{detail:{target:this}}))},this),this._map.on("zoom",function(){this.dispatchEvent(new CustomEvent("zoom",{detail:{target:this}}))},this),this._map.on("zoomend",function(){this._updateMapCenter(),this.dispatchEvent(new CustomEvent("zoomend",{detail:{target:this}}))},this);const e=(t=>{this.whenLayersReady().then(()=>{t&&t.layer._layerEl&&(this._map.setMaxZoom(this.extent.zoom.maxZoom),this._map.setMinZoom(this.extent.zoom.minZoom))})}).bind(this);this.whenLayersReady().then(()=>{this._map.setMaxZoom(this.extent.zoom.maxZoom),this._map.setMinZoom(this.extent.zoom.minZoom),this._map.on("layeradd layerremove",e,this)}),this.addEventListener("fullscreenchange",function(t){null===document.fullscreenElement?this._map.contextMenu.setViewFullScreenInnerHTML("view"):this._map.contextMenu.setViewFullScreenInnerHTML("exit")}),this.addEventListener("keydown",function(t){"MAPML-VIEWER"===document.activeElement.nodeName&&(t.ctrlKey&&82===t.keyCode?(t.preventDefault(),this.reload()):t.altKey&&39===t.keyCode?(t.preventDefault(),this.forward()):t.altKey&&37===t.keyCode&&(t.preventDefault(),this.back()))})}locate(t){this._geolocationButton&&this._geolocationButton.stop(),t?(t.zoomTo&&(t.setView=t.zoomTo,delete t.zoomTo),this._map.locate(t)):this._map.locate({setView:!0,maxZoom:16})}toggleDebug(){this._debug?(this._debug.remove(),this._debug=void 0):this._debug=M.debugOverlay().addTo(this._map)}_changeWidth(t){this._container&&(this._container.style.width=t+"px",this.shadowRoot.styleSheets[0].cssRules[0].style.width=t+"px"),this._map&&this._map.invalidateSize(!1)}_changeHeight(t){this._container&&(this._container.style.height=t+"px",this.shadowRoot.styleSheets[0].cssRules[0].style.height=t+"px"),this._map&&this._map.invalidateSize(!1)}zoomTo(t,e,o){o=Number.isInteger(+o)?+o:this.zoom;e=new L.LatLng(+t,+e);this._map.setView(e,o),this.zoom=o,this.lat=e.lat,this.lon=e.lng}_updateMapCenter(){this.lat=this._map.getCenter().lat,this.lon=this._map.getCenter().lng,this.zoom=this._map.getZoom()}_resetHistory(){this._history=[],this._historyIndex=-1,this._traversalCall=!1,this._addToHistory()}_addToHistory(){var t;0Math.round(M[e.projection].options.bounds.getSize().x/M[e.projection].options.resolutions[t])},vertical:{name:"y",min:0,max:t=>Math.round(M[e.projection].options.bounds.getSize().y/M[e.projection].options.resolutions[t])},bounds:t=>L.bounds([M[e.projection].options.crs.tcrs.horizontal.min,M[e.projection].options.crs.tcrs.vertical.min],[M[e.projection].options.crs.tcrs.horizontal.max(t),M[e.projection].options.crs.tcrs.vertical.max(t)])},pcrs:{horizontal:{name:"easting",get min(){return M[e.projection].options.bounds.min.x},get max(){return M[e.projection].options.bounds.max.x}},vertical:{name:"northing",get min(){return M[e.projection].options.bounds.min.y},get max(){return M[e.projection].options.bounds.max.y}},get bounds(){return M[e.projection].options.bounds}},gcrs:{horizontal:{name:"longitude",get min(){return M[e.projection].unproject(M.OSMTILE.options.bounds.min).lng},get max(){return M[e.projection].unproject(M.OSMTILE.options.bounds.max).lng}},vertical:{name:"latitude",get min(){return M[e.projection].unproject(M.OSMTILE.options.bounds.min).lat},get max(){return M[e.projection].unproject(M.OSMTILE.options.bounds.max).lat}},get bounds(){return L.latLngBounds([M[e.projection].options.crs.gcrs.vertical.min,M[e.projection].options.crs.gcrs.horizontal.min],[M[e.projection].options.crs.gcrs.vertical.max,M[e.projection].options.crs.gcrs.horizontal.max])}},map:{horizontal:{name:"i",min:0,max:t=>t.getSize().x},vertical:{name:"j",min:0,max:t=>t.getSize().y},bounds:t=>L.bounds(L.point([0,0]),t.getSize())},tile:{horizontal:{name:"i",min:0,max:t},vertical:{name:"j",min:0,max:t},get bounds(){return L.bounds([M[e.projection].options.crs.tile.horizontal.min,M[e.projection].options.crs.tile.vertical.min],[M[e.projection].options.crs.tile.horizontal.max,M[e.projection].options.crs.tile.vertical.max])}},tilematrix:{horizontal:{name:"column",min:0,max:t=>Math.round(M[e.projection].options.crs.tcrs.horizontal.max(t)/M[e.projection].options.crs.tile.bounds.getSize().x)},vertical:{name:"row",min:0,max:t=>Math.round(M[e.projection].options.crs.tcrs.vertical.max(t)/M[e.projection].options.crs.tile.bounds.getSize().y)},bounds:t=>L.bounds([M[e.projection].options.crs.tilematrix.horizontal.min,M[e.projection].options.crs.tilematrix.vertical.min],[M[e.projection].options.crs.tilematrix.horizontal.max(t),M[e.projection].options.crs.tilematrix.vertical.max(t)])}}}),M[e.projection.toUpperCase()]=M[e.projection],e.projection}whenReady(){return new Promise((e,t)=>{let o,i;this._map?e():(o=setInterval(function(t){t._map&&(clearInterval(o),clearTimeout(i),e())},200,this),i=setTimeout(function(){clearInterval(o),clearTimeout(i),t("Timeout reached waiting for map to be ready")},5e3))})}whenLayersReady(){let t=[];for(var e of[...this.layers])t.push(e.whenReady());return Promise.allSettled(t)}whenProjectionDefined(n){return new Promise((e,t)=>{let o,i;M[n]?e():(o=setInterval(function(t){M[t]&&(clearInterval(o),clearTimeout(i),e())},200,n),i=setTimeout(function(){clearInterval(o),clearTimeout(i),t("Timeout reached waiting for projection to be defined")},5e3))})}geojson2mapml(t,e={}){void 0===e.projection&&(e.projection=this.projection);e=M.geojson2mapml(t,e);return this.appendChild(e),e}}window.customElements.define("mapml-viewer",MapViewer),window.customElements.define("layer-",MapLayer),window.customElements.define("map-caption",MapCaption),window.customElements.define("map-feature",MapFeature),window.customElements.define("map-extent",MapExtent),window.customElements.define("map-input",MapInput),window.customElements.define("map-select",MapSelect),window.customElements.define("map-link",MapLink),window.customElements.define("map-style",MapStyle);export{MapViewer}; //# sourceMappingURL=mapml-viewer.js.map \ No newline at end of file diff --git a/src/extension/mapml/src/main/resources/viewer/widget/mapml.js b/src/extension/mapml/src/main/resources/viewer/widget/mapml.js index 4b4dc4887a9..994d449a4c8 100644 --- a/src/extension/mapml/src/main/resources/viewer/widget/mapml.js +++ b/src/extension/mapml/src/main/resources/viewer/widget/mapml.js @@ -1,6 +1,6 @@ -/*! @maps4html/web-map-custom-element 10-04-2024 */ +/*! @maps4html/web-map-custom-element 29-04-2024 */ -!function(){"use strict";function t(t){return new m(t)}function e(t,e){return new c(t,e)}function o(t,e){return new p(t,e)}function i(t,e){return new h(t,e)}function n(t){return new d(t)}function a(t,e){return new _(t,e)}function s(t,e){return new y(t,e)}function r(t,e,o,i,n,a){return new f(t,e,o,i,n,a)}function l(t,e,o){return t||e?new g(t,e,o):null}function u(){return new b}var m=L.GridLayer.extend({initialize:function(t){L.setOptions(this,t),this.zoomBounds=this._getZoomBounds(t.tileContainer,t.maxZoomBound),L.extend(this.options,this.zoomBounds),this._groups=this._groupTiles(this.options.tileContainer.getElementsByTagName("map-tile")),this._bounds=this._getLayerBounds(this._groups,this.options.projection),this.layerBounds=this._bounds[Object.keys(this._bounds)[0]];for(var e of Object.keys(this._bounds))this.layerBounds.extend(this._bounds[e].min),this.layerBounds.extend(this._bounds[e].max)},onAdd:function(t){this._map=t,L.GridLayer.prototype.onAdd.call(this,this._map),this._handleMoveEnd()},getEvents:function(){let t=L.GridLayer.prototype.getEvents.call(this,this._map);return this._parentOnMoveEnd=t.moveend,t.moveend=this._handleMoveEnd,t.move=()=>{},t},isVisible:function(){var t=this._map.getZoom();let e=t;return e=e>this.options.maxNativeZoom?this.options.maxNativeZoom:e,e=e=this.zoomBounds.minZoom&&this._bounds[e]&&this._bounds[e].overlaps(M.pixelToPCRSBounds(this._map.getPixelBounds(),this._map.getZoom(),this._map.options.projection))},_handleMoveEnd:function(t){this.isVisible()&&this._parentOnMoveEnd()},_isValidTile(t){return this._groups[this._tileCoordsToKey(t)]},createTile:function(t){let o=this._groups[this._tileCoordsToKey(t)]||[],i=document.createElement("map-tile"),n=this.getTileSize();i.setAttribute("col",t.x),i.setAttribute("row",t.y),i.setAttribute("zoom",t.z);for(let e=0;ee.options.zIndex?1:0}},initialize:function(t,e){for(var o in L.setOptions(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1,t)this._addLayer(t[o],o,!0)},onAdd:function(){return this._initLayout(),L.DomEvent.on(this._container.getElementsByTagName("a")[0],"keydown",this._focusFirstLayer,this._container),L.DomEvent.on(this._container,"contextmenu",this._preventDefaultContextMenu,this),this._update(),this._layers.length<1&&!this._map._showControls?this._container.setAttribute("hidden",""):this._map._showControls=!0,this._container},onRemove:function(t){L.DomEvent.off(this._container.getElementsByTagName("a")[0],"keydown",this._focusFirstLayer,this._container)},addOrUpdateOverlay:function(t,e){for(var o=!1,i=0;ie.focus(),0)},_update:function(){if(!this._container)return this;L.DomUtil.empty(this._baseLayersList),L.DomUtil.empty(this._overlaysList),this._layerControlInputs=[];var t,e,o,i,n=0;for(this.options.sortLayers&&this._layers.sort((t,e)=>this.options.sortFunction(t.layer,e.layer,t.name,e.name)),o=0;o=e.minZoom);return i&&this._layers&&o&&o.overlaps(M.pixelToPCRSBounds(n.getPixelBounds(),t,n.options.projection))}},onAdd:function(t){this._map=t,L.FeatureGroup.prototype.onAdd.call(this,t),this._staticFeature&&this._validateRendering(),this._queryFeatures&&t.on("featurepagination",this.showPaginationFeature,this)},addLayer:function(t){var e;return L.FeatureGroup.prototype.addLayer.call(this,t),this.options.layerBounds||(this.layerBounds=this.layerBounds?this.layerBounds.extend(t.layerBounds):L.bounds(t.layerBounds.min,t.layerBounds.max),this.zoomBounds?(t.zoomBounds.minZoomthis.zoomBounds.maxZoom&&(this.zoomBounds.maxZoom=t.zoomBounds.maxZoom),t.zoomBounds.minNativeZoomthis.zoomBounds.maxNativeZoom&&(this.zoomBounds.maxNativeZoom=t.zoomBounds.maxNativeZoom)):this.zoomBounds=t.zoomBounds),this._staticFeature&&((e=t.options.mapmlFeature.zoom)in this._features?this._features[e].push(t):this._features[e]=[t],this._validateRendering()),this},addRendering:function(t){L.FeatureGroup.prototype.addLayer.call(this,t)},onRemove:function(t){this._queryFeatures&&(t.off("featurepagination",this.showPaginationFeature,this),delete this._queryFeatures,L.DomUtil.remove(this._container)),L.FeatureGroup.prototype.onRemove.call(this,t),this._map.featureIndex.cleanIndex()},removeLayer:function(o){if(L.FeatureGroup.prototype.removeLayer.call(this,o),!this.options.layerBounds){delete this.layerBounds,delete this.options._leafletLayer.bounds,delete this.zoomBounds,delete this.options._leafletLayer.zoomBounds,delete this._layers[o._leaflet_id],this._removeFromFeaturesList(o);let t,e;var i;for(i of Object.keys(this._layers)){var n=this._layers[i];t?t.extend(n.layerBounds):t=L.bounds(n.layerBounds.min,n.layerBounds.max),e?(n.zoomBounds.minZoome.maxZoom&&(e.maxZoom=n.zoomBounds.maxZoom),n.zoomBounds.minNativeZoome.maxNativeZoom&&(e.maxNativeZoom=n.zoomBounds.maxNativeZoom)):(e={},e.minZoom=n.zoomBounds.minZoom,e.maxZoom=n.zoomBounds.maxZoom,e.minNativeZoom=n.zoomBounds.minNativeZoom,e.maxNativeZoom=n.zoomBounds.maxNativeZoom)}t?this.layerBounds=t:delete this.layerBounds,e?this.zoomBounds=e:(delete this.zoomBounds,delete this.options.zoomBounds)}return this},removeRendering:function(t){L.FeatureGroup.prototype.removeLayer.call(this,t)},_removeFromFeaturesList:function(t){for(var e in this._features)for(let t=0;t=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},_initContainer:function(){this._container||(this._container=L.DomUtil.create("div","leaflet-layer",this.options.pane),L.DomUtil.addClass(this._container,"mapml-templated-tile-container"),this._updateZIndex())},_handleMoveEnd:function(t){this.isVisible()&&this._parentOnMoveEnd()},createTile:function(e){let o=document.createElement("DIV"),i=this.getTileSize();if(L.DomUtil.addClass(o,"mapml-tile-group"),L.DomUtil.addClass(o,"leaflet-tile"),this._template.linkEl.dispatchEvent(new CustomEvent("tileloadstart",{detail:{x:e.x,y:e.y,zoom:e.z,appendTile:t=>{o.appendChild(t)}}})),this._template.type.startsWith("image/")){let t=L.TileLayer.prototype.createTile.call(this,e,function(){});t.width=i.x,t.height=i.y,o.appendChild(t)}else this._url.includes(M.BLANK_TT_TREF)||this._fetchTile(e,o);return o},_mapmlTileReady:function(t){L.DomUtil.addClass(t,"leaflet-tile-loaded")},getPane:function(){return this.options.pane},_fetchTile:function(e,o){var t=this.getTileUrl(e);t&&fetch(t,{redirect:"follow"}).then(function(t){return 200<=t.status&&t.status<300?Promise.resolve(t):(console.log("Looks like there was a problem. Status Code: "+t.status),Promise.reject(t))}).then(function(t){return t.text()}).then(t=>{return(new DOMParser).parseFromString(t,"application/xml")}).then(t=>{this._createFeatures(t,e,o),this._mapmlTileReady(o)}).catch(t=>{console.log("Error Creating Tile")})},_parseStylesheetAsHTML:function(t,e,o){if(o instanceof Element&&t&&t.querySelector("map-link[rel=stylesheet],map-style")){if(e instanceof Element)e=e.getAttribute("href")?e.getAttribute("href"):document.URL;else if(!e||""===e||e instanceof Object)return;for(var i,n,a=[],s=t.querySelectorAll("map-link[rel=stylesheet],map-style"),r=0;r{"href"!==t.nodeName&&e.setAttribute(t.nodeName,t.nodeValue)})}},_createFeatures:function(t,e,o){var i;t.querySelector("map-link[rel=stylesheet],map-style")&&(i=t.querySelector("map-base")&&t.querySelector("map-base").hasAttribute("href")?new URL(t.querySelector("map-base").getAttribute("href")).href:t.URL,this._parseStylesheetAsHTML(t,i,o));let n=L.SVG.create("svg"),a=L.SVG.create("g"),s=this._map.options.crs.options.crs.tile.bounds.max.x,r=e.x*s,l=e.y*s,u=M.featureLayer(null,{projection:this._map.options.projection,tiles:!0,layerBounds:this.extentBounds,zoomBounds:this.zoomBounds,interactive:!1,mapEl:this._linkEl.getMapEl()});var m=M.getNativeVariables(t),c=t.querySelectorAll("map-feature:has(> map-geometry)");for(let t=0;t=this._template.tilematrix.bounds.length||!this._template.tilematrix.bounds[t.z].contains(t))return"";var e,o={},i=this._template.linkEl,n=i.zoomInput;for(e in o[this._template.tilematrix.col.name]=t.x,o[this._template.tilematrix.row.name]=t.y,n&&i.hasAttribute("tref")&&i.getAttribute("tref").includes(`{${n.getAttribute("name")}}`)&&(o[this._template.zoom.name]=this._getZoomForUrl()),o[this._template.pcrs.easting.left]=this._tileMatrixToPCRSPosition(t,"top-left").x,o[this._template.pcrs.easting.right]=this._tileMatrixToPCRSPosition(t,"top-right").x,o[this._template.pcrs.northing.top]=this._tileMatrixToPCRSPosition(t,"top-left").y,o[this._template.pcrs.northing.bottom]=this._tileMatrixToPCRSPosition(t,"bottom-left").y,this._template.tile)["row","col","zoom","left","right","top","bottom"].indexOf(e)<0&&(o[e]=this._template.tile[e]);return this._map&&!this._map.options.crs.infinite&&(t=this._globalTileRange.max.y-t.y,this.options.tms&&(o[this._template.tilematrix.row.name]=t)),o.r=this.options.detectRetina&&L.Browser.retina&&0this.options.maxNativeZoom&&(this._template.step=this.options.maxNativeZoom),t!==e?t=e:t%this._template.step!=0&&(t=Math.floor(t/this._template.step)*this._template.step),t}}),d=L.LayerGroup.extend({initialize:function(t){L.LayerGroup.prototype.initialize.call(this,null,t),this._container=L.DomUtil.create("div","leaflet-layer"),this._extentEl=this.options.extentEl,this.changeOpacity(this.options.opacity),L.DomUtil.addClass(this._container,"mapml-extentlayer-container")},getEvents:function(){return{zoomstart:this._onZoomStart}},_onZoomStart:function(){this.closePopup()},getContainer:function(){return this._container},onAdd:function(t){L.LayerGroup.prototype.onAdd.call(this,t);let e=this.options.extentEl.parentLayer._layer._container;e.appendChild(this._container)},redraw:function(){this.eachLayer(function(t){t.redraw()})},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},onRemove:function(){L.LayerGroup.prototype.onRemove.call(this,this._map),L.DomUtil.remove(this._container)},_previousFeature:function(t){0<=this._count+-1&&(this._count--,this._map.fire("featurepagination",{i:this._count,popup:this}))},_nextFeature:function(t){this._count+1=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},getEvents:function(){return{moveend:this._onMoveEnd}},onAdd:function(e){this._map=e,this.options.pane.appendChild(this._container);var t=this.options.opacity||1,o=this._container;this._features?this._features.eachLayer(t=>t.addTo(e)):this._features=M.featureLayer(null,{renderer:M.featureRenderer(),pane:o,layerBounds:this.extentBounds,zoomBounds:this.zoomBounds,opacity:t,projection:e.options.projection,mapEl:this._linkEl.getMapEl(),onEachFeature:function(t,e){var o=document.createElement("div");o.classList.add("mapml-popup-content"),o.insertAdjacentHTML("afterbegin",t.innerHTML),e.bindPopup(o,{autoClose:!1,minWidth:108})}}),this._onMoveEnd()},onRemove:function(){this._features&&this._features.eachLayer(t=>t.remove()),L.DomUtil.remove(this._container)},appendStyleLink:function(e){if(e.link){let t=this._getStylePositionAndNode();t.node.insertAdjacentElement(t.position,e.link)}},_getStylePositionAndNode:function(){return this._container.lastChild&&("SVG"===this._container.lastChild.nodeName.toUpperCase()||this._container.lastChild.classList.contains("mapml-vector-container"))?{position:"beforebegin",node:this._container.lastChild}:this._container.lastChild?{position:"afterend",node:this._container.lastChild}:{position:"afterbegin",node:this._container}},appendStyleElement:function(e){if(e.styleElement){let t=this._getStylePositionAndNode();t.node.insertAdjacentElement(t.position,e.styleElement)}},redraw:function(){this._onMoveEnd()},_removeCSS:function(){var o=this._container.querySelectorAll("link[rel=stylesheet],style");for(let e=0;e *, map-body > *");for(let t=0;t{for(let t=0;t=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},_clearLayer:function(){var e=this._container.querySelectorAll("img");for(let t=0;t map-meta[name=extent][content]")?M.getBoundsFromMeta(this._layerEl.shadowRoot):this._layerEl.querySelector(":scope > map-meta[name=extent][content]")?M.getBoundsFromMeta(this._layerEl):void 0,r=this._layerEl.src&&this._layerEl.shadowRoot.querySelector(":host > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this._layerEl.shadowRoot):this._layerEl.querySelector(":scope > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this._layerEl):void 0;const l=(this._layerEl.src?this._layerEl.shadowRoot:this._layerEl).querySelectorAll("map-extent");["_staticTileLayer","_mapmlvectors","_extentLayer"].forEach(t=>{let e,o,i,n;if(r&&(e=r.maxZoom,o=r.minZoom,n=r.maxNativeZoom||-1/0,i=r.minNativeZoom||1/0),"_extentLayer"===t&&l.length)for(let t=0;t'+e+"");L.setOptions(n,{attribution:e});var e=a.querySelector("map-link[rel=legend]");e&&(n._legendUrl=e.getAttribute("href"));n._map&&n._map.hasLayer(n)&&n._map.attributionControl.addAttribution(n.getAttribution())}(),a.querySelector("map-title")?(n._title=a.querySelector("map-title").textContent.trim(),n._titleIsReadOnly=!0):a instanceof Element&&a.hasAttribute("label")&&(n._title=a.getAttribute("label").trim()),M[n.options.projection]&&function(){if(a.querySelector("map-tile")){var t=document.createElement("map-tiles"),e=a.querySelector("map-meta[name=zoom][content]")||a.querySelector("map-input[type=zoom][value]");t.setAttribute("zoom",e&&e.getAttribute("content")||e&&e.getAttribute("value")||"0");for(var o=a.getElementsByTagName("map-tile"),i=0;i{L.DomEvent.stop(t),s.featureIndex._sortIndex(),s.closePopup(),s._container.focus()},n);let r=L.DomUtil.create("button","mapml-popup-button",t);r.type="button",r.title="Previous Feature",r.innerHTML="",L.DomEvent.on(r,"click",e._previousFeature,n);let l=L.DomUtil.create("p","mapml-feature-count",t),u=this._totalFeatureCount||1;l.innerText=n._count+1+"/"+u;let m=L.DomUtil.create("button","mapml-popup-button",t);m.type="button",m.title="Next Feature",m.innerHTML="",L.DomEvent.on(m,"click",e._nextFeature,n);let c=L.DomUtil.create("button","mapml-popup-button",t);c.type="button",c.title="Focus Controls",c.innerHTML="",L.DomEvent.on(c,"click",t=>{s.featureIndex._sortIndex(),s.featureIndex.currentIndex=s.featureIndex.inBoundFeatures.length-1,s.featureIndex.inBoundFeatures[0]?.path.setAttribute("tabindex",-1),s.featureIndex.inBoundFeatures[s.featureIndex.currentIndex]?.path.setAttribute("tabindex",0),L.DomEvent.stop(t),s.closePopup(),s._controlContainer.querySelector("A:not([hidden])").focus()},n);var p=L.DomUtil.create("hr","mapml-popup-divider");function h(t){let e=t.originalEvent.path||t.originalEvent.composedPath();var o=9===t.originalEvent.keyCode,i=t.originalEvent.shiftKey;(e[0].classList.contains("leaflet-popup-close-button")&&o&&!i||27===t.originalEvent.keyCode||e[0].classList.contains("leaflet-popup-close-button")&&13===t.originalEvent.keyCode||e[0].classList.contains("mapml-popup-content")&&o&&i||e[0]===n._content.querySelector("a")&&o&&i)&&setTimeout(()=>{s.closePopup(n),a.focus(),L.DomEvent.stop(t)},0)}function d(t){let e=t.originalEvent.path||t.originalEvent.composedPath();var o=9===t.originalEvent.keyCode,i=t.originalEvent.shiftKey;13===t.originalEvent.keyCode&&e[0].classList.contains("leaflet-popup-close-button")||27===t.originalEvent.keyCode?(L.DomEvent.stopPropagation(t),s.closePopup(n),s._container.focus(),27!==t.originalEvent.keyCode&&(s._popupClosed=!0)):o&&e[0].classList.contains("leaflet-popup-close-button")?s.closePopup(n):e[0].classList.contains("mapml-popup-content")&&o&&i?(s.closePopup(n),setTimeout(()=>{L.DomEvent.stop(t),s._container.focus()},0)):e[0]===n._content.querySelector("a")&&o&&i&&(s.closePopup(n),setTimeout(()=>{L.DomEvent.stop(t),s.getContainer.focus()},0))}function _(o){let i=this._content,n=o?o.currFeature:this._source._groupLayer._featureEl;if(i.querySelector("a.mapml-zoom-link")&&i.querySelector("a.mapml-zoom-link").remove(),n.querySelector("map-geometry")){var a=n.extent.topLeft.gcrs,o=n.extent.bottomRight.gcrs,o=L.latLngBounds(L.latLng(a.horizontal,a.vertical),L.latLng(o.horizontal,o.vertical)).getCenter(!0);let t=document.createElement("a");t.href=`#${n.getZoomToZoom()},${o.lng},`+o.lat,t.innerHTML=""+M.options.locale.popupZoom,t.className="mapml-zoom-link",t.onclick=t.onkeydown=function(t){(t instanceof MouseEvent||13===t.keyCode)&&(t.preventDefault(),n.zoomTo(),s.closePopup(),s.getContainer().focus())};let e=i.querySelector(".mapml-zoom-link");e&&e.remove(),i.insertBefore(t,i.querySelector("hr.mapml-popup-divider"))}}n._navigationBar=t,n._content.appendChild(p),n._content.appendChild(t),o.focus(),a&&!M.options.featureIndexOverlayOption?(a.setAttribute("aria-expanded","true"),s.on("keydown",h)):s.on("keydown",d),s.on("popupclose",function t(e){e.popup===n&&(s.off("keydown",h),s.off("keydown",d),s.off("popupopen",_),s.off("popupclose",t),a&&a.setAttribute("aria-expanded","false"))})}}),b=L.Layer.extend({onAdd:function(t){var e=t.getSize();(400{},t},isVisible:function(){var t=this._map.getZoom();let e=t;return e=e>this.options.maxNativeZoom?this.options.maxNativeZoom:e,e=e=this.zoomBounds.minZoom&&this._bounds[e]&&this._bounds[e].overlaps(M.pixelToPCRSBounds(this._map.getPixelBounds(),this._map.getZoom(),this._map.options.projection))},_handleMoveEnd:function(t){this.isVisible()&&this._parentOnMoveEnd()},_isValidTile(t){return this._groups[this._tileCoordsToKey(t)]},createTile:function(t){let o=this._groups[this._tileCoordsToKey(t)]||[],i=document.createElement("map-tile"),n=this.getTileSize();i.setAttribute("col",t.x),i.setAttribute("row",t.y),i.setAttribute("zoom",t.z);for(let e=0;ee.options.zIndex?1:0}},initialize:function(t,e){for(var o in L.setOptions(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1,t)this._addLayer(t[o],o,!0)},onAdd:function(){return this._initLayout(),L.DomEvent.on(this._container.getElementsByTagName("a")[0],"keydown",this._focusFirstLayer,this._container),L.DomEvent.on(this._container,"contextmenu",this._preventDefaultContextMenu,this),this._update(),this._layers.length<1&&!this._map._showControls?this._container.setAttribute("hidden",""):this._map._showControls=!0,this._container},onRemove:function(t){L.DomEvent.off(this._container.getElementsByTagName("a")[0],"keydown",this._focusFirstLayer,this._container)},addOrUpdateOverlay:function(t,e){for(var o=!1,i=0;ie.focus(),0)},_update:function(){if(!this._container)return this;L.DomUtil.empty(this._baseLayersList),L.DomUtil.empty(this._overlaysList),this._layerControlInputs=[];var t,e,o,i,n=0;for(this.options.sortLayers&&this._layers.sort((t,e)=>this.options.sortFunction(t.layer,e.layer,t.name,e.name)),o=0;o=e.minZoom);return i&&this._layers&&o&&o.overlaps(M.pixelToPCRSBounds(n.getPixelBounds(),t,n.options.projection))}},onAdd:function(t){this._map=t,L.FeatureGroup.prototype.onAdd.call(this,t),this._staticFeature&&this._validateRendering(),this._queryFeatures&&t.on("featurepagination",this.showPaginationFeature,this)},addLayer:function(t){var e;return L.FeatureGroup.prototype.addLayer.call(this,t),this.options.layerBounds||(this.layerBounds=this.layerBounds?this.layerBounds.extend(t.layerBounds):L.bounds(t.layerBounds.min,t.layerBounds.max),this.zoomBounds?(t.zoomBounds.minZoomthis.zoomBounds.maxZoom&&(this.zoomBounds.maxZoom=t.zoomBounds.maxZoom),t.zoomBounds.minNativeZoomthis.zoomBounds.maxNativeZoom&&(this.zoomBounds.maxNativeZoom=t.zoomBounds.maxNativeZoom)):this.zoomBounds=t.zoomBounds),this._staticFeature&&((e=t.options.mapmlFeature.zoom)in this._features?this._features[e].push(t):this._features[e]=[t],this._validateRendering()),this},addRendering:function(t){L.FeatureGroup.prototype.addLayer.call(this,t)},onRemove:function(t){this._queryFeatures&&(t.off("featurepagination",this.showPaginationFeature,this),delete this._queryFeatures,L.DomUtil.remove(this._container)),L.FeatureGroup.prototype.onRemove.call(this,t),this._map.featureIndex.cleanIndex()},removeLayer:function(o){if(L.FeatureGroup.prototype.removeLayer.call(this,o),!this.options.layerBounds){delete this.layerBounds,delete this.options._leafletLayer.bounds,delete this.zoomBounds,delete this.options._leafletLayer.zoomBounds,delete this._layers[o._leaflet_id],this._removeFromFeaturesList(o);let t,e;var i;for(i of Object.keys(this._layers)){var n=this._layers[i];t?t.extend(n.layerBounds):t=L.bounds(n.layerBounds.min,n.layerBounds.max),e?(n.zoomBounds.minZoome.maxZoom&&(e.maxZoom=n.zoomBounds.maxZoom),n.zoomBounds.minNativeZoome.maxNativeZoom&&(e.maxNativeZoom=n.zoomBounds.maxNativeZoom)):(e={},e.minZoom=n.zoomBounds.minZoom,e.maxZoom=n.zoomBounds.maxZoom,e.minNativeZoom=n.zoomBounds.minNativeZoom,e.maxNativeZoom=n.zoomBounds.maxNativeZoom)}t?this.layerBounds=t:delete this.layerBounds,e?this.zoomBounds=e:(delete this.zoomBounds,delete this.options.zoomBounds)}return this},removeRendering:function(t){L.FeatureGroup.prototype.removeLayer.call(this,t)},_removeFromFeaturesList:function(t){for(var e in this._features)for(let t=0;t=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},_initContainer:function(){this._container||(this._container=L.DomUtil.create("div","leaflet-layer",this.options.pane),L.DomUtil.addClass(this._container,"mapml-templated-tile-container"),this._updateZIndex())},_handleMoveEnd:function(t){this.isVisible()&&this._parentOnMoveEnd()},createTile:function(e){let o=document.createElement("DIV"),i=this.getTileSize();if(L.DomUtil.addClass(o,"mapml-tile-group"),L.DomUtil.addClass(o,"leaflet-tile"),this._template.linkEl.dispatchEvent(new CustomEvent("tileloadstart",{detail:{x:e.x,y:e.y,zoom:e.z,appendTile:t=>{o.appendChild(t)}}})),this._template.type.startsWith("image/")){let t=L.TileLayer.prototype.createTile.call(this,e,function(){});t.width=i.x,t.height=i.y,o.appendChild(t)}else this._url.includes(M.BLANK_TT_TREF)||this._fetchTile(e,o);return o},_mapmlTileReady:function(t){L.DomUtil.addClass(t,"leaflet-tile-loaded")},getPane:function(){return this.options.pane},_fetchTile:function(e,o){var t=this.getTileUrl(e);t&&fetch(t,{redirect:"follow"}).then(function(t){return 200<=t.status&&t.status<300?Promise.resolve(t):(console.log("Looks like there was a problem. Status Code: "+t.status),Promise.reject(t))}).then(function(t){return t.text()}).then(t=>{return(new DOMParser).parseFromString(t,"application/xml")}).then(t=>{this._createFeatures(t,e,o),this._mapmlTileReady(o)}).catch(t=>{console.log("Error Creating Tile")})},_parseStylesheetAsHTML:function(t,e,o){if(o instanceof Element&&t&&t.querySelector("map-link[rel=stylesheet],map-style")){if(e instanceof Element)e=e.getAttribute("href")?e.getAttribute("href"):document.URL;else if(!e||""===e||e instanceof Object)return;for(var i,n,a=[],s=t.querySelectorAll("map-link[rel=stylesheet],map-style"),r=0;r{"href"!==t.nodeName&&e.setAttribute(t.nodeName,t.nodeValue)})}},_createFeatures:function(t,e,o){var i;t.querySelector("map-link[rel=stylesheet],map-style")&&(i=t.querySelector("map-base")&&t.querySelector("map-base").hasAttribute("href")?new URL(t.querySelector("map-base").getAttribute("href")).href:t.URL,this._parseStylesheetAsHTML(t,i,o));let n=L.SVG.create("svg"),a=L.SVG.create("g"),s=this._map.options.crs.options.crs.tile.bounds.max.x,r=e.x*s,l=e.y*s,u=M.featureLayer(null,{projection:this._map.options.projection,tiles:!0,layerBounds:this.extentBounds,zoomBounds:this.zoomBounds,interactive:!1,mapEl:this._linkEl.getMapEl()});var m=M.getNativeVariables(t),c=t.querySelectorAll("map-feature:has(> map-geometry)");for(let t=0;t=this._template.tilematrix.bounds.length||!this._template.tilematrix.bounds[t.z].contains(t))return"";var e,o={},i=this._template.linkEl,n=i.zoomInput;for(e in o[this._template.tilematrix.col.name]=t.x,o[this._template.tilematrix.row.name]=t.y,n&&i.hasAttribute("tref")&&i.getAttribute("tref").includes(`{${n.getAttribute("name")}}`)&&(o[this._template.zoom.name]=this._getZoomForUrl()),o[this._template.pcrs.easting.left]=this._tileMatrixToPCRSPosition(t,"top-left").x,o[this._template.pcrs.easting.right]=this._tileMatrixToPCRSPosition(t,"top-right").x,o[this._template.pcrs.northing.top]=this._tileMatrixToPCRSPosition(t,"top-left").y,o[this._template.pcrs.northing.bottom]=this._tileMatrixToPCRSPosition(t,"bottom-left").y,this._template.tile)["row","col","zoom","left","right","top","bottom"].indexOf(e)<0&&(o[e]=this._template.tile[e]);return this._map&&!this._map.options.crs.infinite&&(t=this._globalTileRange.max.y-t.y,this.options.tms&&(o[this._template.tilematrix.row.name]=t)),o.r=this.options.detectRetina&&L.Browser.retina&&0this.options.maxNativeZoom&&(this._template.step=this.options.maxNativeZoom),t!==e?t=e:t%this._template.step!=0&&(t=Math.floor(t/this._template.step)*this._template.step),t}}),d=L.LayerGroup.extend({initialize:function(t){L.LayerGroup.prototype.initialize.call(this,null,t),this._container=L.DomUtil.create("div","leaflet-layer"),this._extentEl=this.options.extentEl,this.changeOpacity(this.options.opacity),L.DomUtil.addClass(this._container,"mapml-extentlayer-container")},getEvents:function(){return{zoomstart:this._onZoomStart}},_onZoomStart:function(){this.closePopup()},getContainer:function(){return this._container},onAdd:function(t){L.LayerGroup.prototype.onAdd.call(this,t);let e=this.options.extentEl.parentLayer._layer._container;e.appendChild(this._container)},redraw:function(){this.eachLayer(function(t){t.redraw()})},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},onRemove:function(){L.LayerGroup.prototype.onRemove.call(this,this._map),L.DomUtil.remove(this._container)},_previousFeature:function(t){0<=this._count+-1&&(this._count--,this._map.fire("featurepagination",{i:this._count,popup:this}))},_nextFeature:function(t){this._count+1=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},getEvents:function(){return{moveend:this._onMoveEnd}},onAdd:function(e){this._map=e,this.options.pane.appendChild(this._container);var t=this.options.opacity||1,o=this._container;this._features?this._features.eachLayer(t=>t.addTo(e)):this._features=M.featureLayer(null,{renderer:M.featureRenderer(),pane:o,layerBounds:this.extentBounds,zoomBounds:this.zoomBounds,opacity:t,projection:e.options.projection,mapEl:this._linkEl.getMapEl(),onEachFeature:function(t,e){var o=document.createElement("div");o.classList.add("mapml-popup-content"),o.insertAdjacentHTML("afterbegin",t.innerHTML),e.bindPopup(o,{autoClose:!1,minWidth:108})}}),this._onMoveEnd()},onRemove:function(){this._features&&this._features.eachLayer(t=>t.remove()),L.DomUtil.remove(this._container)},appendStyleLink:function(e){if(e.link){let t=this._getStylePositionAndNode();t.node.insertAdjacentElement(t.position,e.link)}},_getStylePositionAndNode:function(){return this._container.lastChild&&("SVG"===this._container.lastChild.nodeName.toUpperCase()||this._container.lastChild.classList.contains("mapml-vector-container"))?{position:"beforebegin",node:this._container.lastChild}:this._container.lastChild?{position:"afterend",node:this._container.lastChild}:{position:"afterbegin",node:this._container}},appendStyleElement:function(e){if(e.styleElement){let t=this._getStylePositionAndNode();t.node.insertAdjacentElement(t.position,e.styleElement)}},redraw:function(){this._onMoveEnd()},_removeCSS:function(){var o=this._container.querySelectorAll("link[rel=stylesheet],style");for(let e=0;e *, map-body > *");for(let t=0;t{for(let t=0;t=this.zoomBounds.minZoom&&this.extentBounds.overlaps(o)},_clearLayer:function(){var e=this._container.querySelectorAll("img");for(let t=0;t map-meta[name=extent][content]")?M.getBoundsFromMeta(this._layerEl.shadowRoot):this._layerEl.querySelector(":scope > map-meta[name=extent][content]")?M.getBoundsFromMeta(this._layerEl):void 0,r=this._layerEl.src&&this._layerEl.shadowRoot.querySelector(":host > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this._layerEl.shadowRoot):this._layerEl.querySelector(":scope > map-meta[name=zoom][content]")?M.getZoomBoundsFromMeta(this._layerEl):void 0;const l=(this._layerEl.src?this._layerEl.shadowRoot:this._layerEl).querySelectorAll("map-extent");["_staticTileLayer","_mapmlvectors","_extentLayer"].forEach(t=>{let e,o,i,n;if(r&&(e=r.maxZoom,o=r.minZoom,n=r.maxNativeZoom||-1/0,i=r.minNativeZoom||1/0),"_extentLayer"===t&&l.length)for(let t=0;t'+e+"");L.setOptions(n,{attribution:e});var e=a.querySelector("map-link[rel=legend]");e&&(n._legendUrl=e.getAttribute("href"));n._map&&n._map.hasLayer(n)&&n._map.attributionControl.addAttribution(n.getAttribution())}(),a.querySelector("map-title")?(n._title=a.querySelector("map-title").textContent.trim(),n._titleIsReadOnly=!0):a instanceof Element&&a.hasAttribute("label")&&(n._title=a.getAttribute("label").trim()),M[n.options.projection]&&function(){if(a.querySelector("map-tile")){var t=document.createElement("map-tiles"),e=a.querySelector("map-meta[name=zoom][content]")||a.querySelector("map-input[type=zoom][value]");t.setAttribute("zoom",e&&e.getAttribute("content")||e&&e.getAttribute("value")||"0");for(var o=a.getElementsByTagName("map-tile"),i=0;i{L.DomEvent.stop(t),s.featureIndex._sortIndex(),s.closePopup(),s._container.focus()},n);let r=L.DomUtil.create("button","mapml-popup-button",t);r.type="button",r.title="Previous Feature",r.innerHTML="",L.DomEvent.on(r,"click",e._previousFeature,n);let l=L.DomUtil.create("p","mapml-feature-count",t),u=this._totalFeatureCount||1;l.innerText=n._count+1+"/"+u;let m=L.DomUtil.create("button","mapml-popup-button",t);m.type="button",m.title="Next Feature",m.innerHTML="",L.DomEvent.on(m,"click",e._nextFeature,n);let c=L.DomUtil.create("button","mapml-popup-button",t);c.type="button",c.title="Focus Controls",c.innerHTML="",L.DomEvent.on(c,"click",t=>{s.featureIndex._sortIndex(),s.featureIndex.currentIndex=s.featureIndex.inBoundFeatures.length-1,s.featureIndex.inBoundFeatures[0]?.path.setAttribute("tabindex",-1),s.featureIndex.inBoundFeatures[s.featureIndex.currentIndex]?.path.setAttribute("tabindex",0),L.DomEvent.stop(t),s.closePopup(),s._controlContainer.querySelector("A:not([hidden])").focus()},n);var p=L.DomUtil.create("hr","mapml-popup-divider");function h(t){let e=t.originalEvent.path||t.originalEvent.composedPath();var o=9===t.originalEvent.keyCode,i=t.originalEvent.shiftKey;(e[0].classList.contains("leaflet-popup-close-button")&&o&&!i||27===t.originalEvent.keyCode||e[0].classList.contains("leaflet-popup-close-button")&&13===t.originalEvent.keyCode||e[0].classList.contains("mapml-popup-content")&&o&&i||e[0]===n._content.querySelector("a")&&o&&i)&&setTimeout(()=>{s.closePopup(n),a.focus(),L.DomEvent.stop(t)},0)}function d(t){let e=t.originalEvent.path||t.originalEvent.composedPath();var o=9===t.originalEvent.keyCode,i=t.originalEvent.shiftKey;13===t.originalEvent.keyCode&&e[0].classList.contains("leaflet-popup-close-button")||27===t.originalEvent.keyCode?(L.DomEvent.stopPropagation(t),s.closePopup(n),s._container.focus(),27!==t.originalEvent.keyCode&&(s._popupClosed=!0)):o&&e[0].classList.contains("leaflet-popup-close-button")?s.closePopup(n):e[0].classList.contains("mapml-popup-content")&&o&&i?(s.closePopup(n),setTimeout(()=>{L.DomEvent.stop(t),s._container.focus()},0)):e[0]===n._content.querySelector("a")&&o&&i&&(s.closePopup(n),setTimeout(()=>{L.DomEvent.stop(t),s.getContainer.focus()},0))}function _(o){let i=this._content,n=o?o.currFeature:this._source._groupLayer._featureEl;if(i.querySelector("a.mapml-zoom-link")&&i.querySelector("a.mapml-zoom-link").remove(),n.querySelector("map-geometry")){var a=n.extent.topLeft.gcrs,o=n.extent.bottomRight.gcrs,o=L.latLngBounds(L.latLng(a.horizontal,a.vertical),L.latLng(o.horizontal,o.vertical)).getCenter(!0);let t=document.createElement("a");t.href=`#${n.getZoomToZoom()},${o.lng},`+o.lat,t.innerHTML=""+M.options.locale.popupZoom,t.className="mapml-zoom-link",t.onclick=t.onkeydown=function(t){(t instanceof MouseEvent||13===t.keyCode)&&(t.preventDefault(),n.zoomTo(),s.closePopup(),s.getContainer().focus())};let e=i.querySelector(".mapml-zoom-link");e&&e.remove(),i.insertBefore(t,i.querySelector("hr.mapml-popup-divider"))}}n._navigationBar=t,n._content.appendChild(p),n._content.appendChild(t),o.focus(),a&&!M.options.featureIndexOverlayOption?(a.setAttribute("aria-expanded","true"),s.on("keydown",h)):s.on("keydown",d),s.on("popupclose",function t(e){e.popup===n&&(s.off("keydown",h),s.off("keydown",d),s.off("popupopen",_),s.off("popupclose",t),a&&a.setAttribute("aria-expanded","false"))})}}),b=L.Layer.extend({onAdd:function(t){var e=t.getSize();(400tile: i: ${Math.trunc(a)}, j: ${Math.trunc(s)} @@ -24,7 +24,7 @@ pcrs: easting: ${i.x.toFixed(2)}, northing: ${i.y.toFixed(2)} - `}}}),v=function(t){return new x(t)},C=L.GridLayer.extend({initialize:function(t){L.setOptions(this,t),L.GridLayer.prototype.initialize.call(this,this._map)},createTile:function(t){let e=L.DomUtil.create("div","mapml-debug-tile");return e.setAttribute("col",t.x),e.setAttribute("row",t.y),e.setAttribute("zoom",t.z),e.innerHTML=["col: "+t.x,"row: "+t.y,"zoom: "+t.z].join(", "),e.style.outline="1px dashed red",e}}),E=function(t){return new C(t)},S=L.LayerGroup.extend({initialize:function(t){L.setOptions(this,t),L.LayerGroup.prototype.initialize.call(this,this._map,t)},onAdd:function(t){t.on("overlayremove",this._mapLayerUpdate,this),t.on("overlayadd",this._mapLayerUpdate,this);var e=t.options.crs.transformation.transform(L.point(0,0),t.options.crs.scale(0));this._centerVector=L.circle(t.options.crs.pointToLatLng(e,0),{radius:250,className:"mapml-debug-vectors projection-centre"}),this._centerVector.bindTooltip("Projection Center"),this._addBounds(t)},onRemove:function(t){this.clearLayers()},_addBounds:function(r){setTimeout(()=>{let t=Object.keys(r._layers),o=r._layers,i=["#FF5733","#8DFF33","#3397FF","#E433FF","#F3FF33"],n=0;this.addLayer(this._centerVector);for(var a of t)if(o[a].layerBounds||o[a].extentBounds){let t;t=o[a].layerBounds?[o[a].layerBounds.min,L.point(o[a].layerBounds.max.x,o[a].layerBounds.min.y),o[a].layerBounds.max,L.point(o[a].layerBounds.min.x,o[a].layerBounds.max.y)]:[o[a].extentBounds.min,L.point(o[a].extentBounds.max.x,o[a].extentBounds.min.y),o[a].extentBounds.max,L.point(o[a].extentBounds.min.x,o[a].extentBounds.max.y)];var s=o[a].extentBounds&&o[a].options.linkEl.getLayerEl().hasAttribute("data-testid")?o[a].options.linkEl.getLayerEl().getAttribute("data-testid"):o[a].layerBounds&&o[a].options?._leafletLayer?._layerEl.hasAttribute("data-testid")?o[a].options._leafletLayer._layerEl.getAttribute("data-testid"):"";let e=k(t,{className:this.options.className.concat(" ",s),color:i[n%i.length],weight:2,opacity:1,fillOpacity:.01,fill:!0});o[a].options._leafletLayer&&e.bindTooltip(o[a].options._leafletLayer._title,{sticky:!0}),this.addLayer(e),n++}var e;r.totalLayerBounds&&(e=[r.totalLayerBounds.min,L.point(r.totalLayerBounds.max.x,r.totalLayerBounds.min.y),r.totalLayerBounds.max,L.point(r.totalLayerBounds.min.x,r.totalLayerBounds.max.y)],e=k(e,{className:"mapml-debug-vectors mapml-total-bounds",color:"#808080",weight:5,opacity:.5,fill:!1}),this.addLayer(e))},0)},_mapLayerUpdate:function(t){this.clearLayers(),this._addBounds(t.target)}}),T=function(t){return new S(t)},A=L.Path.extend({getCenter:function(t){let e=this._map.options.crs;return e.unproject(L.bounds(this._locations).getCenter())},options:{className:"mapml-debug-extent"},initialize:function(t,e){this._locations=t,L.setOptions(this,e)},_project:function(){this._rings=[];let e=this._map.options.crs.scale(this._map.getZoom()),o=this._map;for(let t=0;t{!this._map.isFocused||this._map._popupClosed||" "!==t.originalEvent.key&&13!=+t.originalEvent.keyCode?delete this._map._popupClosed:this._map.fire("click",{latlng:this._map.getCenter(),layerPoint:this._map.latLngToLayerPoint(this._map.getCenter()),containerPoint:this._map.latLngToContainerPoint(this._map.getCenter())})},0)},_queryTopLayer:function(t){var e=this._getTopQueryableLayer();e&&(e._mapmlFeatures&&delete e._mapmlFeatures,this._query(t,e))},_query(u,m){function e(t){return n.transformation.untransform(t,n.scale(i))}function o(t){return n.unproject(n.transformation.untransform(t,n.scale(i)),i)}var i=u.target.getZoom(),a=this._map,n=M[m.options.projection],s=a.options.crs.options.crs.tile.bounds.max.x,r=m._container,l={autoClose:!1,autoPan:!0,maxHeight:.5*a.getSize().y-50},c=n.latLngToPoint(u.latlng,i),p=c.divideBy(s).floor(),h=new L.Bounds(c.divideBy(s).floor().multiplyBy(s),c.divideBy(s).ceil().multiplyBy(s)),t=this._map.project(u.latlng),d=this._map.options.crs.scale(this._map.getZoom()),d=this._map.options.crs.transformation.untransform(t,d),_=m.getQueryTemplates(d,i);let y=[];for(let t=0;t<_.length;t++){var f,g={},b=_[t];for(f in g[b.query.tilei]=c.x.toFixed()-p.x*s,g[b.query.tilej]=c.y.toFixed()-p.y*s,g[b.query.mapi]=a.getSize().divideBy(2).x.toFixed(),g[b.query.mapj]=a.getSize().divideBy(2).y.toFixed(),g[b.query.pixelleft]=n.pointToLatLng(c,i).lng,g[b.query.pixeltop]=n.pointToLatLng(c,i).lat,g[b.query.pixelright]=n.pointToLatLng(c.add([1,1]),i).lng,g[b.query.pixelbottom]=n.pointToLatLng(c.add([1,1]),i).lat,g[b.query.column]=p.x,g[b.query.row]=p.y,g[b.query.x]=c.x.toFixed(),g[b.query.y]=c.y.toFixed(),g[b.query.easting]=e(c).x,g[b.query.northing]=e(c).y,g[b.query.longitude]=o(c).lng,g[b.query.latitude]=o(c).lat,g[b.query.zoom]=i,g[b.query.width]=a.getSize().x,g[b.query.height]=a.getSize().y,g[b.query.mapbottom]=e(c.add(a.getSize().divideBy(2))).y,g[b.query.mapleft]=e(c.subtract(a.getSize().divideBy(2))).x,g[b.query.maptop]=e(c.subtract(a.getSize().divideBy(2))).y,g[b.query.mapright]=e(c.add(a.getSize().divideBy(2))).x,g[b.query.tilebottom]=e(h.max).y,g[b.query.tileleft]=e(h.min).x,g[b.query.tiletop]=e(h.min).y,g[b.query.tileright]=e(h.max).x,b.query)["mapi","mapj","tilei","tilej","row","col","x","y","easting","northing","longitude","latitude","width","height","zoom","mapleft","mapright",",maptop","mapbottom","tileleft","tileright","tiletop","tilebottom","pixeltop","pixelbottom","pixelleft","pixelright"].indexOf(f)<0&&(g[f]=b.query[f]);y.push(function(t,e){const l=new DOMParser;return fetch(L.Util.template(t.template,e),{redirect:"follow"}).then(e=>{if(200<=e.status&&e.status<300)return e.text().then(t=>({contenttype:e.headers.get("Content-Type"),text:t}));throw new Error(e.status)}).then(i=>{let n=[];var a=""+u.latlng.lng+" "+u.latlng.lat+"";if(i.contenttype.startsWith("text/mapml")){let t=l.parseFromString(i.text,"application/xml");var s=t.querySelectorAll("map-feature:not(:has(map-geometry))");if(s.length){let o=l.parseFromString(a,"application/xml");for(let e=0;e"+a+"","text/html");e.body?o.querySelector("map-properties").appendChild(e.querySelector("html")):o.querySelector("map-properties").append(i.text),n.push(o.querySelector("map-feature"))}return{features:n,template:t}}).catch(t=>{console.log("Looks like there was a problem. Status: "+t.message)})}(b,g))}Promise.allSettled(y).then(t=>{m._mapmlFeatures=[];for(var e of t)if("fulfilled"===e.status){for(var o of e.value.features)o._linkEl=e.value.template.linkEl;m._mapmlFeatures=m._mapmlFeatures.concat(e.value.features)}0Alt+Left Arrow)",callback:this._goBack},{text:M.options.locale.cmForward+" (Alt+Right Arrow)",callback:this._goForward},{text:M.options.locale.cmReload+" (Ctrl+R)",callback:this._reload},{text:M.options.locale.btnFullScreen+" (F)",callback:this._toggleFullScreen},{spacer:"-"},{text:M.options.locale.cmCopyCoords+" (C)",callback:this._copyCoords,hideOnSelect:!1,popup:!0,submenu:[{text:M.options.locale.cmCopyMapML,callback:this._copyMapML},{text:M.options.locale.cmCopyExtent,callback:this._copyExtent},{text:M.options.locale.cmCopyLocation,callback:this._copyLocation}]},{text:M.options.locale.cmPasteLayer+" (P)",callback:this._paste},{spacer:"-"},{text:M.options.locale.cmToggleControls+" (T)",callback:this._toggleControls},{text:M.options.locale.cmToggleDebug+" (D)",callback:this._toggleDebug},{text:M.options.locale.cmViewSource+" (V)",callback:this._viewSource}],this.defExtCS=M.options.defaultExtCoor,this.defLocCS=M.options.defaultLocCoor;this._menuItems.LYRZOOMTO=0,this._menuItems.LYRCOPY=1,this._layerItems=[{text:M.options.locale.lmZoomToLayer+" (Z)",callback:this._zoomToLayer},{text:M.options.locale.lmCopyLayer+" (L)",callback:this._copyLayer}],this._extentLayerItems=[{text:M.options.locale.lmZoomToExtent+" (Z)",callback:this._zoomToMapExtent},{text:M.options.locale.lmCopyExtent+" (L)",callback:this._copyMapExtent}],this._mapMenuVisible=!1,this._keyboardEvent=!1,this._container=L.DomUtil.create("div","mapml-contextmenu",t.getContainer()),this._container.setAttribute("hidden",""),this._items[0].el=this._createItem(this._container,this._items[0]),this._items[1].el=this._createItem(this._container,this._items[1]),this._items[2].el=this._createItem(this._container,this._items[2]),this._items[3].el=this._createItem(this._container,this._items[3]),this._items[4].el=this._createItem(this._container,this._items[4]),this._items[5].el=this._createItem(this._container,this._items[5]),this._copySubMenu=L.DomUtil.create("div","mapml-contextmenu mapml-submenu",this._container),this._copySubMenu.id="mapml-copy-submenu",this._copySubMenu.setAttribute("hidden",""),this._clickEvent=null;this._menuItems.CPYMENUMAP=0,this._menuItems.CPYMENUEXTENT=1,this._menuItems.CPYMENULOC=2,this._createItem(this._copySubMenu,this._items[5].submenu[0],0),this._createItem(this._copySubMenu,this._items[5].submenu[1],1),this._createItem(this._copySubMenu,this._items[5].submenu[2],2),this._items[6].el=this._createItem(this._container,this._items[6]),this._items[7].el=this._createItem(this._container,this._items[7]),this._items[8].el=this._createItem(this._container,this._items[8]),this._items[9].el=this._createItem(this._container,this._items[9]),this._items[10].el=this._createItem(this._container,this._items[10]),this._layerMenu=L.DomUtil.create("div","mapml-contextmenu mapml-layer-menu",t.getContainer()),this._layerMenu.setAttribute("hidden",""),this._createItem(this._layerMenu,this._layerItems[0]),this._createItem(this._layerMenu,this._layerItems[1]),this._extentLayerMenu=L.DomUtil.create("div","mapml-contextmenu mapml-extent-menu",t.getContainer()),this._extentLayerMenu.setAttribute("hidden",""),this._createItem(this._extentLayerMenu,this._extentLayerItems[0]),this._createItem(this._extentLayerMenu,this._extentLayerItems[1]),L.DomEvent.on(this._container,"click",L.DomEvent.stop).on(this._container,"mousedown",L.DomEvent.stop).on(this._container,"dblclick",L.DomEvent.stop).on(this._container,"contextmenu",L.DomEvent.stop).on(this._layerMenu,"click",L.DomEvent.stop).on(this._layerMenu,"mousedown",L.DomEvent.stop).on(this._layerMenu,"dblclick",L.DomEvent.stop).on(this._layerMenu,"contextmenu",L.DomEvent.stop).on(this._extentLayerMenu,"click",L.DomEvent.stop).on(this._extentLayerMenu,"mousedown",L.DomEvent.stop).on(this._extentLayerMenu,"dblclick",L.DomEvent.stop).on(this._extentLayerMenu,"contextmenu",L.DomEvent.stop),this.t=document.createElement("template"),this.t.innerHTML=` + `}}}),v=function(t){return new x(t)},C=L.GridLayer.extend({initialize:function(t){L.setOptions(this,t),L.GridLayer.prototype.initialize.call(this,this._map)},createTile:function(t){let e=L.DomUtil.create("div","mapml-debug-tile");return e.setAttribute("col",t.x),e.setAttribute("row",t.y),e.setAttribute("zoom",t.z),e.innerHTML=["col: "+t.x,"row: "+t.y,"zoom: "+t.z].join(", "),e.style.outline="1px dashed red",e}}),E=function(t){return new C(t)},S=L.LayerGroup.extend({initialize:function(t){L.setOptions(this,t),L.LayerGroup.prototype.initialize.call(this,this._map,t)},onAdd:function(t){t.on("overlayremove",this._mapLayerUpdate,this),t.on("overlayadd",this._mapLayerUpdate,this);var e=t.options.crs.transformation.transform(L.point(0,0),t.options.crs.scale(0));this._centerVector=L.circle(t.options.crs.pointToLatLng(e,0),{radius:250,className:"mapml-debug-vectors projection-centre"}),this._centerVector.bindTooltip("Projection Center"),this._addBounds(t)},onRemove:function(t){this.clearLayers()},_addBounds:function(r){setTimeout(()=>{let t=Object.keys(r._layers),o=r._layers,i=["#FF5733","#8DFF33","#3397FF","#E433FF","#F3FF33"],n=0;this.addLayer(this._centerVector);for(var a of t)if(o[a].layerBounds||o[a].extentBounds){let t;t=o[a].layerBounds?[o[a].layerBounds.min,L.point(o[a].layerBounds.max.x,o[a].layerBounds.min.y),o[a].layerBounds.max,L.point(o[a].layerBounds.min.x,o[a].layerBounds.max.y)]:[o[a].extentBounds.min,L.point(o[a].extentBounds.max.x,o[a].extentBounds.min.y),o[a].extentBounds.max,L.point(o[a].extentBounds.min.x,o[a].extentBounds.max.y)];var s=o[a].extentBounds&&o[a].options.linkEl.getLayerEl().hasAttribute("data-testid")?o[a].options.linkEl.getLayerEl().getAttribute("data-testid"):o[a].layerBounds&&o[a].options?._leafletLayer?._layerEl.hasAttribute("data-testid")?o[a].options._leafletLayer._layerEl.getAttribute("data-testid"):"";let e=I(t,{className:this.options.className.concat(" ",s),color:i[n%i.length],weight:2,opacity:1,fillOpacity:.01,fill:!0});o[a].options._leafletLayer&&e.bindTooltip(o[a].options._leafletLayer._title,{sticky:!0}),this.addLayer(e),n++}var e;r.totalLayerBounds&&(e=[r.totalLayerBounds.min,L.point(r.totalLayerBounds.max.x,r.totalLayerBounds.min.y),r.totalLayerBounds.max,L.point(r.totalLayerBounds.min.x,r.totalLayerBounds.max.y)],e=I(e,{className:"mapml-debug-vectors mapml-total-bounds",color:"#808080",weight:5,opacity:.5,fill:!1}),this.addLayer(e))},0)},_mapLayerUpdate:function(t){this.clearLayers(),this._addBounds(t.target)}}),T=function(t){return new S(t)},A=L.Path.extend({getCenter:function(t){let e=this._map.options.crs;return e.unproject(L.bounds(this._locations).getCenter())},options:{className:"mapml-debug-extent"},initialize:function(t,e){this._locations=t,L.setOptions(this,e)},_project:function(){this._rings=[];let e=this._map.options.crs.scale(this._map.getZoom()),o=this._map;for(let t=0;t{!this._map.isFocused||this._map._popupClosed||" "!==t.originalEvent.key&&13!=+t.originalEvent.keyCode?delete this._map._popupClosed:this._map.fire("click",{latlng:this._map.getCenter(),layerPoint:this._map.latLngToLayerPoint(this._map.getCenter()),containerPoint:this._map.latLngToContainerPoint(this._map.getCenter())})},0)},_queryTopLayer:function(t){var e=this._getTopQueryableLayer();e&&(e._mapmlFeatures&&delete e._mapmlFeatures,this._query(t,e))},_query(m,a){function e(t){return n.transformation.untransform(t,n.scale(i))}function o(t){return n.unproject(n.transformation.untransform(t,n.scale(i)),i)}var i=m.target.getZoom(),s=this._map,n=M[a.options.projection],r=s.options.crs.options.crs.tile.bounds.max.x,l=a._container,u={autoClose:!1,autoPan:!0,maxHeight:.5*s.getSize().y-50},c=n.latLngToPoint(m.latlng,i),p=c.divideBy(r).floor(),h=new L.Bounds(c.divideBy(r).floor().multiplyBy(r),c.divideBy(r).ceil().multiplyBy(r)),t=this._map.project(m.latlng),d=this._map.options.crs.scale(this._map.getZoom()),d=this._map.options.crs.transformation.untransform(t,d),_=a.getQueryTemplates(d,i);let y=[];for(let t=0;t<_.length;t++){var f,g={},b=_[t];for(f in g[b.query.tilei]=c.x.toFixed()-p.x*r,g[b.query.tilej]=c.y.toFixed()-p.y*r,g[b.query.mapi]=s.getSize().divideBy(2).x.toFixed(),g[b.query.mapj]=s.getSize().divideBy(2).y.toFixed(),g[b.query.pixelleft]=n.pointToLatLng(c,i).lng,g[b.query.pixeltop]=n.pointToLatLng(c,i).lat,g[b.query.pixelright]=n.pointToLatLng(c.add([1,1]),i).lng,g[b.query.pixelbottom]=n.pointToLatLng(c.add([1,1]),i).lat,g[b.query.column]=p.x,g[b.query.row]=p.y,g[b.query.x]=c.x.toFixed(),g[b.query.y]=c.y.toFixed(),g[b.query.easting]=e(c).x,g[b.query.northing]=e(c).y,g[b.query.longitude]=o(c).lng,g[b.query.latitude]=o(c).lat,g[b.query.zoom]=i,g[b.query.width]=s.getSize().x,g[b.query.height]=s.getSize().y,g[b.query.mapbottom]=e(c.add(s.getSize().divideBy(2))).y,g[b.query.mapleft]=e(c.subtract(s.getSize().divideBy(2))).x,g[b.query.maptop]=e(c.subtract(s.getSize().divideBy(2))).y,g[b.query.mapright]=e(c.add(s.getSize().divideBy(2))).x,g[b.query.tilebottom]=e(h.max).y,g[b.query.tileleft]=e(h.min).x,g[b.query.tiletop]=e(h.min).y,g[b.query.tileright]=e(h.max).x,b.query)["mapi","mapj","tilei","tilej","row","col","x","y","easting","northing","longitude","latitude","width","height","zoom","mapleft","mapright",",maptop","mapbottom","tileleft","tileright","tiletop","tilebottom","pixeltop","pixelbottom","pixelleft","pixelright"].indexOf(f)<0&&(g[f]=b.query[f]);y.push(function(t,e){const u=new DOMParser;return fetch(L.Util.template(t.template,e),{redirect:"follow"}).then(e=>{if(200<=e.status&&e.status<300)return e.text().then(t=>({contenttype:e.headers.get("Content-Type"),text:t}));throw new Error(e.status)}).then(i=>{let n=[],o=[];var a=""+m.latlng.lng+" "+m.latlng.lat+"";if(i.contenttype.startsWith("text/mapml")){let t=u.parseFromString(i.text,"application/xml");var s=t.querySelectorAll("map-feature:not(:has(map-geometry))");if(s.length){let o=u.parseFromString(a,"application/xml");for(let e=0;et.meta=o)}else try{let t=u.parseFromString(i.text,"application/xml");var r,l=t.querySelectorAll("map-feature");if(t.querySelector("parsererror")||0===l.length)throw new Error("parsererror");let e=u.parseFromString(a,"application/xml");o=Array.prototype.slice.call(t.querySelectorAll("map-meta[name=cs], map-meta[name=zoom], map-meta[name=projection]"));for(r of l)r.querySelector("map-geometry")||r.appendChild(e.firstElementChild.cloneNode(!0)),r.meta=o,n.push(r)}catch(t){let e=u.parseFromString(i.text,"text/html"),o=u.parseFromString(""+a+"","text/html");e.body?o.querySelector("map-properties").appendChild(e.querySelector("html")):o.querySelector("map-properties").append(i.text),n.push(o.querySelector("map-feature"))}return{features:n,template:t}}).catch(t=>{console.log("Looks like there was a problem. Status: "+t.message)})}(b,g))}Promise.allSettled(y).then(t=>{a._mapmlFeatures=[];for(var e of t)if("fulfilled"===e.status){for(var o of e.value.features)o._linkEl=e.value.template.linkEl;a._mapmlFeatures=a._mapmlFeatures.concat(e.value.features)}0Alt+Left Arrow)",callback:this._goBack},{text:M.options.locale.cmForward+" (Alt+Right Arrow)",callback:this._goForward},{text:M.options.locale.cmReload+" (Ctrl+R)",callback:this._reload},{text:M.options.locale.btnFullScreen+" (F)",callback:this._toggleFullScreen},{spacer:"-"},{text:M.options.locale.cmCopyCoords+" (C)",callback:this._copyCoords,hideOnSelect:!1,popup:!0,submenu:[{text:M.options.locale.cmCopyMapML,callback:this._copyMapML},{text:M.options.locale.cmCopyExtent,callback:this._copyExtent},{text:M.options.locale.cmCopyLocation,callback:this._copyLocation}]},{text:M.options.locale.cmPasteLayer+" (P)",callback:this._paste},{spacer:"-"},{text:M.options.locale.cmToggleControls+" (T)",callback:this._toggleControls},{text:M.options.locale.cmToggleDebug+" (D)",callback:this._toggleDebug},{text:M.options.locale.cmViewSource+" (V)",callback:this._viewSource}],this.defExtCS=M.options.defaultExtCoor,this.defLocCS=M.options.defaultLocCoor;this._menuItems.LYRZOOMTO=0,this._menuItems.LYRCOPY=1,this._layerItems=[{text:M.options.locale.lmZoomToLayer+" (Z)",callback:this._zoomToLayer},{text:M.options.locale.lmCopyLayer+" (L)",callback:this._copyLayer}],this._extentLayerItems=[{text:M.options.locale.lmZoomToExtent+" (Z)",callback:this._zoomToMapExtent},{text:M.options.locale.lmCopyExtent+" (L)",callback:this._copyMapExtent}],this._mapMenuVisible=!1,this._keyboardEvent=!1,this._container=L.DomUtil.create("div","mapml-contextmenu",t.getContainer()),this._container.setAttribute("hidden",""),this._items[0].el=this._createItem(this._container,this._items[0]),this._items[1].el=this._createItem(this._container,this._items[1]),this._items[2].el=this._createItem(this._container,this._items[2]),this._items[3].el=this._createItem(this._container,this._items[3]),this._items[4].el=this._createItem(this._container,this._items[4]),this._items[5].el=this._createItem(this._container,this._items[5]),this._copySubMenu=L.DomUtil.create("div","mapml-contextmenu mapml-submenu",this._container),this._copySubMenu.id="mapml-copy-submenu",this._copySubMenu.setAttribute("hidden",""),this._clickEvent=null;this._menuItems.CPYMENUMAP=0,this._menuItems.CPYMENUEXTENT=1,this._menuItems.CPYMENULOC=2,this._createItem(this._copySubMenu,this._items[5].submenu[0],0),this._createItem(this._copySubMenu,this._items[5].submenu[1],1),this._createItem(this._copySubMenu,this._items[5].submenu[2],2),this._items[6].el=this._createItem(this._container,this._items[6]),this._items[7].el=this._createItem(this._container,this._items[7]),this._items[8].el=this._createItem(this._container,this._items[8]),this._items[9].el=this._createItem(this._container,this._items[9]),this._items[10].el=this._createItem(this._container,this._items[10]),this._layerMenu=L.DomUtil.create("div","mapml-contextmenu mapml-layer-menu",t.getContainer()),this._layerMenu.setAttribute("hidden",""),this._createItem(this._layerMenu,this._layerItems[0]),this._createItem(this._layerMenu,this._layerItems[1]),this._extentLayerMenu=L.DomUtil.create("div","mapml-contextmenu mapml-extent-menu",t.getContainer()),this._extentLayerMenu.setAttribute("hidden",""),this._createItem(this._extentLayerMenu,this._extentLayerItems[0]),this._createItem(this._extentLayerMenu,this._extentLayerItems[1]),L.DomEvent.on(this._container,"click",L.DomEvent.stop).on(this._container,"mousedown",L.DomEvent.stop).on(this._container,"dblclick",L.DomEvent.stop).on(this._container,"contextmenu",L.DomEvent.stop).on(this._layerMenu,"click",L.DomEvent.stop).on(this._layerMenu,"mousedown",L.DomEvent.stop).on(this._layerMenu,"dblclick",L.DomEvent.stop).on(this._layerMenu,"contextmenu",L.DomEvent.stop).on(this._extentLayerMenu,"click",L.DomEvent.stop).on(this._extentLayerMenu,"mousedown",L.DomEvent.stop).on(this._extentLayerMenu,"dblclick",L.DomEvent.stop).on(this._extentLayerMenu,"contextmenu",L.DomEvent.stop),this.t=document.createElement("template"),this.t.innerHTML=`

@@ -42,5 +42,5 @@ `,u+=`tcrs: x:${Math.trunc(i.x)}, y:${Math.trunc(i.y)} `,u+=`pcrs: easting:${l.x.toFixed(2)}, northing:${l.y.toFixed(2)} `,u+=`gcrs: lon :${o.latlng.lng.toFixed(6)}, lat:`+o.latlng.lat.toFixed(6),this.contextMenu._copyData(u)},_createItem:function(t,e,o){if(e.spacer)return this._createSeparator(t,o);t=this._insertElementAt("button","mapml-contextmenu-item",t,o),o=this._createEventHandler(t,e.callback,e.context,e.hideOnSelect);return t.innerHTML=""+e.text,t.setAttribute("type","button"),t.classList.add("mapml-button"),e.popup&&(t.setAttribute("aria-haspopup","true"),t.setAttribute("aria-expanded","false"),t.setAttribute("aria-controls","mapml-copy-submenu")),L.DomEvent.on(t,"mouseover",this._onItemMouseOver,this).on(t,"mouseout",this._onItemMouseOut,this).on(t,"mousedown",L.DomEvent.stopPropagation).on(t,"click",o),L.Browser.touch&&L.DomEvent.on(t,this._touchstart,L.DomEvent.stopPropagation),L.Browser.pointer||L.DomEvent.on(t,"click",this._onItemMouseOut,this),{id:L.Util.stamp(t),el:t,callback:o}},_createSeparator:function(t,e){e=this._insertElementAt("div","mapml-contextmenu-separator",t,e);return{id:L.Util.stamp(e),el:e}},_createEventHandler:function(r,l,u,m){let c=this;return m=void 0===m||m,function(t){let e=c._map,o=c._showLocation.containerPoint,i=e.containerPointToLayerPoint(o),n=e.layerPointToLatLng(i),a=c._showLocation.relatedTarget,s={containerPoint:o,layerPoint:i,latlng:n,relatedTarget:a};m&&c._hide(),l&&l.call(u||e,s),c._map.fire("contextmenu.select",{contextmenu:c,el:r})}},_insertElementAt:function(t,e,o,i){let n,a=document.createElement(t);return a.className=e,void 0!==i&&(n=o.children[i]),n?o.insertBefore(a,n):o.appendChild(a),a},_show:function(e){if(!e.originalEvent.target.closest(".mapml-vector-container")){this._mapMenuVisible&&this._hide();let t=(this._clickEvent=e).originalEvent.target;if(t.closest("fieldset"))t=t.closest("fieldset"),"mapml-layer-item"===t.className?(t=t.querySelector("span"),this._layerMenu.removeAttribute("hidden"),this._showAtPoint(e.containerPoint,e,this._layerMenu)):"mapml-layer-extent"===t.className&&(t=t.querySelector("span"),this._extentLayerMenu.removeAttribute("hidden"),this._showAtPoint(e.containerPoint,e,this._extentLayerMenu)),this._layerClicked=t;else if(t.classList.contains("leaflet-container")||t.classList.contains("mapml-debug-extent")||"path"===t.tagName){var o=this._map.options.mapEl.layers;this._layerClicked=Array.from(o).find(t=>t.checked);let t=e.containerPoint;L.Browser.gecko&&(t=(i=this._map.getContainer(),o=i.getBoundingClientRect().width,i=i.getBoundingClientRect().height,{x:Number.parseInt(o/2),y:Number.parseInt(i/2)})),this._container.removeAttribute("hidden"),this._showAtPoint(t,e,this._container),this._updateCS()}var i,n;0!==e.originalEvent.button&&-1!==e.originalEvent.button||(this._keyboardEvent=!0,this._layerClicked.className.includes("mapml-layer-item")?(n=document.activeElement,this._elementInFocus=n.shadowRoot.activeElement,this._layerMenuTabs=1,this._layerMenu.firstChild.focus()):this._layerClicked.className.includes("mapml-extent-item")?(n=document.activeElement,this._elementInFocus=n.shadowRoot.activeElement,this._extentLayerMenuTabs=1,this._extentLayerMenu.firstChild.focus()):this._container.querySelectorAll("button:not([disabled])")[0].focus())}},_showAtPoint:function(t,e,o){var i;this._items.length&&(i=L.extend(e||{},{contextmenu:this}),this._showLocation={containerPoint:t},e&&e.relatedTarget&&(this._showLocation.relatedTarget=e.relatedTarget),this._setPosition(t,o),this._mapMenuVisible||(o.removeAttribute("hidden"),this._mapMenuVisible=!0),this._map.fire("contextmenu.show",i))},_hide:function(){this._mapMenuVisible&&(this._mapMenuVisible=!1,this._container.setAttribute("hidden",""),this._copySubMenu.setAttribute("hidden",""),this._layerMenu.setAttribute("hidden",""),this._extentLayerMenu.setAttribute("hidden",""),this._map.fire("contextmenu.hide",{contextmenu:this}),setTimeout(()=>this._map._container.focus(),0),this.activeIndex=0,this.isRunned=!1)},_setPosition:function(t,e){var o,i=this._map.getSize(),n=this._getElementSize(e);this._map.options.contextmenuAnchor&&(o=L.point(this._map.options.contextmenuAnchor),t=t.add(o)),(e._leaflet_pos=t).x+n.x>i.x?(e.style.left="auto",e.style.right=Math.min(Math.max(i.x-t.x,0),i.x-n.x-1)+"px"):(e.style.left=Math.max(t.x,0)+"px",e.style.right="auto"),t.y+n.y>i.y?(e.style.top="auto",e.style.bottom=Math.min(Math.max(i.y-t.y,0),i.y-n.y-1)+"px"):(e.style.top=Math.max(t.y,0)+"px",e.style.bottom="auto")},_getElementSize:function(t){let e=this._size;return e&&!this._sizeChanged||(e={},t.style.left="-999999px",t.style.right="auto",e.x=t.offsetWidth,e.y=t.offsetHeight,t.style.left="auto",this._sizeChanged=!1),e},_focusOnLayerControl:function(){this._mapMenuVisible=!1,delete this._layerMenuTabs,delete this._extentLayerMenuTabs,this._layerMenu.setAttribute("hidden",""),this._extentLayerMenu.setAttribute("hidden",""),(this._elementInFocus||this._layerClicked.parentElement.firstChild).focus(),delete this._elementInFocus},_setActiveItem:function(o){if(null===document.activeElement.shadowRoot&&!0===this.noActiveEl&&(this.noActiveEl=!1,this._items[9].el.el.focus()),document.activeElement.shadowRoot.activeElement.innerHTML===this._items[o].el.el.innerHTML){let t=o+1;for(;this._items[t].el.el.disabled;)t++,t>=this._items.length&&(t=0);this._setActiveItem(t)}else if(this.excludedIndices.includes(o)){let t=o+1,e=o-1;for(;this.excludedIndices.includes(t)||this._items[t].el.el.disabled;)t++,t>=this._items.length&&(t=0);for(;this.excludedIndices.includes(e)||this._items[e].el.el.disabled;)e--,e<0&&(e=this._items.length-1);this.activeIndex=this._items.length&&(t=0);this._setActiveItem(t)}else this._setActiveItem(0),this.isRunned=!0;else{let t=0;for(;this._items[t].el.el.disabled;)t++,t>=this._items.length&&(t=0);this._setActiveItem(t)}else this._extentLayerMenu.children[this._menuItems.LYRZOOMTO].focus();else this._extentLayerMenu.children[this._menuItems.LYRCOPY].focus();else this._layerMenu.children[this._menuItems.LYRZOOMTO].focus();else this._layerMenu.children[this._menuItems.LYRCOPY].focus();else this._copySubMenu.children[this._menuItems.CPYMENUEXTENT].focus();else this._copySubMenu.children[this._menuItems.CPYMENULOC].focus();else this._copySubMenu.children[this._menuItems.CPYMENUMAP].focus();else if("ArrowRight"===t.code)null!==document.activeElement.shadowRoot&&document.activeElement.shadowRoot.activeElement.innerHTML===this._items[this._menuItems.CTXCOPY].el.el.innerHTML&&this._copySubMenu.hasAttribute("hidden")?(this._showCopySubMenu(),this._copySubMenu.children[0].focus()):document.activeElement.shadowRoot.activeElement.innerHTML!==this._items[this._menuItems.CTXCOPY].el.el.innerHTML||this._copySubMenu.hasAttribute("hidden")||this._copySubMenu.children[0].focus();else if("ArrowLeft"===t.code)this._copySubMenu.hasAttribute("hidden")||null===document.activeElement.shadowRoot||document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENUMAP].innerHTML&&document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENUEXTENT].innerHTML&&document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENULOC].innerHTML||(this._copySubMenu.setAttribute("hidden",""),this._setActiveItem(this._menuItems.CTXCOPY));else if("Escape"===t.code){if(this._layerMenuTabs||this._extentLayerMenuTabs)return L.DomEvent.stop(t),void this._focusOnLayerControl();null===document.activeElement.shadowRoot||this._copySubMenu.hasAttribute("hidden")?this._hide():document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENUMAP].innerHTML&&document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENUEXTENT].innerHTML&&document.activeElement.shadowRoot.activeElement.innerHTML!==this._copySubMenu.children[this._menuItems.CPYMENULOC].innerHTML||(this._copySubMenu.setAttribute("hidden",""),this._setActiveItem(this._menuItems.CTXCOPY))}else"KeyC"!==t.code&&document.activeElement.shadowRoot.activeElement.innerHTML!==this._items[this._menuItems.CTXCOPY].el.el.innerHTML&&this._hide();switch(t.code){case"Enter":document.activeElement.shadowRoot.activeElement.innerHTML===this._items[this._menuItems.CTXCOPY].el.el.innerHTML?(this._copyCoords({latlng:this._map.getCenter()}),this._copySubMenu.firstChild.focus()):this._map._container.parentNode.activeElement.parentNode.classList.contains("mapml-contextmenu")&&this._map._container.parentNode.activeElement.click();break;case"Space":this._map._container.parentNode.activeElement.parentNode.classList.contains("mapml-contextmenu")&&this._map._container.parentNode.activeElement.click();break;case"KeyC":this._copyCoords({latlng:this._map.getCenter()}),this._copySubMenu.firstChild.focus();break;case"KeyD":this._toggleDebug(t);break;case"KeyM":this._copyMapML(t);break;case"KeyL":this._layerClicked.className.includes("mapml-layer-item")?this._copyLayer(t):this._layerClicked.className.includes("mapml-extent-item")&&this._copyMapExtent(t);break;case"KeyF":this._toggleFullScreen(t);break;case"KeyP":this._paste(t);break;case"KeyT":this._toggleControls(t);break;case"KeyV":this._viewSource(t);break;case"KeyZ":this._layerClicked.className.includes("mapml-layer-item")?this._zoomToLayer(t):this._layerClicked.className.includes("mapml-extent-item")&&this._zoomToMapExtent(t)}}},_showCopySubMenu:function(t){let e=this._map.getSize(),o=this._clickEvent,i=this._copySubMenu,n=this._items[5].el.el;n.setAttribute("aria-expanded","true"),i.removeAttribute("hidden");var a=this._container.offsetWidth,s=(this._container.offsetHeight,i.offsetWidth);o.containerPoint.x+a+s>e.x?(i.style.left="auto",i.style.right=a+"px"):(i.style.left=a+"px",i.style.right="auto"),i.style.top="78px",i.style.bottom="auto"},_hideCopySubMenu:function(t){if(t.relatedTarget&&t.relatedTarget.parentElement&&!t.relatedTarget.parentElement.classList.contains("mapml-submenu")&&!t.relatedTarget.classList.contains("mapml-submenu")){let t=this._copySubMenu,e=this._items[4].el.el;e.setAttribute("aria-expanded","false"),t.setAttribute("hidden",""),this.noActiveEl=!0}},_onItemMouseOver:function(t){L.DomUtil.addClass(t.target||t.srcElement,"over"),t.srcElement.innerText===M.options.locale.cmCopyCoords+" (C)"&&this._showCopySubMenu(t)},_onItemMouseOut:function(t){L.DomUtil.removeClass(t.target||t.srcElement,"over"),this._hideCopySubMenu(t)},toggleContextMenuItem:function(t,e){t=t.toUpperCase(),"disabled"===e?"CONTROLS"===t?this._items[8].el.el.disabled=!0:"BACK"===t?this._items[0].el.el.disabled=!0:"FORWARD"===t?this._items[1].el.el.disabled=!0:"RELOAD"===t&&(this._items[2].el.el.disabled=!0):"enabled"===e&&("CONTROLS"===t?this._items[8].el.el.disabled=!1:"BACK"===t?this._items[0].el.el.disabled=!1:"FORWARD"===t?this._items[1].el.el.disabled=!1:"RELOAD"===t&&(this._items[2].el.el.disabled=!1))},setViewFullScreenInnerHTML:function(t){"view"===t?this._map.contextMenu._items[3].el.el.innerHTML=M.options.locale.btnFullScreen+" (F)":"exit"===t&&(this._map.contextMenu._items[3].el.el.innerHTML=M.options.locale.btnExitFullScreen+" (F)")}}),w=function(e,o,t){if(!e||!o)return{};let i=[],n=[],a=[],s=[],r=o.options.crs.tile.bounds.max.y;for(let t=0;t{!o.inPlace&&i?e.parentElement.zoomTo(+i.lat,+i.lng,+i.z):o.inPlace||e.zoomTo(),s&&(e.opacity=s),a.getContainer().focus()})}},Y=function(t){if(!t)return null;let o,i=t instanceof ShadowRoot?":host":":scope",n=t.querySelector(i+" > map-meta[name=projection]")&&M._metaContentToObject(t.querySelector(i+" > map-meta[name=projection]").getAttribute("content")).content.toUpperCase()||M.FALLBACK_PROJECTION;try{var a=t.querySelector(i+" > map-meta[name=extent]")&&M._metaContentToObject(t.querySelector(i+" > map-meta[name=extent]").getAttribute("content")),s=a.zoom;let e=Object.keys(a);for(let t=0;t map-meta[name=zoom]").getAttribute("content"));return e.min&&e.max&&e.value?{minZoom:+e.min,maxZoom:+e.max,minNativeZoom:+e.value,maxNativeZoom:+e.value}:e.min&&e.max?{minZoom:+e.min,maxZoom:+e.max}:e.min?{minZoom:+e.min}:e.max?{maxZoom:+e.max}:void 0},K=function(t,o){if(!t)return null;let i=100,n=0,a=t.querySelectorAll("map-feature"),e,s;for(let e=0;e';e.insertAdjacentHTML("beforeend",t),e.lastElementChild.whenReady().catch(()=>{e&&e.removeChild(e.lastChild),t=null})}catch(t){if(")|()|( - - org.codehaus.jackson - jackson-mapper-asl - ${jackson1.version} - com.fasterxml.jackson.core jackson-core From b6921584f57964b806d3bede629c5e506520cee0 Mon Sep 17 00:00:00 2001 From: davidblasby <48937730+davidblasby@users.noreply.github.com> Date: Mon, 13 May 2024 09:24:54 -0700 Subject: [PATCH 31/31] Jwt multi admin (#7610) * Add JWT Header support for multiple ADMIN/ROLE_ADMINISTRATOR support * better javascript handling for when creating a new jwt headers filter * doc change for role converter * fix bug when json path doesn't exist * delete old src/ dir * afabiani review changes - (c) header, more specific import, UI: Role Source enum --------- Co-authored-by: david blasby --- .../community/jwt-headers/configuration.rst | 6 + .../GeoServerJwtHeadersFilterConfig.java | 7 +- .../web/JwtHeadersAuthFilterPanel.html | 11 +- .../security/jwtheaders/JwtConfiguration.java | 18 +- .../jwtheaders/roles/RoleConverter.java | 6 +- .../jwtheaders/token/TokenValidator.java | 4 + .../username/JwtHeaderUserNameExtractor.java | 7 +- .../roles/JwtHeadersRolesExtractorTest.java | 18 + .../jwtheaders/roles/RoleConverterTest.java | 37 +- .../JwtHeaderUserNameExtractorTest.java | 34 +- .../main/java/applicationSecurityContext.xml | 20 - .../filter/GeoServerJwtHeadersFilter.java | 195 ---------- .../GeoServerJwtHeadersFilterConfig.java | 326 ----------------- ...ServerJwtHeadersFilterConfigValidator.java | 54 --- ...erverJwtHeadersAuthenticationProvider.java | 58 --- .../JwtHeadersWebAuthDetailsSource.java | 29 -- .../JwtHeadersWebAuthenticationDetails.java | 28 -- .../roles/JwtHeadersRolesExtractor.java | 101 ----- .../jwtheaders/roles/RoleConverter.java | 52 --- .../token/TokenAudienceValidator.java | 35 -- .../token/TokenEndpointValidator.java | 104 ------ .../token/TokenExpiryValidator.java | 34 -- .../token/TokenSignatureValidator.java | 96 ----- .../jwtheaders/token/TokenValidator.java | 67 ---- .../username/JwtHeaderUserNameExtractor.java | 100 ----- ...HeadersAuthFilterPanel$JsonClaimPanel.html | 148 -------- .../web/JwtHeadersAuthFilterPanel.html | 188 ---------- .../web/JwtHeadersAuthFilterPanel.java | 98 ----- .../web/JwtHeadersAuthFilterPanelInfo.java | 23 -- .../web/UserNameFormatChoiceRenderer.java | 29 -- .../resources/GeoServerApplication.properties | 43 --- .../src/main/resources/applicationContext.xml | 34 -- .../jwtheaders/JwtHeadersIntegrationTest.java | 344 ------------------ .../filter/JwtHeadersFilterTest.java | 81 ----- .../roles/JwtHeadersRolesExtractorTest.java | 55 --- .../jwtheaders/roles/RoleConverterTest.java | 137 ------- .../token/TokenAudienceValidatorTest.java | 56 --- .../token/TokenEndpointValidatorTest.java | 123 ------- .../token/TokenExpiryValidatorTest.java | 85 ----- .../token/TokenSignatureValidatorTest.java | 94 ----- .../jwtheaders/token/TokenValidatorTest.java | 155 -------- .../JwtHeaderUserNameExtractorTest.java | 56 --- .../web/JwtHeadersAuthFilterPanelTest.java | 109 ------ 43 files changed, 126 insertions(+), 3179 deletions(-) delete mode 100644 src/community/jwt-headers/src/main/java/applicationSecurityContext.xml delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilter.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfig.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfigValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoserverJwtHeadersAuthenticationProvider.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/details/JwtHeadersWebAuthDetailsSource.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/details/JwtHeadersWebAuthenticationDetails.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractor.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/roles/RoleConverter.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/token/TokenAudienceValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/token/TokenEndpointValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/token/TokenExpiryValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/token/TokenSignatureValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/token/TokenValidator.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractor.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel$JsonClaimPanel.html delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanelInfo.java delete mode 100644 src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/UserNameFormatChoiceRenderer.java delete mode 100644 src/community/jwt-headers/src/main/resources/GeoServerApplication.properties delete mode 100644 src/community/jwt-headers/src/main/resources/applicationContext.xml delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/JwtHeadersIntegrationTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/filter/JwtHeadersFilterTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/token/TokenAudienceValidatorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/token/TokenEndpointValidatorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/token/TokenExpiryValidatorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/token/TokenSignatureValidatorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/token/TokenValidatorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractorTest.java delete mode 100644 src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanelTest.java diff --git a/doc/en/user/source/community/jwt-headers/configuration.rst b/doc/en/user/source/community/jwt-headers/configuration.rst index 2380f1b7f3d..b1e567a1644 100644 --- a/doc/en/user/source/community/jwt-headers/configuration.rst +++ b/doc/en/user/source/community/jwt-headers/configuration.rst @@ -156,6 +156,12 @@ For example, a conversion map like `GeoserverAdministrator=ROLE_ADMINISTRATOR` w In our example, the user has two roles "GeoserverAdministrator" and "GeonetworkAdministrator". If the "Only allow External Roles that are explicitly named above" is checked, then GeoServer will only see the "ROLE_ADMINISTRATOR" role. If unchecked, it will see "ROLE_ADMINISTRATOR" and "GeonetworkAdministrator". In neither case will it see the converted "GeoserverAdministrator" roles. +You can also have multiple GeoServer roles from one external (OIDC) role. For example, this role conversion: + +`GeoserverAdministrator=ROLE_ADMINISTRATOR;GeoserverAdministrator=ADMIN` + +Will give users with the OIDC role `GeoserverAdministrator` two GeoServer roles - `ROLE_ADMINISTRATOR` and `ADMIN`. + JWT Validation ^^^^^^^^^^^^^^ diff --git a/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfig.java b/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfig.java index c9cbb245fa9..c36202c677e 100644 --- a/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfig.java +++ b/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/filter/GeoServerJwtHeadersFilterConfig.java @@ -116,7 +116,12 @@ public SecurityConfig clone(boolean allowEnvParametrization) { /** what formats we support for roles in the header. */ public enum JWTHeaderRoleSource implements RoleSource { JSON, - JWT; + JWT, + + // From: PreAuthenticatedUserNameFilterConfig + Header, + UserGroupService, + RoleService; @Override public boolean equals(RoleSource other) { diff --git a/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html b/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html index 1c242dee94d..434614ef2b4 100644 --- a/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html +++ b/src/community/jwt-headers/gs-jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html @@ -45,16 +45,21 @@ visibilityDiv.style.display = "none"; } - // When the page is loaded, we hide the username "json path" input if its not needed. - window.addEventListener('load', function () { + function reset() { usernameFormatChanged(); showTokenValidationChanged(); toggleVisible(document.getElementById('validateTokenSignature'),'validateTokenSignatureURLDiv'); toggleVisible(document.getElementById('validateTokenAgainstURL'),'validateTokenAgainstURLDiv'); toggleVisible(document.getElementById('validateTokenAudience'),'validateTokenAudienceDiv'); + } - + // When the page is loaded, we hide the username "json path" input if its not needed. + window.addEventListener('load', function () { + reset(); }); + + // when creating a new jwt headers filter, we need to "kick" it. + setTimeout(reset,100); diff --git a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/JwtConfiguration.java b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/JwtConfiguration.java index 9ce5317b6f5..547df788a0b 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/JwtConfiguration.java +++ b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/JwtConfiguration.java @@ -1,7 +1,13 @@ +/* (c) 2024 Open Source Geospatial Foundation - all rights reserved + * This code is licensed under the GPL 2.0 license, available at the root + * application directory. + */ package org.geoserver.security.jwtheaders; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class JwtConfiguration implements Serializable { @@ -58,8 +64,8 @@ public class JwtConfiguration implements Serializable { // convert string of the form: // "externalRoleName1=GeoServerRoleName1;externalRoleName2=GeoServerRoleName2" // To a Map - public Map getRoleConverterAsMap() { - Map result = new HashMap<>(); + public Map> getRoleConverterAsMap() { + Map> result = new HashMap<>(); if (roleConverterString == null || roleConverterString.isBlank()) return result; // empty @@ -72,7 +78,13 @@ public Map getRoleConverterAsMap() { String key = goodCharacters(keyValue[0]); String val = goodCharacters(keyValue[1]); if (key.isBlank() || val.isBlank()) continue; - result.put(key, val); + if (!result.containsKey(key)) { + var list = new ArrayList(); + list.add(val); + result.put(key, list); + } else { + result.get(key).add(val); + } } return result; } diff --git a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/roles/RoleConverter.java b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/roles/RoleConverter.java index ba5ba46ca02..4a3672e3850 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/roles/RoleConverter.java +++ b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/roles/RoleConverter.java @@ -16,7 +16,7 @@ */ public class RoleConverter { - Map conversionMap; + Map> conversionMap; boolean externalNameMustBeListed; @@ -39,11 +39,11 @@ public List convert(List externalRoles) { if (externalRoles == null) return result; // empty for (String externalRole : externalRoles) { - String gsRole = conversionMap.get(externalRole); + List gsRole = conversionMap.get(externalRole); if (gsRole == null && !externalNameMustBeListed) { result.add(externalRole); } else if (gsRole != null) { - result.add(gsRole); + result.addAll(gsRole); } } return result; diff --git a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/token/TokenValidator.java b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/token/TokenValidator.java index fc62834d330..dfcbfb4da80 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/token/TokenValidator.java +++ b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/token/TokenValidator.java @@ -31,6 +31,10 @@ public TokenValidator(JwtConfiguration config) { public void validate(String accessToken) throws Exception { + accessToken = accessToken.replaceFirst("^Bearer", ""); + accessToken = accessToken.replaceFirst("^bearer", ""); + accessToken = accessToken.trim(); + if (!jwtHeadersConfig.isValidateToken()) { return; } diff --git a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractor.java b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractor.java index fb7f0c38998..39e32a7d02e 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractor.java +++ b/src/community/jwt-headers/jwt-headers-util/src/main/java/org/geoserver/security/jwtheaders/username/JwtHeaderUserNameExtractor.java @@ -34,7 +34,12 @@ public static Object getClaim(Map map, String path) { // if this is trivial (single item in pathList), return the value. // otherwise, go into the map one level (pathList[0]) and recurse on the result. private static Object getClaim(Map map, List pathList) { - if (pathList.size() == 1) return map.get(pathList.get(0)); + if (map == null) { + return null; + } + if (pathList.size() == 1) { + return map.get(pathList.get(0)); + } String first = pathList.get(0); pathList.remove(0); diff --git a/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java b/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java index 610fbc0a010..085791a5261 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java +++ b/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java @@ -39,6 +39,24 @@ public void testSimpleJwt() throws ParseException { Assert.assertEquals("GeoserverAdministrator", roles.get(0)); } + /** + * Test Tokens that start with "Bearer ". + * + * @throws ParseException + */ + @Test + public void testSimpleJwtBearer() throws ParseException { + String accessToken = + "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItWEdld190TnFwaWRrYTl2QXNJel82WEQtdnJmZDVyMlNWTWkwcWMyR1lNIn0.eyJleHAiOjE3MDcxNTMxNDYsImlhdCI6MTcwNzE1Mjg0NiwiYXV0aF90aW1lIjoxNzA3MTUyNjQ1LCJqdGkiOiJlMzhjY2ZmYy0zMWNjLTQ0NmEtYmU1Yy04MjliNDE0NTkyZmQiLCJpc3MiOiJodHRwczovL2xvZ2luLWxpdmUtZGV2Lmdlb2NhdC5saXZlL3JlYWxtcy9kYXZlLXRlc3QyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVhMzNlM2NjLWYwZTEtNDIxOC04OWNiLThkNDhjMjdlZWUzZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImxpdmUta2V5MiIsIm5vbmNlIjoiQldzc2M3cTBKZ0tHZC1OdFc1QlFhVlROMkhSa25LQmVIY0ZMTHZ5OXpYSSIsInNlc3Npb25fc3RhdGUiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJhY3IiOiIwIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZGF2ZS10ZXN0MiIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJsaXZlLWtleTIiOnsicm9sZXMiOlsiR2Vvc2VydmVyQWRtaW5pc3RyYXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcGhvbmUgb2ZmbGluZV9hY2Nlc3MgbWljcm9wcm9maWxlLWp3dCBwcm9maWxlIGFkZHJlc3MgZW1haWwiLCJzaWQiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJ1cG4iOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYWRkcmVzcyI6e30sIm5hbWUiOiJkYXZpZCBibGFzYnkiLCJncm91cHMiOlsiZGVmYXVsdC1yb2xlcy1kYXZlLXRlc3QyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImdpdmVuX25hbWUiOiJkYXZpZCIsImZhbWlseV9uYW1lIjoiYmxhc2J5IiwiZW1haWwiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCJ9.fHzXd7oISnqWb09ah9wikfP2UOBeiOA3vd_aDg3Bw-xcfv9aD3CWhAK5FUDPYSPyj4whAcknZbUgUzcm0qkaI8V_aS65F3Fug4jt4nC9YPL4zMSJ5an4Dp6jlQ3OQhrKFn4FwaoW61ndMmScsZZWEQyj6gzHnn5cknqySB26tVydT6q57iTO7KQFcXRdbXd6GWIoFGS-ud9XzxQMUdNfYmsDD7e6hoWhe9PJD9Zq4KT6JN13hUU4Dos-Z5SBHjRa6ieHoOe9gqkjKyA1jT1NU42Nqr-mTV-ql22nAoXuplpvOYc5-09-KDDzSDuVKFwLCNMN3ZyRF1wWuydJeU-gOQ"; + + List roles = + getExtractor(JWT.toString(), "", "resource_access.live-key2.roles") + .getRoles(accessToken).stream() + .collect(Collectors.toList()); + Assert.assertEquals(1, roles.size()); + Assert.assertEquals("GeoserverAdministrator", roles.get(0)); + } + @Test public void testSimpleJson() throws ParseException { String json = diff --git a/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java b/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java index e9bf5553180..0153c9bb28e 100644 --- a/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java +++ b/src/community/jwt-headers/jwt-headers-util/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java @@ -24,7 +24,7 @@ public void testParseNull() { JwtConfiguration config = new JwtConfiguration(); config.setRoleConverterString(null); - Map map = config.getRoleConverterAsMap(); + Map> map = config.getRoleConverterAsMap(); Assert.assertEquals(0, map.size()); config.setRoleConverterString(""); @@ -50,18 +50,18 @@ public void testParseSimple() { JwtConfiguration config = new JwtConfiguration(); config.setRoleConverterString("a=b"); - Map map = config.getRoleConverterAsMap(); + Map> map = config.getRoleConverterAsMap(); Assert.assertEquals(1, map.size()); Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); + Assert.assertEquals(Arrays.asList("b"), map.get("a")); config.setRoleConverterString("a=b;c=d"); map = config.getRoleConverterAsMap(); Assert.assertEquals(2, map.size()); Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); + Assert.assertEquals(Arrays.asList("b"), map.get("a")); Assert.assertTrue(map.containsKey("c")); - Assert.assertEquals("d", map.get("c")); + Assert.assertEquals(Arrays.asList("d"), map.get("c")); } /** @@ -75,24 +75,24 @@ public void testParseBad() { // bad format config.setRoleConverterString("a=b;c=;d"); - Map map = config.getRoleConverterAsMap(); + Map> map = config.getRoleConverterAsMap(); Assert.assertEquals(1, map.size()); Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); + Assert.assertEquals(Arrays.asList("b"), map.get("a")); // bad chars config.setRoleConverterString("a= b** ;c=**;d"); map = config.getRoleConverterAsMap(); Assert.assertEquals(1, map.size()); Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); + Assert.assertEquals(Arrays.asList("b"), map.get("a")); // removes html tags config.setRoleConverterString("a= - - - - - - - - - - - - - - - - - - - -
- - -
- - - -
- - \ No newline at end of file diff --git a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html b/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html deleted file mode 100644 index 9ef8a29979b..00000000000 --- a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - -
- - - - - - -
- - -
- -
- - -
- -
- - - - - -
- - -
- -
-
- - -
- -
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
- -
-
- - -
- -
-
- - -
-
- - -
-
- -
- -
- -
- -
- - -
-
- - -
-
- - \ No newline at end of file diff --git a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.java b/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.java deleted file mode 100644 index 290e7f750a0..00000000000 --- a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanel.java +++ /dev/null @@ -1,98 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.web; - -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JSON; -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JWT; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.markup.html.panel.Panel; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.Model; -import org.geoserver.security.config.PreAuthenticatedUserNameFilterConfig; -import org.geoserver.security.config.RoleSource; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; -import org.geoserver.security.web.auth.PreAuthenticatedUserNameFilterPanel; -import org.geoserver.security.web.auth.RoleSourceChoiceRenderer; -import org.geoserver.web.wicket.HelpLink; - -/** Jwt Headers auth panel Wicket */ -public class JwtHeadersAuthFilterPanel - extends PreAuthenticatedUserNameFilterPanel { - - protected DropDownChoice - userNameFormatChoice; - - public JwtHeadersAuthFilterPanel(String id, IModel model) { - super(id, model); - - add(new HelpLink("UsernameHelp", this).setDialog(dialog)); - add(new TextField("userNameHeaderAttributeName").setRequired(true)); - - add(new TextField("userNameJsonPath").setRequired(false)); - - add(new CheckBox("validateToken").setRequired(false)); - add(new HelpLink("validateTokenHelp", this).setDialog(dialog)); - - add(new CheckBox("validateTokenExpiry").setRequired(false)); - - add(new CheckBox("validateTokenSignature").setRequired(false)); - add(new TextField("validateTokenSignatureURL").setRequired(false)); - - add(new CheckBox("validateTokenAgainstURL").setRequired(false)); - add(new TextField("validateTokenAgainstURLEndpoint").setRequired(false)); - add(new CheckBox("validateSubjectWithEndpoint").setRequired(false)); - - add(new CheckBox("validateTokenAudience").setRequired(false)); - add(new TextField("validateTokenAudienceClaimName").setRequired(false)); - add(new TextField("validateTokenAudienceClaimValue").setRequired(false)); - - userNameFormatChoice = - new DropDownChoice( - "userNameFormatChoice", - Arrays.asList( - GeoServerJwtHeadersFilterConfig.UserNameHeaderFormat.values()), - new UserNameFormatChoiceRenderer()); - - add(userNameFormatChoice); - } - - @Override - protected DropDownChoice createRoleSourceDropDown() { - List sources = - new ArrayList<>( - Arrays.asList( - GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.values())); - sources.addAll( - Arrays.asList( - PreAuthenticatedUserNameFilterConfig.PreAuthenticatedUserNameRoleSource - .values())); - return new DropDownChoice<>("roleSource", sources, new RoleSourceChoiceRenderer()); - } - - @Override - protected Panel getRoleSourcePanel(RoleSource model) { - if (JSON.equals(model) || JWT.equals(model)) { - return new JsonClaimPanel("panel"); - } - return super.getRoleSourcePanel(model); - } - - static class JsonClaimPanel extends Panel { - public JsonClaimPanel(String id) { - super(id, new Model<>()); - add(new TextField("rolesJsonPath").setRequired(true)); - add(new TextField("rolesHeaderName").setRequired(true)); - add(new TextField("roleConverterString").setRequired(false)); - add(new CheckBox("onlyExternalListedRoles").setRequired(false)); - } - } -} diff --git a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanelInfo.java b/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanelInfo.java deleted file mode 100644 index fa4d51417a5..00000000000 --- a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/JwtHeadersAuthFilterPanelInfo.java +++ /dev/null @@ -1,23 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.web; - -import org.geoserver.security.filter.GeoServerRequestHeaderAuthenticationFilter; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilter; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; -import org.geoserver.security.web.auth.AuthenticationFilterPanelInfo; - -/** Configuration panel extension for {@link GeoServerRequestHeaderAuthenticationFilter}. */ -public class JwtHeadersAuthFilterPanelInfo - extends AuthenticationFilterPanelInfo< - GeoServerJwtHeadersFilterConfig, JwtHeadersAuthFilterPanel> { - - public JwtHeadersAuthFilterPanelInfo() { - setComponentClass(JwtHeadersAuthFilterPanel.class); - setServiceClass(GeoServerJwtHeadersFilter.class); - setServiceConfigClass(GeoServerJwtHeadersFilterConfig.class); - } -} diff --git a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/UserNameFormatChoiceRenderer.java b/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/UserNameFormatChoiceRenderer.java deleted file mode 100644 index 62f48d3a9d7..00000000000 --- a/src/community/jwt-headers/src/main/java/org/geoserver/security/jwtheaders/web/UserNameFormatChoiceRenderer.java +++ /dev/null @@ -1,29 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.web; - -import org.apache.wicket.Application; -import org.apache.wicket.markup.html.form.ChoiceRenderer; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; - -/** Wicket support class for the UserNameHeaderFormat displayed on the config page. */ -public class UserNameFormatChoiceRenderer - extends ChoiceRenderer { - - /** serialVersionUID */ - private static final long serialVersionUID = 1L; - - @Override - public Object getDisplayValue(GeoServerJwtHeadersFilterConfig.UserNameHeaderFormat rs) { - String key = "UserNameHeaderFormat." + rs.toString(); - return Application.get().getResourceSettings().getLocalizer().getString(key, null); - } - - @Override - public String getIdValue(GeoServerJwtHeadersFilterConfig.UserNameHeaderFormat rs, int index) { - return rs.toString(); - } -} diff --git a/src/community/jwt-headers/src/main/resources/GeoServerApplication.properties b/src/community/jwt-headers/src/main/resources/GeoServerApplication.properties deleted file mode 100644 index 3a67fe5d85f..00000000000 --- a/src/community/jwt-headers/src/main/resources/GeoServerApplication.properties +++ /dev/null @@ -1,43 +0,0 @@ -JwtHeadersAuthFilterPanel.short=JWT Headers -JwtHeadersAuthFilterPanel.title=HTTP JWT Request Header Authentication -JwtHeadersAuthFilterPanel.description=Authenticates by checking existence of a JWT HTTP request header - -org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilter.name=HTTP JWT Header -org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilter.title=HTTP request JWT header authentication -JwtHeadersAuthFilterPanel.userNameHeaderAttributeName=Request header attribute for User Name -JwtHeadersAuthFilterPanel.UsernameHeader=User Name Extraction From Header -JwtHeadersAuthFilterPanel.UsernameHelp.title=User Name Extraction from HTTP Headers -JwtHeadersAuthFilterPanel.UsernameHelp=\ -Where to extract the User Name from the HTTP request. -RoleSource.JWT=Header Containing JWT -RoleSource.JSON=Header Containing JSON String -JwtHeadersAuthFilterPanel.jsonPath=JSON Path (i.e. property1.property2) -JwtHeadersAuthFilterPanel.rolesHeaderName=Request Header attribute for Roles -UserNameHeaderFormat.STRING=Simple String -UserNameHeaderFormat.JSON=JSON -UserNameHeaderFormat.JWT=JWT -JwtHeadersAuthFilterPanel.formatUserName=Format the Header value is in -JwtHeadersAuthFilterPanel.userNamePath=JSON path for the User Name -JwtHeadersAuthFilterPanel.roleConverterTitle=Role Converter Map from External Roles to Geoserver Roles -JwtHeadersAuthFilterPanel.roleConverterHint=eg. ExternalRole1=GeoServerRole1;ExternalRole2=GeoServerRole2 -JwtHeadersAuthFilterPanel.externalRoleTitle=External Role Name -JwtHeadersAuthFilterPanel.gsRoleTitle=GeoServer Role Name -JwtHeadersAuthFilterPanel.roleConverterTableTitle=Role Conversion Summary -JwtHeadersAuthFilterPanel.roleConverterOnlyListedExternalRoles=Only allow External Roles that are explicitly named above -JwtHeadersAuthFilterPanel.showTokenValidation=Validate JWT (Access Token) -JwtHeadersAuthFilterPanel.validateTokenSignatureText=Validate JWT (Access Token) Signature -JwtHeadersAuthFilterPanel.validateTokenSignatureURL=JSON Web Key Set URL (jwks_uri) -JwtHeadersAuthFilterPanel.validateTokenAgainstURL=Validate JWT (Access Token) Against Endpoint -JwtHeadersAuthFilterPanel.validateTokenAgainstURLText=URL (userinfo_endpoint) -JwtHeadersAuthFilterPanel.validateTokenAgainstURLSubject=Also validate Subject -JwtHeadersAuthFilterPanel.validateTokenAudience=Validate JWT (Access Token) Audience -JwtHeadersAuthFilterPanel.validateTokenAudienceClaimName=Claim Name (usually 'aud', 'azp', or 'appid') -JwtHeadersAuthFilterPanel.validateTokenAudienceClaimValue=Required Claim Value -JwtHeadersAuthFilterPanel.validateTokenHeader=Header Attached JWT Validation -JwtHeadersAuthFilterPanel.validateTokenExpiry=Validate Token Expiry -JwtHeadersAuthFilterPanel.validateTokenHelp= \ -Options for validating a JWT Token.\ -Typically only required for Access Token validation (Bearer Tokens). -JwtHeadersAuthFilterPanel.validateTokenHelp.title=JWT Validation Options - - diff --git a/src/community/jwt-headers/src/main/resources/applicationContext.xml b/src/community/jwt-headers/src/main/resources/applicationContext.xml deleted file mode 100644 index 97b4b7d779c..00000000000 --- a/src/community/jwt-headers/src/main/resources/applicationContext.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/JwtHeadersIntegrationTest.java b/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/JwtHeadersIntegrationTest.java deleted file mode 100644 index 1bd347c74ee..00000000000 --- a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/JwtHeadersIntegrationTest.java +++ /dev/null @@ -1,344 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders; - -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JSON; -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JWT; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.SortedSet; -import java.util.stream.Collectors; -import javax.servlet.Filter; -import javax.servlet.ServletRequestEvent; -import javax.servlet.http.HttpSession; -import org.geoserver.data.test.SystemTestData; -import org.geoserver.platform.GeoServerExtensions; -import org.geoserver.security.*; -import org.geoserver.security.config.SecurityFilterConfig; -import org.geoserver.security.config.SecurityManagerConfig; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilter; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; -import org.geoserver.security.jwtheaders.filter.details.JwtHeadersWebAuthenticationDetails; -import org.geoserver.test.GeoServerSystemTestSupport; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.mock.web.MockFilterChain; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.security.web.context.HttpRequestResponseHolder; -import org.springframework.security.web.context.HttpSessionSecurityContextRepository; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.RequestContextListener; -import org.springframework.web.context.request.ServletRequestAttributes; - -public class JwtHeadersIntegrationTest extends GeoServerSystemTestSupport { - - @Override - protected void onSetUp(SystemTestData testData) throws Exception { - super.onSetUp(testData); - } - - protected GeoServerJwtHeadersFilterConfig injectConfig1() throws Exception { - GeoServerSecurityManager manager = getSecurityManager(); - - SortedSet filters = manager.listFilters(); - if (filters.contains("JwtHeaders1")) { - SecurityFilterConfig config = manager.loadFilterConfig("JwtHeaders1", false); - manager.removeFilter(config); - } - if (filters.contains("JwtHeaders2")) { - SecurityFilterConfig config = manager.loadFilterConfig("JwtHeaders2", false); - manager.removeFilter(config); - } - - GeoServerJwtHeadersFilterConfig filterConfig = new GeoServerJwtHeadersFilterConfig(); - filterConfig.setName("JwtHeaders1"); - - filterConfig.setClassName(GeoServerJwtHeadersFilter.class.getName()); - - // username - filterConfig.setUserNameJsonPath("preferred_username"); - filterConfig.setUserNameFormatChoice( - GeoServerJwtHeadersFilterConfig.UserNameHeaderFormat.JSON); - filterConfig.setUserNameHeaderAttributeName("json-header"); - - // roles - filterConfig.setRoleSource(JSON); - filterConfig.setRoleConverterString("GeoserverAdministrator=ROLE_ADMINISTRATOR"); - filterConfig.setRolesJsonPath("resource_access.live-key2.roles"); - filterConfig.setRolesHeaderName("json-header"); - filterConfig.setOnlyExternalListedRoles(true); - - manager.saveFilter(filterConfig); - - SecurityManagerConfig config = manager.getSecurityConfig(); - GeoServerSecurityFilterChain chain = config.getFilterChain(); - RequestFilterChain www = chain.getRequestChainByName("web"); - // www.setFilterNames("JwtHeaders1", "anonymous"); - www.setFilterNames("JwtHeaders1"); - - manager.saveSecurityConfig(config); - return filterConfig; - } - - protected GeoServerJwtHeadersFilterConfig injectConfig2() throws Exception { - GeoServerSecurityManager manager = getSecurityManager(); - - SortedSet filters = manager.listFilters(); - if (filters.contains("JwtHeaders1")) { - SecurityFilterConfig config = manager.loadFilterConfig("JwtHeaders1", false); - manager.removeFilter(config); - } - if (filters.contains("JwtHeaders2")) { - SecurityFilterConfig config = manager.loadFilterConfig("JwtHeaders2", false); - manager.removeFilter(config); - } - - GeoServerJwtHeadersFilterConfig filterConfig = new GeoServerJwtHeadersFilterConfig(); - filterConfig.setName("JwtHeaders2"); - - filterConfig.setClassName(GeoServerJwtHeadersFilter.class.getName()); - - // username - filterConfig.setUserNameJsonPath("preferred_username"); - filterConfig.setUserNameFormatChoice( - GeoServerJwtHeadersFilterConfig.UserNameHeaderFormat.JWT); - filterConfig.setUserNameHeaderAttributeName("json-header"); - - // roles - filterConfig.setRoleSource(JWT); - filterConfig.setRoleConverterString("GeoserverAdministrator=ROLE_ADMINISTRATOR"); - filterConfig.setRolesJsonPath("resource_access.live-key2.roles"); - filterConfig.setRolesHeaderName("json-header"); - filterConfig.setOnlyExternalListedRoles(true); - - manager.saveFilter(filterConfig); - - SecurityManagerConfig config = manager.getSecurityConfig(); - GeoServerSecurityFilterChain chain = config.getFilterChain(); - RequestFilterChain www = chain.getRequestChainByName("web"); - - www.setFilterNames("JwtHeaders2"); - - manager.saveSecurityConfig(config); - return filterConfig; - } - - /** - * Enable the Spring Security authentication filters, we want the test to be complete and - * realistic - */ - @Override - protected List getFilters() { - - SecurityManagerConfig mconfig = getSecurityManager().getSecurityConfig(); - GeoServerSecurityFilterChain filterChain = mconfig.getFilterChain(); - VariableFilterChain chain = (VariableFilterChain) filterChain.getRequestChainByName("web"); - List result = new ArrayList<>(); - for (String filterName : chain.getCompiledFilterNames()) { - try { - result.add(getSecurityManager().loadFilter(filterName)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return result; - } - - String json = - "{\"exp\":1707155912,\"iat\":1707155612,\"jti\":\"888715ae-a79d-4633-83e5-9b97dee02bbc\",\"iss\":\"https://login-live-dev.geocat.live/realms/dave-test2\",\"aud\":\"account\",\"sub\":\"ea33e3cc-f0e1-4218-89cb-8d48c27eee3d\",\"typ\":\"Bearer\",\"azp\":\"live-key2\",\"session_state\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"acr\":\"1\",\"realm_access\":{\"roles\":[\"default-roles-dave-test2\",\"offline_access\",\"uma_authorization\"]},\"resource_access\":{\"live-key2\":{\"roles\":[\"GeoserverAdministrator\"]},\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"scope\":\"openidprofileemail\",\"sid\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"email_verified\":false,\"name\":\"davidblasby\",\"preferred_username\":\"david.blasby@geocat.net\",\"given_name\":\"david\",\"family_name\":\"blasby\",\"email\":\"david.blasby@geocat.net\"}"; - - @Test - public void testSimpleJSON() throws Exception { - GeoServerJwtHeadersFilterConfig filterConfig = injectConfig1(); - // mimick user pressing on login button - MockHttpServletRequest webRequest = createRequest("web/"); - MockHttpServletResponse webResponse = executeOnSecurityFilters(webRequest); - - int responseStatus = webResponse.getStatus(); - - Assert.assertTrue(responseStatus > 400); // access denied - - webRequest = createRequest("web/"); - webRequest.addHeader("json-header", json); - - webResponse = executeOnSecurityFilters(webRequest); - - responseStatus = webResponse.getStatus(); - - Assert.assertEquals(responseStatus, 200); // good request - - // get the security context - Authentication auth = getAuthentication(webRequest, webResponse); - - Assert.assertNotNull(auth); - Assert.assertEquals(PreAuthenticatedAuthenticationToken.class, auth.getClass()); - Assert.assertEquals(JwtHeadersWebAuthenticationDetails.class, auth.getDetails().getClass()); - - String authFilterId = - ((JwtHeadersWebAuthenticationDetails) auth.getDetails()).getJwtHeadersConfigId(); - Assert.assertEquals(filterConfig.getId(), authFilterId); - - List roles = - auth.getAuthorities().stream() - .map(x -> x.getAuthority()) - .collect(Collectors.toList()); - Assert.assertEquals(2, roles.size()); - Assert.assertTrue(roles.contains("ROLE_ADMINISTRATOR")); - Assert.assertTrue(roles.contains("ROLE_AUTHENTICATED")); - int tt = 0; - } - - String accessToken = - "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItWEdld190TnFwaWRrYTl2QXNJel82WEQtdnJmZDVyMlNWTWkwcWMyR1lNIn0.eyJleHAiOjE3MDcxNTMxNDYsImlhdCI6MTcwNzE1Mjg0NiwiYXV0aF90aW1lIjoxNzA3MTUyNjQ1LCJqdGkiOiJlMzhjY2ZmYy0zMWNjLTQ0NmEtYmU1Yy04MjliNDE0NTkyZmQiLCJpc3MiOiJodHRwczovL2xvZ2luLWxpdmUtZGV2Lmdlb2NhdC5saXZlL3JlYWxtcy9kYXZlLXRlc3QyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVhMzNlM2NjLWYwZTEtNDIxOC04OWNiLThkNDhjMjdlZWUzZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImxpdmUta2V5MiIsIm5vbmNlIjoiQldzc2M3cTBKZ0tHZC1OdFc1QlFhVlROMkhSa25LQmVIY0ZMTHZ5OXpYSSIsInNlc3Npb25fc3RhdGUiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJhY3IiOiIwIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZGF2ZS10ZXN0MiIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJsaXZlLWtleTIiOnsicm9sZXMiOlsiR2Vvc2VydmVyQWRtaW5pc3RyYXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcGhvbmUgb2ZmbGluZV9hY2Nlc3MgbWljcm9wcm9maWxlLWp3dCBwcm9maWxlIGFkZHJlc3MgZW1haWwiLCJzaWQiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJ1cG4iOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYWRkcmVzcyI6e30sIm5hbWUiOiJkYXZpZCBibGFzYnkiLCJncm91cHMiOlsiZGVmYXVsdC1yb2xlcy1kYXZlLXRlc3QyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImdpdmVuX25hbWUiOiJkYXZpZCIsImZhbWlseV9uYW1lIjoiYmxhc2J5IiwiZW1haWwiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCJ9.fHzXd7oISnqWb09ah9wikfP2UOBeiOA3vd_aDg3Bw-xcfv9aD3CWhAK5FUDPYSPyj4whAcknZbUgUzcm0qkaI8V_aS65F3Fug4jt4nC9YPL4zMSJ5an4Dp6jlQ3OQhrKFn4FwaoW61ndMmScsZZWEQyj6gzHnn5cknqySB26tVydT6q57iTO7KQFcXRdbXd6GWIoFGS-ud9XzxQMUdNfYmsDD7e6hoWhe9PJD9Zq4KT6JN13hUU4Dos-Z5SBHjRa6ieHoOe9gqkjKyA1jT1NU42Nqr-mTV-ql22nAoXuplpvOYc5-09-KDDzSDuVKFwLCNMN3ZyRF1wWuydJeU-gOQ"; - - @Test - public void testSimpleJWT() throws Exception { - GeoServerJwtHeadersFilterConfig filterConfig = injectConfig2(); - // mimick user pressing on login button - MockHttpServletRequest webRequest = createRequest("web/"); - MockHttpServletResponse webResponse = executeOnSecurityFilters(webRequest); - - int responseStatus = webResponse.getStatus(); - - Assert.assertTrue(responseStatus > 400); // access denied - - webRequest = createRequest("web/"); - webRequest.addHeader("json-header", accessToken); - - webResponse = executeOnSecurityFilters(webRequest); - - responseStatus = webResponse.getStatus(); - - Assert.assertEquals(responseStatus, 200); // good request - - Authentication auth = getAuthentication(webRequest, webResponse); - - Assert.assertNotNull(auth); - Assert.assertEquals(PreAuthenticatedAuthenticationToken.class, auth.getClass()); - Assert.assertEquals(JwtHeadersWebAuthenticationDetails.class, auth.getDetails().getClass()); - - String authFilterId = - ((JwtHeadersWebAuthenticationDetails) auth.getDetails()).getJwtHeadersConfigId(); - Assert.assertEquals(filterConfig.getId(), authFilterId); - - List roles = - auth.getAuthorities().stream() - .map(x -> x.getAuthority()) - .collect(Collectors.toList()); - Assert.assertEquals(2, roles.size()); - Assert.assertTrue(roles.contains("ROLE_ADMINISTRATOR")); - Assert.assertTrue(roles.contains("ROLE_AUTHENTICATED")); - int tt = 0; - } - - @Test - public void testLogout() throws Exception { - GeoServerJwtHeadersFilterConfig filterConfig = injectConfig2(); - MockHttpServletRequest webRequest = createRequest("web/"); - MockHttpServletResponse webResponse = executeOnSecurityFilters(webRequest); - - // no token, no access - int responseStatus = webResponse.getStatus(); - Assert.assertTrue(responseStatus > 400); - HttpSession session = webRequest.getSession(); - - // add token - should have access - webRequest = createRequest("web/"); - webRequest.setSession(session); - webRequest.addHeader("json-header", accessToken); - - webResponse = executeOnSecurityFilters(webRequest); - responseStatus = webResponse.getStatus(); - Assert.assertEquals(responseStatus, 200); - - session = webRequest.getSession(); - // remove token - should not have access - webRequest = createRequest("web/"); - webRequest.setSession(session); - - webResponse = executeOnSecurityFilters(webRequest); - responseStatus = webResponse.getStatus(); - Assert.assertTrue(responseStatus > 400); - } - - String json2 = - "{\"exp\":1707155912,\"iat\":1707155612,\"jti\":\"888715ae-a79d-4633-83e5-9b97dee02bbc\",\"iss\":\"https://login-live-dev.geocat.live/realms/dave-test2\",\"aud\":\"account\",\"sub\":\"ea33e3cc-f0e1-4218-89cb-8d48c27eee3d\",\"typ\":\"Bearer\",\"azp\":\"live-key2\",\"session_state\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"acr\":\"1\",\"realm_access\":{\"roles\":[\"default-roles-dave-test2\",\"offline_access\",\"uma_authorization\"]},\"resource_access\":{\"live-key2\":{\"roles\":[\"GeoserverAdministrator\"]},\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"scope\":\"openidprofileemail\",\"sid\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"email_verified\":false,\"name\":\"davidblasby\",\"preferred_username\":\"david.blasby22@geocat.net\",\"given_name\":\"david\",\"family_name\":\"blasby\",\"email\":\"david.blasby@geocat.net\"}"; - - @Test - public void testUserNameChange() throws Exception { - GeoServerJwtHeadersFilterConfig filterConfig = injectConfig1(); - MockHttpServletRequest webRequest = createRequest("web/"); - MockHttpServletResponse webResponse = executeOnSecurityFilters(webRequest); - - // no token, no access - int responseStatus = webResponse.getStatus(); - Assert.assertTrue(responseStatus > 400); - HttpSession session = webRequest.getSession(); - - // add token - should have access - webRequest = createRequest("web/"); - webRequest.setSession(session); - webRequest.addHeader("json-header", json); - - webResponse = executeOnSecurityFilters(webRequest); - responseStatus = webResponse.getStatus(); - Assert.assertEquals(responseStatus, 200); - - Authentication auth = getAuthentication(webRequest, webResponse); - - Assert.assertEquals("david.blasby@geocat.net", auth.getPrincipal()); - - // use different json with different username! - webRequest = createRequest("web/"); - webRequest.setSession(session); - webRequest.addHeader("json-header", json2); - webResponse = executeOnSecurityFilters(webRequest); - responseStatus = webResponse.getStatus(); - Assert.assertEquals(responseStatus, 200); - - auth = getAuthentication(webRequest, webResponse); - - Assert.assertEquals("david.blasby22@geocat.net", auth.getPrincipal()); - } - - public Authentication getAuthentication( - MockHttpServletRequest request, MockHttpServletResponse response) { - SecurityContext context = - new HttpSessionSecurityContextRepository() - .loadContext(new HttpRequestResponseHolder(request, response)); - Authentication auth = context.getAuthentication(); - return auth; - } - - public HttpSession getSession() { - ServletRequestAttributes attr = - (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - HttpSession session = attr.getRequest().getSession(); - return session; - } - - private MockHttpServletResponse executeOnSecurityFilters(MockHttpServletRequest request) - throws IOException, javax.servlet.ServletException { - // for session local support in Spring - new RequestContextListener() - .requestInitialized(new ServletRequestEvent(request.getServletContext(), request)); - - // run on the - MockFilterChain chain = new MockFilterChain(); - MockHttpServletResponse response = new MockHttpServletResponse(); - GeoServerSecurityFilterChainProxy filterChainProxy = - GeoServerExtensions.bean(GeoServerSecurityFilterChainProxy.class); - filterChainProxy.doFilter(request, response, chain); - - return response; - } -} diff --git a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/filter/JwtHeadersFilterTest.java b/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/filter/JwtHeadersFilterTest.java deleted file mode 100644 index 0a2c5a796bc..00000000000 --- a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/filter/JwtHeadersFilterTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.filter; - -import java.io.IOException; -import org.geoserver.security.jwtheaders.filter.details.JwtHeadersWebAuthenticationDetails; -import org.geoserver.test.GeoServerAbstractTestSupport; -import org.junit.Test; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.util.Assert; - -public class JwtHeadersFilterTest { - - @Test - public void testExistingAuthIsFromThisConfig() throws IOException { - GeoServerJwtHeadersFilterConfig config1 = new GeoServerJwtHeadersFilterConfig(); - config1.setId("abc123"); - GeoServerJwtHeadersFilter filter1 = new GeoServerJwtHeadersFilter(); - filter1.initializeFromConfig(config1); - - GeoServerJwtHeadersFilterConfig config2 = new GeoServerJwtHeadersFilterConfig(); - config2.setId("aaaaaaaaaaa111111111111111"); - GeoServerJwtHeadersFilter filter2 = new GeoServerJwtHeadersFilter(); - filter2.initializeFromConfig(config1); - - // trivial cases - - // no existing auth - Assert.isTrue(!filter1.existingAuthIsFromThisConfig(null)); - // not from us - UsernamePasswordAuthenticationToken auth = - new UsernamePasswordAuthenticationToken(null, null, null); - Assert.isTrue(!filter1.existingAuthIsFromThisConfig(auth)); - - // details, but wrong type - auth.setDetails(new WebAuthenticationDetails(null, null)); - Assert.isTrue(!filter1.existingAuthIsFromThisConfig(auth)); - - // more complex cases - // details is JwtHeaders, but wrong id - auth.setDetails( - new JwtHeadersWebAuthenticationDetails( - config2.getId(), - new GeoServerAbstractTestSupport.GeoServerMockHttpServletRequest("", ""))); - Assert.isTrue(!filter1.existingAuthIsFromThisConfig(auth)); - - // details is JwtHeaders,right id - auth.setDetails( - new JwtHeadersWebAuthenticationDetails( - config1.getId(), - new GeoServerAbstractTestSupport.GeoServerMockHttpServletRequest("", ""))); - Assert.isTrue(filter1.existingAuthIsFromThisConfig(auth)); - } - - @Test - public void testPrincipleHasChanged() throws IOException { - GeoServerJwtHeadersFilterConfig config1 = new GeoServerJwtHeadersFilterConfig(); - config1.setId("abc123"); - GeoServerJwtHeadersFilter filter1 = new GeoServerJwtHeadersFilter(); - filter1.initializeFromConfig(config1); - - // trivial cases - Assert.isTrue(!filter1.principleHasChanged(null, null)); - Assert.isTrue(!filter1.principleHasChanged(null, "aaa")); - - UsernamePasswordAuthenticationToken auth_aaa = - new UsernamePasswordAuthenticationToken("aaa", null, null); - UsernamePasswordAuthenticationToken auth_bbb = - new UsernamePasswordAuthenticationToken("bbb", null, null); - - // aaa->aaa - Assert.isTrue(!filter1.principleHasChanged(auth_aaa, "aaa")); - - /// bbb->aaa - Assert.isTrue(filter1.principleHasChanged(auth_bbb, "aaa")); - } -} diff --git a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java b/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java deleted file mode 100644 index 5e704003300..00000000000 --- a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/JwtHeadersRolesExtractorTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.roles; - -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JSON; -import static org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource.JWT; - -import java.text.ParseException; -import java.util.List; -import java.util.stream.Collectors; -import org.geoserver.security.impl.GeoServerRole; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; -import org.junit.Assert; -import org.junit.Test; - -public class JwtHeadersRolesExtractorTest { - - public JwtHeadersRolesExtractor getExtractor( - GeoServerJwtHeadersFilterConfig.JWTHeaderRoleSource roleSource, - String roleConverterMapString, - String path) { - GeoServerJwtHeadersFilterConfig config = new GeoServerJwtHeadersFilterConfig(); - config.setRoleSource(roleSource); - config.setRoleConverterString(roleConverterMapString); - config.setRolesJsonPath(path); - return new JwtHeadersRolesExtractor(config); - } - - @Test - public void testSimpleJwt() throws ParseException { - String accessToken = - "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItWEdld190TnFwaWRrYTl2QXNJel82WEQtdnJmZDVyMlNWTWkwcWMyR1lNIn0.eyJleHAiOjE3MDcxNTMxNDYsImlhdCI6MTcwNzE1Mjg0NiwiYXV0aF90aW1lIjoxNzA3MTUyNjQ1LCJqdGkiOiJlMzhjY2ZmYy0zMWNjLTQ0NmEtYmU1Yy04MjliNDE0NTkyZmQiLCJpc3MiOiJodHRwczovL2xvZ2luLWxpdmUtZGV2Lmdlb2NhdC5saXZlL3JlYWxtcy9kYXZlLXRlc3QyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVhMzNlM2NjLWYwZTEtNDIxOC04OWNiLThkNDhjMjdlZWUzZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImxpdmUta2V5MiIsIm5vbmNlIjoiQldzc2M3cTBKZ0tHZC1OdFc1QlFhVlROMkhSa25LQmVIY0ZMTHZ5OXpYSSIsInNlc3Npb25fc3RhdGUiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJhY3IiOiIwIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZGF2ZS10ZXN0MiIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJsaXZlLWtleTIiOnsicm9sZXMiOlsiR2Vvc2VydmVyQWRtaW5pc3RyYXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcGhvbmUgb2ZmbGluZV9hY2Nlc3MgbWljcm9wcm9maWxlLWp3dCBwcm9maWxlIGFkZHJlc3MgZW1haWwiLCJzaWQiOiIxY2FiZmU1NC1lOWU0LTRjMmMtODQwNy03NTZiMjczZmFmZmIiLCJ1cG4iOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiYWRkcmVzcyI6e30sIm5hbWUiOiJkYXZpZCBibGFzYnkiLCJncm91cHMiOlsiZGVmYXVsdC1yb2xlcy1kYXZlLXRlc3QyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCIsImdpdmVuX25hbWUiOiJkYXZpZCIsImZhbWlseV9uYW1lIjoiYmxhc2J5IiwiZW1haWwiOiJkYXZpZC5ibGFzYnlAZ2VvY2F0Lm5ldCJ9.fHzXd7oISnqWb09ah9wikfP2UOBeiOA3vd_aDg3Bw-xcfv9aD3CWhAK5FUDPYSPyj4whAcknZbUgUzcm0qkaI8V_aS65F3Fug4jt4nC9YPL4zMSJ5an4Dp6jlQ3OQhrKFn4FwaoW61ndMmScsZZWEQyj6gzHnn5cknqySB26tVydT6q57iTO7KQFcXRdbXd6GWIoFGS-ud9XzxQMUdNfYmsDD7e6hoWhe9PJD9Zq4KT6JN13hUU4Dos-Z5SBHjRa6ieHoOe9gqkjKyA1jT1NU42Nqr-mTV-ql22nAoXuplpvOYc5-09-KDDzSDuVKFwLCNMN3ZyRF1wWuydJeU-gOQ"; - - List roles = - getExtractor(JWT, "", "resource_access.live-key2.roles").getRoles(accessToken) - .stream() - .collect(Collectors.toList()); - Assert.assertEquals(1, roles.size()); - Assert.assertEquals("GeoserverAdministrator", roles.get(0).getAuthority()); - } - - @Test - public void testSimpleJson() throws ParseException { - String json = - "{\"exp\":1707155912,\"iat\":1707155612,\"jti\":\"888715ae-a79d-4633-83e5-9b97dee02bbc\",\"iss\":\"https://login-live-dev.geocat.live/realms/dave-test2\",\"aud\":\"account\",\"sub\":\"ea33e3cc-f0e1-4218-89cb-8d48c27eee3d\",\"typ\":\"Bearer\",\"azp\":\"live-key2\",\"session_state\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"acr\":\"1\",\"realm_access\":{\"roles\":[\"default-roles-dave-test2\",\"offline_access\",\"uma_authorization\"]},\"resource_access\":{\"live-key2\":{\"roles\":[\"GeoserverAdministrator\"]},\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"scope\":\"openidprofileemail\",\"sid\":\"ae7796fa-b374-4754-a294-e0eb834b23b5\",\"email_verified\":false,\"name\":\"davidblasby\",\"preferred_username\":\"david.blasby@geocat.net\",\"given_name\":\"david\",\"family_name\":\"blasby\",\"email\":\"david.blasby@geocat.net\"}"; - List roles = - getExtractor(JSON, "", "resource_access.live-key2.roles").getRoles(json).stream() - .collect(Collectors.toList()); - Assert.assertEquals(1, roles.size()); - Assert.assertEquals("GeoserverAdministrator", roles.get(0).getAuthority()); - } -} diff --git a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java b/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java deleted file mode 100644 index 12917b3f375..00000000000 --- a/src/community/jwt-headers/src/test/java/org/geoserver/security/jwtheaders/roles/RoleConverterTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* (c) 2024 Open Source Geospatial Foundation - all rights reserved - * This code is licensed under the GPL 2.0 license, available at the root - * application directory. - */ - -package org.geoserver.security.jwtheaders.roles; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.geoserver.security.impl.GeoServerRole; -import org.geoserver.security.jwtheaders.filter.GeoServerJwtHeadersFilterConfig; -import org.junit.Assert; -import org.junit.Test; - -public class RoleConverterTest { - - /** - * tests parsing of the roleConverter string - * format:externalRoleName1=GeoServerRoleName1;externalRoleName2=GeoServerRoleName2" These check - * for empty maps - */ - @Test - public void testParseNull() { - GeoServerJwtHeadersFilterConfig config = new GeoServerJwtHeadersFilterConfig(); - - config.setRoleConverterString(null); - Map map = config.getRoleConverterAsMap(); - Assert.assertEquals(0, map.size()); - - config.setRoleConverterString(""); - map = config.getRoleConverterAsMap(); - Assert.assertEquals(0, map.size()); - - config.setRoleConverterString("adadadfafdasdf"); - map = config.getRoleConverterAsMap(); - Assert.assertEquals(0, map.size()); - - config.setRoleConverterString("adadadfafdasdf="); - map = config.getRoleConverterAsMap(); - Assert.assertEquals(0, map.size()); - } - - /** - * tests parsing of the roleConverter string - * format:externalRoleName1=GeoServerRoleName1;externalRoleName2=GeoServerRoleName2" These - * checks simple (correct) inputs - */ - @Test - public void testParseSimple() { - GeoServerJwtHeadersFilterConfig config = new GeoServerJwtHeadersFilterConfig(); - - config.setRoleConverterString("a=b"); - Map map = config.getRoleConverterAsMap(); - Assert.assertEquals(1, map.size()); - Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); - - config.setRoleConverterString("a=b;c=d"); - map = config.getRoleConverterAsMap(); - Assert.assertEquals(2, map.size()); - Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); - Assert.assertTrue(map.containsKey("c")); - Assert.assertEquals("d", map.get("c")); - } - - /** - * tests parsing of the roleConverter string - * format:externalRoleName1=GeoServerRoleName1;externalRoleName2=GeoServerRoleName2" These - * checks bad inputs - */ - @Test - public void testParseBad() { - GeoServerJwtHeadersFilterConfig config = new GeoServerJwtHeadersFilterConfig(); - - // bad format - config.setRoleConverterString("a=b;c=;d"); - Map map = config.getRoleConverterAsMap(); - Assert.assertEquals(1, map.size()); - Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); - - // bad chars - config.setRoleConverterString("a= b** ;c=**;d"); - map = config.getRoleConverterAsMap(); - Assert.assertEquals(1, map.size()); - Assert.assertTrue(map.containsKey("a")); - Assert.assertEquals("b", map.get("a")); - - // removes html tags - config.setRoleConverterString("a=