diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..76090127 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Add prettier to the project +41024ddb7961b04a5688bbc997cb74de6fab4763 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 55365e2e..4dd27d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -**/node_modules -.keys -.zsign_cache/ db.* deps/ios_toolchain/ deps/openssl/ dist/ +.keys +**/node_modules out +*.swo +*.swp +.zsign_cache/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..595b4cc1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,14 @@ +node_modules +src +deps +.clang-format + +# Minified files +**/*.min.css +**/*.min.js +**/leaflet.* +**/commonmark* +**/w3.css +apps/ssb/tribute.esm.js +apps/api/app.js +**/emojis.json diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 00000000..130ad1e2 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,5 @@ +trailingComma: 'es5' +useTabs: true +semi: true +singleQuote: true +bracketSpacing: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..61923ce9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to Tilde Friends + +Thank you for your interest in Tilde Friends. + +Above all, Tilde Friends aims to be a fun, safe place to play. When that is at +odds with the course of development, we will work through it with respectful +communication. + +## How can I contribute? + +The nature of Tilde Friends makes for a wide range of ways to contribute + +- Just use it. Really, just kicking the tires will probably shake out issues + in useful ways at this point. +- Report and comment on bugs: https://dev.tildefriends.net/issues. +- Make apps. You don't need my permission to make and share apps with Tilde + Friends. I hope that an ecosystem of good apps grows outside of this + repository. If you want to recreate better versions of the stock apps, just + do it. If you make a better ssb app or whatever and drop me a line however + is most convenient for you, I will probably take a look and consider + replacing the stock one with it. +- Write about it. Docs in the git repository, blog posts, private messages to + me with ideas...really there is no wrong answer. Just make some noise, and + I'll do my best to incorporate or otherwise link your feedback and make the + most of it. +- Write C code in the git repository. I'm really striving for it to be the + case that other people don't really need to meddle in there, but if you can + help out, I will gladly review your pull requests via + https://dev.tildefriends.net/pulls. + +## Best practices + +- The C code is formatted with clang-format. Run `make format`. +- The rest is formatted with prettier. Run `npm run prettier`. +- We strive to have code compile on all platforms with no warnings and run with + no sanitizer issues. +- There are tests. Run `out/debug/tildefriends test`. diff --git a/GNUmakefile b/GNUmakefile index 0baa09f8..107aa0b0 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -3,9 +3,12 @@ MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules -VERSION_CODE := 16 -VERSION_NUMBER := 0.0.16-wip -VERSION_NAME := Medium English breakfast tea. +VERSION_CODE := 17 +VERSION_NUMBER := 0.0.17-wip +VERSION_NAME := Please enjoy responsibly. + +SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3450200.zip +LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz PROJECT = tildefriends BUILD_DIR ?= out @@ -14,6 +17,18 @@ UNAME_M := $(shell uname -m) ANDROID_SDK ?= ~/Android/Sdk +ifeq ($(UNAME_M),x86_64) +ifneq ($(UNAME_S),Haiku) +debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common +debug: LDFLAGS += -fsanitize=address -fsanitize=undefined +endif +endif + +ifeq ($(UNAME_M),aarch64) +debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common +debug: LDFLAGS += -fsanitize=address -fsanitize=undefined +endif + ifeq ($(UNAME_S),Darwin) BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease else ifeq ($(UNAME_S),Linux) @@ -55,7 +70,7 @@ CFLAGS += \ ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0 ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-34 -ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.1.10909125 +ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.2.11394342 ANDROID_MIN_SDK_VERSION := 24 ANDROID_TARGET_SDK_VERSION := 34 @@ -207,18 +222,6 @@ $(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib $(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include $(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib -ifeq ($(UNAME_M),x86_64) -ifneq ($(UNAME_S),Haiku) -debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common -debug: LDFLAGS += -fsanitize=address -fsanitize=undefined -endif -endif - -ifeq ($(UNAME_M),aarch64) -debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common -debug: LDFLAGS += -fsanitize=address -fsanitize=undefined -endif - get_objs = \ $(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \ $(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \ @@ -245,7 +248,6 @@ $(APP_OBJS): CFLAGS += \ -Ideps/quickjs \ -Ideps/sqlite \ -Ideps/valgrind \ - -Ideps/xopt \ -Wdouble-promotion \ -Werror ifeq ($(UNAME_M),x86_64) @@ -497,18 +499,6 @@ $(SQLITE_OBJS): CFLAGS += \ -Wno-unused-function \ -Wno-unused-variable -XOPT_SOURCES := deps/xopt/xopt.c -XOPT_OBJS := $(call get_objs,XOPT_SOURCES) -$(filter $(BUILD_DIR)/win%,$(XOPT_OBJS)): CFLAGS += \ - -DHAVE_SNPRINTF \ - -DHAVE_VSNPRINTF \ - -DHAVE_VASNPRINTF \ - -DHAVE_VASPRINTF \ - -Dvsnprintf=rpl_vsnprintf -$(XOPT_OBJS): CFLAGS += \ - -Wno-implicit-const-int-float-conversion \ - -Wno-pointer-to-int-cast - QUICKJS_SOURCES := \ deps/quickjs/cutils.c \ deps/quickjs/libbf.c \ @@ -637,8 +627,7 @@ ALL_APP_OBJS := \ $(QUICKJS_OBJS) \ $(SODIUM_OBJS) \ $(SQLITE_OBJS) \ - $(UV_OBJS) \ - $(XOPT_OBJS) + $(UV_OBJS) DEPS = $(ALL_APP_OBJS:.o=.d) -include $(DEPS) @@ -717,7 +706,7 @@ PACKAGE_DIRS := \ deps/prettier/ \ deps/lit/ -RAW_FILES := $(filter-out apps/blog% apps/gg% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f)) +RAW_FILES := $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f)) out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release @@ -736,10 +725,11 @@ out/apk/TildeFriends-arm-%.unsigned.apk: @cp out/android$(BUILD_TYPE)-armv7a/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/tildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so - @cp out/apk/res.apk $@ + @cp out/apk/res.apk $@.zip @cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/ - @cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@ -q -9 -r . && cd ../../ - @zip -u $@ -q -9 $(RAW_FILES) + @cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ + @zip -u $@.zip -q $(RAW_FILES) + @$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@ out/apk/TildeFriends-x86-%.unsigned.apk: @mkdir -p $(dir $@) out/apk-x86-$(BUILD_TYPE)/lib/x86_64/ out/apk-x86-$(BUILD_TYPE)/lib/x86/ @@ -748,16 +738,22 @@ out/apk/TildeFriends-x86-%.unsigned.apk: @cp out/android$(BUILD_TYPE)-x86/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86_64/tildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so - @cp out/apk/res.apk $@ + @cp out/apk/res.apk $@.zip @cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/ - @cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@ -q -9 -r . && cd ../../ - @zip -u $@ -q -9 $(RAW_FILES) + @cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ + @zip -u $@.zip -q $(RAW_FILES) + @$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@ out/%.apk: out/apk/%.unsigned.apk @echo "[apksigner] $(notdir $@)" - @$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --out $@ $< + @$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $< -release-apk: out/TildeFriends-arm-release.apk out/TildeFriends-x86-release.apk +out/%.zopfli.apk: out/%.apk + @echo "[zopfli] $(notdir $@)" + $(ANDROID_BUILD_TOOLS)/zipalign -f -z 4 $< $@.zopfli + @$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $@.zopfli + +release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk .PHONY: release-apk releaseapkgo: out/TildeFriends-arm-release.apk @@ -814,11 +810,13 @@ apklog: fetchdeps: @echo "[fetch] libuv" - @test -f out/deps/libuv.tar.gz || (mkdir -p out/deps/ && curl -q https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz -o out/deps/libuv.tar.gz) - @test -d deps/libuv/ || (mkdir -p deps/libuv/ && tar -C deps/libuv/ -m --strip=1 -xf out/deps/libuv.tar.gz) + @test -f out/deps/libuv.tar.gz && test "$$(cat out/deps/libuv.txt 2>/dev/null)" = $(LIBUV_URL) || (mkdir -p out/deps/ && curl -q $(LIBUV_URL) -o out/deps/libuv.tar.gz) + @test -d deps/libuv/ && test "$$(cat out/deps/libuv.txt 2>/dev/null)" = $(LIBUV_URL) || (rm -rf deps/libuv/ && mkdir -p deps/libuv/ && tar -C deps/libuv/ -m --strip=1 -xf out/deps/libuv.tar.gz) + @echo -n $(LIBUV_URL) > out/deps/libuv.txt @echo "[fetch] sqlite" - @test -f out/deps/sqlite.zip || (mkdir -p out/deps/ && curl -q https://www.sqlite.org/2024/sqlite-amalgamation-3450100.zip -o out/deps/sqlite.zip) - @test -d deps/sqlite/ || (mkdir -p deps/sqlite/ && unzip -qDj -d deps/sqlite/ out/deps/sqlite.zip) + @test -f out/deps/sqlite.zip && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p out/deps/ && curl -q $(SQLITE_URL) -o out/deps/sqlite.zip) + @test -d deps/sqlite/ && test "$$(cat out/deps/sqlite.txt 2>/dev/null)" = $(SQLITE_URL) || (mkdir -p deps/sqlite/ && unzip -qDjo -d deps/sqlite/ out/deps/sqlite.zip) + @echo -n $(SQLITE_URL) > out/deps/sqlite.txt @echo "[fetch] prettier" @test -f deps/prettier/standalone.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/standalone.mjs @test -f deps/prettier/html.mjs || curl -q --create-dirs -O --output-dir deps/prettier/ https://cdn.jsdelivr.net/npm/prettier@3.2.5/plugins/html.mjs @@ -839,7 +837,7 @@ $(filter $(BUILD_DIR)/win%,$(APP_OBJS)): | $(WINDOWS_DEPS) endif ifeq ($(UNAME_S),Darwin) -IOS_DEPS := deps/openssl/ios/usr/local/lib/libssl.a +IOS_DEPS := deps/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a $(IOS_DEPS): +@tools/ssl-ios $(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS) @@ -855,7 +853,6 @@ dist: release-apk iosrelease-ipa @mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER) @git archive main | tar -x -C out/tildefriends-$(VERSION_NUMBER) @tar \ - --exclude=apps/gg* \ --exclude=apps/welcome* \ --exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \ --exclude=deps/libsodium/builds/msvc/vs* \ @@ -871,11 +868,10 @@ dist: release-apk iosrelease-ipa --exclude=deps/zlib/contrib/vstudio \ --exclude=deps/zlib/doc \ -caf dist/tildefriends-$(VERSION_NUMBER).tar.xz out/tildefriends-$(VERSION_NUMBER) - #@rm -rf out/tildefriends-$(VERSION_NUMBER) @echo "[cp] TildeFriends-x86-$(VERSION_NUMBER).apk" - @cp out/TildeFriends-x86-release.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk + @cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk @echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk" - @cp out/TildeFriends-arm-release.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk + @cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk @echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa" @cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa .PHONY: dist @@ -891,6 +887,10 @@ format: @clang-format -i $(wildcard src/*.c src/*.h src/*.m) .PHONY: format +prettier: + @npm run prettier +.PHONY: prettier + docs: @doxygen .PHONY: docs diff --git a/README.md b/README.md index b7c474a2..2ba21b85 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Tilde Friends + Tilde Friends is a tool for making and sharing. A public instance lives at https://www.tildefriends.net/. @@ -7,37 +8,42 @@ It is both a peer-to-peer social network client, participating in Secure Scuttlebutt, as well as a platform for writing and running web applications. ## Goals + 1. Make it easy and fun to run all sorts of web applications. 2. Provide security that is easy to understand and protects your data. 3. Make creating and sharing web applications accessible to anyone with a browser. ## Building -Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. Builds for + +Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. Builds for all of those host platforms plus mingw64, iOS, and android. -1. Requires openssl (`libssl-dev`, in debian-speak). All other dependencies +1. Requires openssl (`libssl-dev`, in debian-speak). All other dependencies are kept up to date in the tree. -2. To build, run `make debug` or `make release`. An executable will be +2. To build, run `make debug` or `make release`. An executable will be generated in a subdirectory of `out/`. 3. It's possible to build for Android, iOS, and Windows on Linux, if you have - the right dependencies in the right places. `make windebug winrelease - iosdebug-ipa iosrelease-ipa release-apk`. + the right dependencies in the right places. `make windebug winrelease +iosdebug-ipa iosrelease-ipa release-apk`. 4. To build in docker, `docker build .`. 5. `make format` will normalize formatting to the coding standard. ## Running + By default, running the built `tildefriends` executable will start a web server -at . `tildefriends -h` lists further options. +at . `tildefriends -h` lists further options. The first user to create an account and log in will be granted administrative -privileges. Further administration can be done at +privileges. Further administration can be done at . ## Documentation + Docs are a work in progress: . ## License + All code unless otherwise noted in is provided under the [MIT](https://opensource.org/licenses/MIT) license. diff --git a/apps/admin.json b/apps/admin.json index 711779a2..a9a2e723 100644 --- a/apps/admin.json +++ b/apps/admin.json @@ -1,4 +1,4 @@ { - "type": "tildefriends-app", - "emoji": "๐ŸŽ›" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐ŸŽ›" +} diff --git a/apps/admin/app.js b/apps/admin/app.js index 0a1c35e8..5916f11f 100644 --- a/apps/admin/app.js +++ b/apps/admin/app.js @@ -18,9 +18,13 @@ async function main() { for (let user of await core.users()) { data.users[user] = await core.permissionsForUser(user); } - await app.setDocument(utf8Decode(getFile('index.html')).replace('$data', JSON.stringify(data))); + await app.setDocument( + utf8Decode(getFile('index.html')).replace('$data', JSON.stringify(data)) + ); } catch { - await app.setDocument('Only an administrator can modify these settings.'); + await app.setDocument( + 'Only an administrator can modify these settings.' + ); } } -main(); \ No newline at end of file +main(); diff --git a/apps/admin/index.html b/apps/admin/index.html index eabbf708..114ab1b9 100644 --- a/apps/admin/index.html +++ b/apps/admin/index.html @@ -1,10 +1,12 @@ - + - +

Tilde Friends Administration

- \ No newline at end of file + diff --git a/apps/admin/script.js b/apps/admin/script.js index 6d43d291..8fabc5a7 100644 --- a/apps/admin/script.js +++ b/apps/admin/script.js @@ -3,25 +3,32 @@ import * as tfrpc from '/static/tfrpc.js'; function delete_user(user) { if (confirm(`Are you sure you want to delete the user "${user}"?`)) { - tfrpc.rpc.delete_user(user).then(function() { - alert(`User "${user}" deleted successfully.`); - }).catch(function(error) { - alert(`Failed to delete user "${user}": ${JSON.stringify(error, null, 2)}.`); - }); + tfrpc.rpc + .delete_user(user) + .then(function () { + alert(`User "${user}" deleted successfully.`); + }) + .catch(function (error) { + alert( + `Failed to delete user "${user}": ${JSON.stringify(error, null, 2)}.` + ); + }); } } function global_settings_set(key, value) { - tfrpc.rpc.global_settings_set(key, value).then(function() { - alert(`Set "${key}" to "${value}".`); - }).catch(function(error) { - alert(`Failed to set "${key}": ${JSON.stringify(error, null, 2)}.`); - }); + tfrpc.rpc + .global_settings_set(key, value) + .then(function () { + alert(`Set "${key}" to "${value}".`); + }) + .catch(function (error) { + alert(`Failed to set "${key}": ${JSON.stringify(error, null, 2)}.`); + }); } -window.addEventListener('load', function() { - const permission_template = (permission) => - html` ${permission}`; +window.addEventListener('load', function () { + const permission_template = (permission) => html` ${permission}`; function input_template(key, description) { if (description.type === 'boolean') { return html` @@ -62,26 +69,24 @@ window.addEventListener('load', function() { } const user_template = (user, permissions) => html`
  • - - ${user}: - ${permissions.map(x => permission_template(x))} + + ${user}: ${permissions.map((x) => permission_template(x))}
  • `; const users_template = (users) => html`

    Users

    -
      - ${Object.entries(users).map(u => user_template(u[0], u[1]))} -
    `; +
      + ${Object.entries(users).map((u) => user_template(u[0], u[1]))} +
    `; const page_template = (data) => html`

    Global Settings

    - ${Object.keys(data.settings).sort().map(x => html`${input_template(x, data.settings[x])}`)} + ${Object.keys(data.settings) + .sort() + .map((x) => html`${input_template(x, data.settings[x])}`)}
    ${users_template(data.users)} -
    - `; + `; render(page_template(g_data), document.body); -}); \ No newline at end of file +}); diff --git a/apps/api.json b/apps/api.json index 2e226336..511730eb 100644 --- a/apps/api.json +++ b/apps/api.json @@ -1,5 +1,5 @@ { - "type": "tildefriends-app", - "emoji": "๐Ÿ“œ", - "previous": "&miGORZ8BwjHg2YO0t4bms6SI28XWPYqnqOZ8u9zsbZc=.sha256" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐Ÿ“œ", + "previous": "&miGORZ8BwjHg2YO0t4bms6SI28XWPYqnqOZ8u9zsbZc=.sha256" +} diff --git a/apps/api/docs.js b/apps/api/docs.js index 9e045bbb..037bf240 100644 --- a/apps/api/docs.js +++ b/apps/api/docs.js @@ -219,7 +219,7 @@ Parses an HTTP response. * *Object* An object with **bytes_parsed**, **minor_version**, **status**, **message**, and **headers** fields on successful parse. `; -docs['sha1Digest()'] =` +docs['sha1Digest()'] = ` Calculates a SHA1 digest. Completes synchronously. @@ -353,4 +353,4 @@ Call a remote function. * **...** Parameters to pass to the function. ### Returns The return value of the called function. -`; \ No newline at end of file +`; diff --git a/apps/apps.json b/apps/apps.json index 7b0370a0..374f3de6 100644 --- a/apps/apps.json +++ b/apps/apps.json @@ -1,5 +1,5 @@ { - "type": "tildefriends-app", - "emoji": "๐Ÿ’ป", - "previous": "&RdVEsVscZm3aWzcMrEZS8mskO5tUmvaEUihex2MMfZQ=.sha256" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐Ÿ’ป", + "previous": "&icsPplXHgmpkbNWyo/WTvRdT/A8BXxW4lJixOtP4ueQ=.sha256" +} diff --git a/apps/apps/app.js b/apps/apps/app.js index d31f1ab7..f9272353 100644 --- a/apps/apps/app.js +++ b/apps/apps/app.js @@ -26,14 +26,15 @@ async function fetch_info(apps) { async function fetch_shared_apps() { let messages = {}; - await ssb.sqlAsync(` - SELECT messages.* + await ssb.sqlAsync( + ` + SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature FROM messages_fts('"application/tildefriends"') JOIN messages ON messages.rowid = messages_fts.rowid - ORDER BY timestamp + ORDER BY messages.timestamp `, [], - function(row) { + function (row) { let content = JSON.parse(row.content); for (let mention of content.mentions) { if (mention?.type === 'application/tildefriends') { @@ -44,10 +45,13 @@ async function fetch_shared_apps() { }; } } - }); + } + ); let result = {}; - for (let app of Object.values(messages).sort((x, y) => y.message.timestamp - x.message.timestamp)) { + for (let app of Object.values(messages).sort( + (x, y) => y.message.timestamp - x.message.timestamp + )) { let app_object = JSON.parse(utf8Decode(await ssb.blobGet(app.blob))); if (app_object) { app_object.blob_id = app.blob; diff --git a/apps/blog.json b/apps/blog.json index 782fa7bf..d6a298e8 100644 --- a/apps/blog.json +++ b/apps/blog.json @@ -1,5 +1,5 @@ { - "type": "tildefriends-app", - "emoji": "๐Ÿชต", - "previous": "&TIrBnpN3iz3O9L9MCCteAcVJZjA83EKdcfu4SCM76VE=.sha256" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐Ÿชต", + "previous": "&TIrBnpN3iz3O9L9MCCteAcVJZjA83EKdcfu4SCM76VE=.sha256" +} diff --git a/apps/blog/app.js b/apps/blog/app.js index 99c0165d..66a800db 100644 --- a/apps/blog/app.js +++ b/apps/blog/app.js @@ -5,4 +5,4 @@ async function main() { await app.setDocument(blog.render_html(blogs)); } -main(); \ No newline at end of file +main(); diff --git a/apps/blog/blog.js b/apps/blog/blog.js index 4a3e0866..d2ba4e01 100644 --- a/apps/blog/blog.js +++ b/apps/blog/blog.js @@ -1,11 +1,19 @@ import * as commonmark from './commonmark.min.js'; function escape(text) { - return (text ?? '').replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>'); + return (text ?? '') + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>'); } function escapeAttribute(text) { - return (text ?? '').replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", '''); + return (text ?? '') + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", '''); } export async function get_blog_message(id) { @@ -13,7 +21,7 @@ export async function get_blog_message(id) { await ssb.sqlAsync( 'SELECT author, timestamp, content FROM messages WHERE id = ?', [id], - function(row) { + function (row) { let content = JSON.parse(row.content); message = { author: row.author, @@ -21,7 +29,8 @@ export async function get_blog_message(id) { blog: content?.blog, title: content?.title, }; - }); + } + ); if (message) { await ssb.sqlAsync( ` @@ -34,9 +43,10 @@ export async function get_blog_message(id) { ORDER BY sequence DESC LIMIT 1 `, [message.author], - function(row) { + function (row) { message.name = row.name; - }); + } + ); } return message; } @@ -51,8 +61,12 @@ export function markdown(md) { node = event.node; if (event.entering) { if (node.destination?.startsWith('&')) { - node.destination = '/' + node.destination + '/view?filename=' + node.firstChild?.literal; - } else if (node.destination?.startsWith('@') || node.destination?.startsWith('%')) { + node.destination = + '/' + node.destination + '/view?filename=' + node.firstChild?.literal; + } else if ( + node.destination?.startsWith('@') || + node.destination?.startsWith('%') + ) { node.destination = '/~core/ssb/#' + escape(node.destination); } } @@ -107,7 +121,7 @@ export function render_html(blogs) {

    ๐ŸชตTilde Friends Blog

    atom feed
    - ${blogs.map(blog_post => render_blog_post(blog_post)).join('\n')} + ${blogs.map((blog_post) => render_blog_post(blog_post)).join('\n')} `; } @@ -135,14 +149,15 @@ export function render_atom(blogs) { ${core.url} ${new Date().toString()} - ${blogs.map(blog_post => render_blog_post_atom(blog_post)).join('\n')} + ${blogs.map((blog_post) => render_blog_post_atom(blog_post)).join('\n')} `; } export async function get_posts() { let blogs = []; let ids = await ssb.getIdentities(); - await ssb.sqlAsync(` + await ssb.sqlAsync( + ` WITH blogs AS ( SELECT @@ -182,8 +197,11 @@ export async function get_posts() { JOIN public ON public.author = blogs.author LEFT OUTER JOIN names ON names.author = blogs.author ORDER BY blogs.timestamp DESC LIMIT 20 - `, [JSON.stringify(ids)], function(row) { - blogs.push(row); - }); + `, + [JSON.stringify(ids)], + function (row) { + blogs.push(row); + } + ); return blogs; -} \ No newline at end of file +} diff --git a/apps/blog/handler.js b/apps/blog/handler.js index ef14b62b..32b54bef 100644 --- a/apps/blog/handler.js +++ b/apps/blog/handler.js @@ -2,30 +2,50 @@ import * as blog from './blog.js'; async function main() { if (request.path.startsWith('%') && request.path.endsWith('.sha256')) { - let id = request.path.startsWith('%25') ? '%' + request.path.substring(3) : request.path; + let id = request.path.startsWith('%25') + ? '%' + request.path.substring(3) + : request.path; let message = await blog.get_blog_message(id); if (message) { - respond({data: await blog.render_blog_post_html(message), content_type: 'text/html; charset=utf-8'}); + respond({ + data: await blog.render_blog_post_html(message), + content_type: 'text/html; charset=utf-8', + }); } else { - respond({data: `Message ${id} not found.`, content_type: 'text/html; charset=utf-8'}); + respond({ + data: `Message ${id} not found.`, + content_type: 'text/html; charset=utf-8', + }); } } else if (request.path == 'atom') { let blogs = await blog.get_posts(); - respond({data: blog.render_atom(blogs), content_type: 'application/atom+xml'}); + respond({ + data: blog.render_atom(blogs), + content_type: 'application/atom+xml', + }); } else { let blogs = await blog.get_posts(); for (let blog_post of blogs) { let title = (blog_post.title || '').replaceAll(/\W/g, '_').toLowerCase(); if (request.path === title) { - respond({data: await blog.render_blog_post_html(blog_post), content_type: 'text/html; charset=utf-8'}); + respond({ + data: await blog.render_blog_post_html(blog_post), + content_type: 'text/html; charset=utf-8', + }); return; } } - respond({data: blog.render_html(blogs), content_type: 'text/html; charset=utf-8'}); + respond({ + data: blog.render_html(blogs), + content_type: 'text/html; charset=utf-8', + }); } } -main().catch(function(error) { - respond({data: ` -
    ${error.message}\n${error.stack}
    `, content_type: 'text/html'}); -}); \ No newline at end of file +main().catch(function (error) { + respond({ + data: ` +
    ${error.message}\n${error.stack}
    `, + content_type: 'text/html', + }); +}); diff --git a/apps/db.json b/apps/db.json index 3a02f75d..f3ed581e 100644 --- a/apps/db.json +++ b/apps/db.json @@ -1,4 +1,4 @@ { - "type": "tildefriends-app", - "emoji": "๐Ÿ’ฝ" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐Ÿ’ฝ" +} diff --git a/apps/db/app.js b/apps/db/app.js index 6e6c2c41..1f65b890 100644 --- a/apps/db/app.js +++ b/apps/db/app.js @@ -51,7 +51,7 @@ async function key_list(db) { app.setDocument(doc); } -core.register('message', async function(message) { +core.register('message', async function (message) { if (message.event == 'hashChange') { let hash = message.hash.substring(1); if (hash.startsWith(':shared:')) { @@ -67,4 +67,4 @@ core.register('message', async function(message) { } }); -database_list(); \ No newline at end of file +database_list(); diff --git a/apps/follow.json b/apps/follow.json index 5e137e35..3cf770d7 100644 --- a/apps/follow.json +++ b/apps/follow.json @@ -1,4 +1,4 @@ { - "type": "tildefriends-app", - "emoji": "โžก๏ธ" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "โžก๏ธ" +} diff --git a/apps/follow/app.js b/apps/follow/app.js index 676b7423..098f8f22 100644 --- a/apps/follow/app.js +++ b/apps/follow/app.js @@ -2,7 +2,7 @@ let g_about_cache = {}; async function query(sql, args) { let result = []; - await ssb.sqlAsync(sql, args, function(row) { + await ssb.sqlAsync(sql, args, function (row) { result.push(row); }); return result; @@ -21,7 +21,8 @@ async function contacts_internal(id, last_row_id, following, max_row_id) { json_extract(content, '$.type') = 'contact' ORDER BY sequence `, - [id, last_row_id, max_row_id]); + [id, last_row_id, max_row_id] + ); for (let row of contacts) { let contact = JSON.parse(row.content); if (contact.following === true) { @@ -42,15 +43,34 @@ async function contact(id, last_row_id, following, max_row_id) { return await contacts_internal(id, last_row_id, following, max_row_id); } -async function following_deep_internal(ids, depth, blocking, last_row_id, following, max_row_id) { - let contacts = await Promise.all([...new Set(ids)].map(x => contact(x, last_row_id, following, max_row_id))); +async function following_deep_internal( + ids, + depth, + blocking, + last_row_id, + following, + max_row_id +) { + let contacts = await Promise.all( + [...new Set(ids)].map((x) => contact(x, last_row_id, following, max_row_id)) + ); let result = {}; for (let i = 0; i < ids.length; i++) { let id = ids[i]; let contact = contacts[i]; let all_blocking = Object.assign({}, contact.blocking, blocking); - let found = Object.keys(contact.following).filter(y => !all_blocking[y]); - let deeper = depth > 1 ? await following_deep_internal(found, depth - 1, all_blocking, last_row_id, following, max_row_id) : []; + let found = Object.keys(contact.following).filter((y) => !all_blocking[y]); + let deeper = + depth > 1 + ? await following_deep_internal( + found, + depth - 1, + all_blocking, + last_row_id, + following, + max_row_id + ) + : []; result[id] = [id, ...found, ...deeper]; } return [...new Set(Object.values(result).flat())]; @@ -68,10 +88,22 @@ async function following_deep(ids, depth, blocking) { last_row_id: 0, }; } - let max_row_id = (await query(` + let max_row_id = ( + await query( + ` SELECT MAX(rowid) AS max_row_id FROM messages - `, []))[0].max_row_id; - let result = await following_deep_internal(ids, depth, blocking, cache.last_row_id, cache.following, max_row_id); + `, + [] + ) + )[0].max_row_id; + let result = await following_deep_internal( + ids, + depth, + blocking, + cache.last_row_id, + cache.following, + max_row_id + ); cache.last_row_id = max_row_id; let store = JSON.stringify(cache); await db.set('following', store); @@ -90,13 +122,15 @@ async function fetch_about(db, ids, users) { }; } let max_row_id = 0; - await ssb.sqlAsync(` + await ssb.sqlAsync( + ` SELECT MAX(rowid) AS max_row_id FROM messages `, [], - function(row) { + function (row) { max_row_id = row.max_row_id; - }); + } + ); for (let id of Object.keys(cache.about)) { if (ids.indexOf(id) == -1) { delete cache.about[id]; @@ -129,17 +163,21 @@ async function fetch_about(db, ids, users) { ORDER BY messages.author, messages.sequence `, [ - JSON.stringify(ids.filter(id => cache.about[id])), - JSON.stringify(ids.filter(id => !cache.about[id])), + JSON.stringify(ids.filter((id) => cache.about[id])), + JSON.stringify(ids.filter((id) => !cache.about[id])), cache.last_row_id, max_row_id, - ]); + ] + ); for (let about of abouts) { let content = JSON.parse(about.content); if (content.about === about.author) { delete content.type; delete content.about; - cache.about[about.author] = Object.assign(cache.about[about.author] || {}, content); + cache.about[about.author] = Object.assign( + cache.about[about.author] || {}, + content + ); } } cache.last_row_id = max_row_id; @@ -155,41 +193,41 @@ async function getAbout(db, id) { if (g_about_cache[id]) { return g_about_cache[id]; } - let o = await db.get(id + ":about"); + let o = await db.get(id + ':about'); const k_version = 4; let f = o ? JSON.parse(o) : o; if (!f || f.version != k_version) { f = {about: {}, sequence: 0, version: k_version}; } await ssb.sqlAsync( - "SELECT "+ - " sequence, "+ - " content "+ - "FROM messages "+ - "WHERE "+ - " author = ?1 AND "+ - " sequence > ?2 AND "+ - " json_extract(content, '$.type') = 'about' AND "+ - " json_extract(content, '$.about') = ?1 "+ - "UNION SELECT MAX(sequence) as sequence, NULL FROM messages WHERE author = ?1 "+ - "ORDER BY sequence", + 'SELECT ' + + ' sequence, ' + + ' content ' + + 'FROM messages ' + + 'WHERE ' + + ' author = ?1 AND ' + + ' sequence > ?2 AND ' + + " json_extract(content, '$.type') = 'about' AND " + + " json_extract(content, '$.about') = ?1 " + + 'UNION SELECT MAX(sequence) as sequence, NULL FROM messages WHERE author = ?1 ' + + 'ORDER BY sequence', [id, f.sequence], - function(row) { + function (row) { f.sequence = row.sequence; if (row.content) { let about = {}; try { about = JSON.parse(row.content); - } catch { - } + } catch {} delete about.about; delete about.type; f.about = Object.assign(f.about, about); } - }); + } + ); let j = JSON.stringify(f); if (o != j) { - await db.set(id + ":about", j); + await db.set(id + ':about', j); } g_about_cache[id] = f.about; return f.about; @@ -198,15 +236,15 @@ async function getAbout(db, id) { async function getSize(db, id) { let size = 0; await ssb.sqlAsync( - "SELECT (LENGTH(author) * COUNT(*) + SUM(LENGTH(content)) + SUM(LENGTH(id))) AS size FROM messages WHERE author = ?1", + 'SELECT (LENGTH(author) * COUNT(*) + SUM(LENGTH(content)) + SUM(LENGTH(id))) AS size FROM messages WHERE author = ?1', [id], function (row) { size += row.size; - }); + } + ); return size; } - async function getSizes(ids) { let sizes = {}; await ssb.sqlAsync( @@ -221,7 +259,8 @@ async function getSizes(ids) { [JSON.stringify(ids)], function (row) { sizes[row.author] = row.size; - }); + } + ); return sizes; } @@ -241,7 +280,10 @@ function niceSize(bytes) { } function escape(value) { - return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>'); + return value + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>'); } async function main() { @@ -249,19 +291,27 @@ async function main() { let db = await database('ssb'); let whoami = await ssb.getIdentities(); let tree = ''; - await app.setDocument(`
    Enumerating followed users...
    `); + await app.setDocument( + `
    Enumerating followed users...
    ` + ); let following = await following_deep(whoami, 2, {}); - await app.setDocument(`
    Getting names and sizes...
    `); + await app.setDocument( + `
    Getting names and sizes...
    ` + ); let [about, sizes] = await Promise.all([ fetch_about(db, following, {}), getSizes(following), ]); await app.setDocument(`
    Finishing...
    `); - following.sort((a, b) => ((sizes[b] ?? 0) - (sizes[a] ?? 0))); + following.sort((a, b) => (sizes[b] ?? 0) - (sizes[a] ?? 0)); for (let id of following) { tree += `
  • ${escape(about[id]?.name ?? id)} ${niceSize(sizes[id] ?? 0)}
  • \n`; } - await app.setDocument('\n\n

    Following

    \n
      ' + tree + '
    \n\n'); + await app.setDocument( + '\n\n

    Following

    \n
      ' + + tree + + '
    \n\n' + ); } -main(); \ No newline at end of file +main(); diff --git a/apps/gg.json b/apps/gg.json deleted file mode 100644 index b5a6f698..00000000 --- a/apps/gg.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "tildefriends-app", - "emoji": "๐Ÿ—บ", - "previous": "&0XSp+xdQwVtQ88bXzvWdH15Ex63hv5zUKTa4zx7HBGM=.sha256" -} \ No newline at end of file diff --git a/apps/gg/app.js b/apps/gg/app.js deleted file mode 100644 index d8ae30db..00000000 --- a/apps/gg/app.js +++ /dev/null @@ -1,80 +0,0 @@ -import * as tfrpc from '/tfrpc.js'; -import * as strava from './strava.js'; - -let g_database; -let g_shared_database; - -tfrpc.register(async function createIdentity() { - return ssb.createIdentity(); -}); -tfrpc.register(async function appendMessage(id, message) { - print('APPEND', JSON.stringify(message)); - return ssb.appendMessageWithIdentity(id, message); -}); -tfrpc.register(function url() { - return core.url; -}); -tfrpc.register(async function getUser() { - return core.user; -}); -tfrpc.register(function getIdentities() { - return ssb.getIdentities(); -}); -tfrpc.register(async function databaseGet(key) { - return g_database ? g_database.get(key) : undefined; -}); -tfrpc.register(async function databaseSet(key, value) { - return g_database ? g_database.set(key, value) : undefined; -}); -tfrpc.register(async function databaseRemove(key, value) { - return g_database ? g_database.remove(key, value) : undefined; -}); -tfrpc.register(async function sharedDatabaseGet(key) { - return g_shared_database ? g_shared_database.get(key) : undefined; -}); -tfrpc.register(async function sharedDatabaseSet(key, value) { - return g_shared_database ? g_shared_database.set(key, value) : undefined; -}); -tfrpc.register(async function sharedDatabaseRemove(key, value) { - return g_shared_database ? g_shared_database.remove(key, value) : undefined; -}); -tfrpc.register(async function query(sql, args) { - let result = []; - await ssb.sqlAsync(sql, args, function callback(row) { - result.push(row); - }); - return result; -}); -tfrpc.register(async function store_blob(blob) { - if (typeof(blob) == 'string') { - blob = utf8Encode(blob); - } - if (Array.isArray(blob)) { - blob = Uint8Array.from(blob); - } - return await ssb.blobStore(blob); -}); - -tfrpc.register(async function get_blob(id) { - return utf8Decode(await ssb.blobGet(id)); -}); -tfrpc.register(strava.refresh_token); - -async function main() { - g_shared_database = await shared_database('state'); - if (core.user.credentials?.session?.name) { - g_database = await database('state'); - } - - let attempt; - if (core.user.credentials?.session?.name) { - let shared_db = await shared_database('state'); - attempt = await shared_db.get(core.user.credentials.session.name); - } - app.setDocument(utf8Decode(getFile('index.html')).replace('${data}', JSON.stringify({ - attempt: attempt, - state: core.user?.credentials?.session?.name, - }))); -} - -main(); \ No newline at end of file diff --git a/apps/gg/emojis.json b/apps/gg/emojis.json deleted file mode 100644 index 9c28b6e2..00000000 --- a/apps/gg/emojis.json +++ /dev/null @@ -1 +0,0 @@ -{"Smileys & Emotion":{"grinning face":"๐Ÿ˜€","grinning face with big eyes":"๐Ÿ˜ƒ","grinning face with smiling eyes":"๐Ÿ˜„","beaming face with smiling eyes":"๐Ÿ˜","grinning squinting face":"๐Ÿ˜†","grinning face with sweat":"๐Ÿ˜…","rolling on the floor laughing":"๐Ÿคฃ","face with tears of joy":"๐Ÿ˜‚","slightly smiling face":"๐Ÿ™‚","upside-down face":"๐Ÿ™ƒ","melting face":"๐Ÿซ ","winking face":"๐Ÿ˜‰","smiling face with smiling eyes":"๐Ÿ˜Š","smiling face with halo":"๐Ÿ˜‡","smiling face with hearts":"๐Ÿฅฐ","smiling face with heart-eyes":"๐Ÿ˜","star-struck":"๐Ÿคฉ","face blowing a kiss":"๐Ÿ˜˜","kissing face":"๐Ÿ˜—","smiling face":"โ˜บ๏ธ","kissing face with closed eyes":"๐Ÿ˜š","kissing face with smiling eyes":"๐Ÿ˜™","smiling face with tear":"๐Ÿฅฒ","face savoring food":"๐Ÿ˜‹","face with tongue":"๐Ÿ˜›","winking face with tongue":"๐Ÿ˜œ","zany face":"๐Ÿคช","squinting face with tongue":"๐Ÿ˜","money-mouth face":"๐Ÿค‘","smiling face with open hands":"๐Ÿค—","face with hand over mouth":"๐Ÿคญ","face with open eyes and hand over mouth":"๐Ÿซข","face with peeking eye":"๐Ÿซฃ","shushing face":"๐Ÿคซ","thinking face":"๐Ÿค”","saluting face":"๐Ÿซก","zipper-mouth face":"๐Ÿค","face with raised eyebrow":"๐Ÿคจ","neutral face":"๐Ÿ˜","expressionless face":"๐Ÿ˜‘","face without mouth":"๐Ÿ˜ถ","dotted line face":"๐Ÿซฅ","face in clouds":"๐Ÿ˜ถโ€๐ŸŒซ๏ธ","smirking face":"๐Ÿ˜","unamused face":"๐Ÿ˜’","face with rolling eyes":"๐Ÿ™„","grimacing face":"๐Ÿ˜ฌ","face exhaling":"๐Ÿ˜ฎโ€๐Ÿ’จ","lying face":"๐Ÿคฅ","shaking face":"๐Ÿซจ","relieved face":"๐Ÿ˜Œ","pensive face":"๐Ÿ˜”","sleepy face":"๐Ÿ˜ช","drooling face":"๐Ÿคค","sleeping face":"๐Ÿ˜ด","face with medical mask":"๐Ÿ˜ท","face with thermometer":"๐Ÿค’","face with head-bandage":"๐Ÿค•","nauseated face":"๐Ÿคข","face vomiting":"๐Ÿคฎ","sneezing face":"๐Ÿคง","hot face":"๐Ÿฅต","cold face":"๐Ÿฅถ","woozy face":"๐Ÿฅด","face with crossed-out eyes":"๐Ÿ˜ต","face with spiral eyes":"๐Ÿ˜ตโ€๐Ÿ’ซ","exploding head":"๐Ÿคฏ","cowboy hat face":"๐Ÿค ","partying face":"๐Ÿฅณ","disguised face":"๐Ÿฅธ","smiling face with sunglasses":"๐Ÿ˜Ž","nerd face":"๐Ÿค“","face with monocle":"๐Ÿง","confused face":"๐Ÿ˜•","face with diagonal mouth":"๐Ÿซค","worried face":"๐Ÿ˜Ÿ","slightly frowning face":"๐Ÿ™","frowning face":"โ˜น๏ธ","face with open mouth":"๐Ÿ˜ฎ","hushed face":"๐Ÿ˜ฏ","astonished face":"๐Ÿ˜ฒ","flushed face":"๐Ÿ˜ณ","pleading face":"๐Ÿฅบ","face holding back tears":"๐Ÿฅน","frowning face with open mouth":"๐Ÿ˜ฆ","anguished face":"๐Ÿ˜ง","fearful face":"๐Ÿ˜จ","anxious face with sweat":"๐Ÿ˜ฐ","sad but relieved face":"๐Ÿ˜ฅ","crying face":"๐Ÿ˜ข","loudly crying face":"๐Ÿ˜ญ","face screaming in fear":"๐Ÿ˜ฑ","confounded face":"๐Ÿ˜–","persevering face":"๐Ÿ˜ฃ","disappointed face":"๐Ÿ˜ž","downcast face with sweat":"๐Ÿ˜“","weary face":"๐Ÿ˜ฉ","tired face":"๐Ÿ˜ซ","yawning face":"๐Ÿฅฑ","face with steam from nose":"๐Ÿ˜ค","enraged face":"๐Ÿ˜ก","angry face":"๐Ÿ˜ ","face with symbols on mouth":"๐Ÿคฌ","smiling face with horns":"๐Ÿ˜ˆ","angry face with horns":"๐Ÿ‘ฟ","skull":"๐Ÿ’€","skull and crossbones":"โ˜ ๏ธ","pile of poo":"๐Ÿ’ฉ","clown face":"๐Ÿคก","ogre":"๐Ÿ‘น","goblin":"๐Ÿ‘บ","ghost":"๐Ÿ‘ป","alien":"๐Ÿ‘ฝ","alien monster":"๐Ÿ‘พ","robot":"๐Ÿค–","grinning cat":"๐Ÿ˜บ","grinning cat with smiling eyes":"๐Ÿ˜ธ","cat with tears of joy":"๐Ÿ˜น","smiling cat with heart-eyes":"๐Ÿ˜ป","cat with wry smile":"๐Ÿ˜ผ","kissing cat":"๐Ÿ˜ฝ","weary cat":"๐Ÿ™€","crying cat":"๐Ÿ˜ฟ","pouting cat":"๐Ÿ˜พ","see-no-evil monkey":"๐Ÿ™ˆ","hear-no-evil monkey":"๐Ÿ™‰","speak-no-evil monkey":"๐Ÿ™Š","love letter":"๐Ÿ’Œ","heart with arrow":"๐Ÿ’˜","heart with ribbon":"๐Ÿ’","sparkling heart":"๐Ÿ’–","growing heart":"๐Ÿ’—","beating heart":"๐Ÿ’“","revolving hearts":"๐Ÿ’ž","two hearts":"๐Ÿ’•","heart decoration":"๐Ÿ’Ÿ","heart exclamation":"โฃ๏ธ","broken heart":"๐Ÿ’”","heart on fire":"โค๏ธโ€๐Ÿ”ฅ","mending heart":"โค๏ธโ€๐Ÿฉน","red heart":"โค๏ธ","pink heart":"๐Ÿฉท","orange heart":"๐Ÿงก","yellow heart":"๐Ÿ’›","green heart":"๐Ÿ’š","blue heart":"๐Ÿ’™","light blue heart":"๐Ÿฉต","purple heart":"๐Ÿ’œ","brown heart":"๐ŸคŽ","black heart":"๐Ÿ–ค","grey heart":"๐Ÿฉถ","white heart":"๐Ÿค","kiss mark":"๐Ÿ’‹","hundred points":"๐Ÿ’ฏ","anger symbol":"๐Ÿ’ข","collision":"๐Ÿ’ฅ","dizzy":"๐Ÿ’ซ","sweat droplets":"๐Ÿ’ฆ","dashing away":"๐Ÿ’จ","hole":"๐Ÿ•ณ๏ธ","speech balloon":"๐Ÿ’ฌ","eye in speech bubble":"๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ","left speech bubble":"๐Ÿ—จ๏ธ","right anger bubble":"๐Ÿ—ฏ๏ธ","thought balloon":"๐Ÿ’ญ","ZZZ":"๐Ÿ’ค"},"People & Body":{"waving hand":"๐Ÿ‘‹","raised back of hand":"๐Ÿคš","hand with fingers splayed":"๐Ÿ–๏ธ","raised hand":"โœ‹","vulcan salute":"๐Ÿ––","rightwards hand":"๐Ÿซฑ","leftwards hand":"๐Ÿซฒ","palm down hand":"๐Ÿซณ","palm up hand":"๐Ÿซด","leftwards pushing hand":"๐Ÿซท","rightwards pushing hand":"๐Ÿซธ","OK hand":"๐Ÿ‘Œ","pinched fingers":"๐ŸคŒ","pinching hand":"๐Ÿค","victory hand":"โœŒ๏ธ","crossed fingers":"๐Ÿคž","hand with index finger and thumb crossed":"๐Ÿซฐ","love-you gesture":"๐ŸคŸ","sign of the horns":"๐Ÿค˜","call me hand":"๐Ÿค™","backhand index pointing left":"๐Ÿ‘ˆ","backhand index pointing right":"๐Ÿ‘‰","backhand index pointing up":"๐Ÿ‘†","middle finger":"๐Ÿ–•","backhand index pointing down":"๐Ÿ‘‡","index pointing up":"โ˜๏ธ","index pointing at the viewer":"๐Ÿซต","thumbs up":"๐Ÿ‘","thumbs down":"๐Ÿ‘Ž","raised fist":"โœŠ","oncoming fist":"๐Ÿ‘Š","left-facing fist":"๐Ÿค›","right-facing fist":"๐Ÿคœ","clapping hands":"๐Ÿ‘","raising hands":"๐Ÿ™Œ","heart hands":"๐Ÿซถ","open hands":"๐Ÿ‘","palms up together":"๐Ÿคฒ","handshake":"๐Ÿค","folded hands":"๐Ÿ™","writing hand":"โœ๏ธ","nail polish":"๐Ÿ’…","selfie":"๐Ÿคณ","flexed biceps":"๐Ÿ’ช","mechanical arm":"๐Ÿฆพ","mechanical leg":"๐Ÿฆฟ","leg":"๐Ÿฆต","foot":"๐Ÿฆถ","ear":"๐Ÿ‘‚","ear with hearing aid":"๐Ÿฆป","nose":"๐Ÿ‘ƒ","brain":"๐Ÿง ","anatomical heart":"๐Ÿซ€","lungs":"๐Ÿซ","tooth":"๐Ÿฆท","bone":"๐Ÿฆด","eyes":"๐Ÿ‘€","eye":"๐Ÿ‘๏ธ","tongue":"๐Ÿ‘…","mouth":"๐Ÿ‘„","biting lip":"๐Ÿซฆ","baby":"๐Ÿ‘ถ","child":"๐Ÿง’","boy":"๐Ÿ‘ฆ","girl":"๐Ÿ‘ง","person":"๐Ÿง‘","person blond hair":"๐Ÿ‘ฑ","man":"๐Ÿ‘จ","person beard":"๐Ÿง”","man beard":"๐Ÿง”โ€โ™‚๏ธ","woman beard":"๐Ÿง”โ€โ™€๏ธ","man red hair":"๐Ÿ‘จโ€๐Ÿฆฐ","man curly hair":"๐Ÿ‘จโ€๐Ÿฆฑ","man white hair":"๐Ÿ‘จโ€๐Ÿฆณ","man bald":"๐Ÿ‘จโ€๐Ÿฆฒ","woman":"๐Ÿ‘ฉ","woman red hair":"๐Ÿ‘ฉโ€๐Ÿฆฐ","person red hair":"๐Ÿง‘โ€๐Ÿฆฐ","woman curly hair":"๐Ÿ‘ฉโ€๐Ÿฆฑ","person curly hair":"๐Ÿง‘โ€๐Ÿฆฑ","woman white hair":"๐Ÿ‘ฉโ€๐Ÿฆณ","person white hair":"๐Ÿง‘โ€๐Ÿฆณ","woman bald":"๐Ÿ‘ฉโ€๐Ÿฆฒ","person bald":"๐Ÿง‘โ€๐Ÿฆฒ","woman blond hair":"๐Ÿ‘ฑโ€โ™€๏ธ","man blond hair":"๐Ÿ‘ฑโ€โ™‚๏ธ","older person":"๐Ÿง“","old man":"๐Ÿ‘ด","old woman":"๐Ÿ‘ต","person frowning":"๐Ÿ™","man frowning":"๐Ÿ™โ€โ™‚๏ธ","woman frowning":"๐Ÿ™โ€โ™€๏ธ","person pouting":"๐Ÿ™Ž","man pouting":"๐Ÿ™Žโ€โ™‚๏ธ","woman pouting":"๐Ÿ™Žโ€โ™€๏ธ","person gesturing NO":"๐Ÿ™…","man gesturing NO":"๐Ÿ™…โ€โ™‚๏ธ","woman gesturing NO":"๐Ÿ™…โ€โ™€๏ธ","person gesturing OK":"๐Ÿ™†","man gesturing OK":"๐Ÿ™†โ€โ™‚๏ธ","woman gesturing OK":"๐Ÿ™†โ€โ™€๏ธ","person tipping hand":"๐Ÿ’","man tipping hand":"๐Ÿ’โ€โ™‚๏ธ","woman tipping hand":"๐Ÿ’โ€โ™€๏ธ","person raising hand":"๐Ÿ™‹","man raising hand":"๐Ÿ™‹โ€โ™‚๏ธ","woman raising hand":"๐Ÿ™‹โ€โ™€๏ธ","deaf person":"๐Ÿง","deaf man":"๐Ÿงโ€โ™‚๏ธ","deaf woman":"๐Ÿงโ€โ™€๏ธ","person bowing":"๐Ÿ™‡","man bowing":"๐Ÿ™‡โ€โ™‚๏ธ","woman bowing":"๐Ÿ™‡โ€โ™€๏ธ","person facepalming":"๐Ÿคฆ","man facepalming":"๐Ÿคฆโ€โ™‚๏ธ","woman facepalming":"๐Ÿคฆโ€โ™€๏ธ","person shrugging":"๐Ÿคท","man shrugging":"๐Ÿคทโ€โ™‚๏ธ","woman shrugging":"๐Ÿคทโ€โ™€๏ธ","health worker":"๐Ÿง‘โ€โš•๏ธ","man health worker":"๐Ÿ‘จโ€โš•๏ธ","woman health worker":"๐Ÿ‘ฉโ€โš•๏ธ","student":"๐Ÿง‘โ€๐ŸŽ“","man student":"๐Ÿ‘จโ€๐ŸŽ“","woman student":"๐Ÿ‘ฉโ€๐ŸŽ“","teacher":"๐Ÿง‘โ€๐Ÿซ","man teacher":"๐Ÿ‘จโ€๐Ÿซ","woman teacher":"๐Ÿ‘ฉโ€๐Ÿซ","judge":"๐Ÿง‘โ€โš–๏ธ","man judge":"๐Ÿ‘จโ€โš–๏ธ","woman judge":"๐Ÿ‘ฉโ€โš–๏ธ","farmer":"๐Ÿง‘โ€๐ŸŒพ","man farmer":"๐Ÿ‘จโ€๐ŸŒพ","woman farmer":"๐Ÿ‘ฉโ€๐ŸŒพ","cook":"๐Ÿง‘โ€๐Ÿณ","man cook":"๐Ÿ‘จโ€๐Ÿณ","woman cook":"๐Ÿ‘ฉโ€๐Ÿณ","mechanic":"๐Ÿง‘โ€๐Ÿ”ง","man mechanic":"๐Ÿ‘จโ€๐Ÿ”ง","woman mechanic":"๐Ÿ‘ฉโ€๐Ÿ”ง","factory worker":"๐Ÿง‘โ€๐Ÿญ","man factory worker":"๐Ÿ‘จโ€๐Ÿญ","woman factory worker":"๐Ÿ‘ฉโ€๐Ÿญ","office worker":"๐Ÿง‘โ€๐Ÿ’ผ","man office worker":"๐Ÿ‘จโ€๐Ÿ’ผ","woman office worker":"๐Ÿ‘ฉโ€๐Ÿ’ผ","scientist":"๐Ÿง‘โ€๐Ÿ”ฌ","man scientist":"๐Ÿ‘จโ€๐Ÿ”ฌ","woman scientist":"๐Ÿ‘ฉโ€๐Ÿ”ฌ","technologist":"๐Ÿง‘โ€๐Ÿ’ป","man technologist":"๐Ÿ‘จโ€๐Ÿ’ป","woman technologist":"๐Ÿ‘ฉโ€๐Ÿ’ป","singer":"๐Ÿง‘โ€๐ŸŽค","man singer":"๐Ÿ‘จโ€๐ŸŽค","woman singer":"๐Ÿ‘ฉโ€๐ŸŽค","artist":"๐Ÿง‘โ€๐ŸŽจ","man artist":"๐Ÿ‘จโ€๐ŸŽจ","woman artist":"๐Ÿ‘ฉโ€๐ŸŽจ","pilot":"๐Ÿง‘โ€โœˆ๏ธ","man pilot":"๐Ÿ‘จโ€โœˆ๏ธ","woman pilot":"๐Ÿ‘ฉโ€โœˆ๏ธ","astronaut":"๐Ÿง‘โ€๐Ÿš€","man astronaut":"๐Ÿ‘จโ€๐Ÿš€","woman astronaut":"๐Ÿ‘ฉโ€๐Ÿš€","firefighter":"๐Ÿง‘โ€๐Ÿš’","man firefighter":"๐Ÿ‘จโ€๐Ÿš’","woman firefighter":"๐Ÿ‘ฉโ€๐Ÿš’","police officer":"๐Ÿ‘ฎ","man police officer":"๐Ÿ‘ฎโ€โ™‚๏ธ","woman police officer":"๐Ÿ‘ฎโ€โ™€๏ธ","detective":"๐Ÿ•ต๏ธ","man detective":"๐Ÿ•ต๏ธโ€โ™‚๏ธ","woman detective":"๐Ÿ•ต๏ธโ€โ™€๏ธ","guard":"๐Ÿ’‚","man guard":"๐Ÿ’‚โ€โ™‚๏ธ","woman guard":"๐Ÿ’‚โ€โ™€๏ธ","ninja":"๐Ÿฅท","construction worker":"๐Ÿ‘ท","man construction worker":"๐Ÿ‘ทโ€โ™‚๏ธ","woman construction worker":"๐Ÿ‘ทโ€โ™€๏ธ","person with crown":"๐Ÿซ…","prince":"๐Ÿคด","princess":"๐Ÿ‘ธ","person wearing turban":"๐Ÿ‘ณ","man wearing turban":"๐Ÿ‘ณโ€โ™‚๏ธ","woman wearing turban":"๐Ÿ‘ณโ€โ™€๏ธ","person with skullcap":"๐Ÿ‘ฒ","woman with headscarf":"๐Ÿง•","person in tuxedo":"๐Ÿคต","man in tuxedo":"๐Ÿคตโ€โ™‚๏ธ","woman in tuxedo":"๐Ÿคตโ€โ™€๏ธ","person with veil":"๐Ÿ‘ฐ","man with veil":"๐Ÿ‘ฐโ€โ™‚๏ธ","woman with veil":"๐Ÿ‘ฐโ€โ™€๏ธ","pregnant woman":"๐Ÿคฐ","pregnant man":"๐Ÿซƒ","pregnant person":"๐Ÿซ„","breast-feeding":"๐Ÿคฑ","woman feeding baby":"๐Ÿ‘ฉโ€๐Ÿผ","man feeding baby":"๐Ÿ‘จโ€๐Ÿผ","person feeding baby":"๐Ÿง‘โ€๐Ÿผ","baby angel":"๐Ÿ‘ผ","Santa Claus":"๐ŸŽ…","Mrs. Claus":"๐Ÿคถ","mx claus":"๐Ÿง‘โ€๐ŸŽ„","superhero":"๐Ÿฆธ","man superhero":"๐Ÿฆธโ€โ™‚๏ธ","woman superhero":"๐Ÿฆธโ€โ™€๏ธ","supervillain":"๐Ÿฆน","man supervillain":"๐Ÿฆนโ€โ™‚๏ธ","woman supervillain":"๐Ÿฆนโ€โ™€๏ธ","mage":"๐Ÿง™","man mage":"๐Ÿง™โ€โ™‚๏ธ","woman mage":"๐Ÿง™โ€โ™€๏ธ","fairy":"๐Ÿงš","man fairy":"๐Ÿงšโ€โ™‚๏ธ","woman fairy":"๐Ÿงšโ€โ™€๏ธ","vampire":"๐Ÿง›","man vampire":"๐Ÿง›โ€โ™‚๏ธ","woman vampire":"๐Ÿง›โ€โ™€๏ธ","merperson":"๐Ÿงœ","merman":"๐Ÿงœโ€โ™‚๏ธ","mermaid":"๐Ÿงœโ€โ™€๏ธ","elf":"๐Ÿง","man elf":"๐Ÿงโ€โ™‚๏ธ","woman elf":"๐Ÿงโ€โ™€๏ธ","genie":"๐Ÿงž","man genie":"๐Ÿงžโ€โ™‚๏ธ","woman genie":"๐Ÿงžโ€โ™€๏ธ","zombie":"๐ŸงŸ","man zombie":"๐ŸงŸโ€โ™‚๏ธ","woman zombie":"๐ŸงŸโ€โ™€๏ธ","troll":"๐ŸงŒ","person getting massage":"๐Ÿ’†","man getting massage":"๐Ÿ’†โ€โ™‚๏ธ","woman getting massage":"๐Ÿ’†โ€โ™€๏ธ","person getting haircut":"๐Ÿ’‡","man getting haircut":"๐Ÿ’‡โ€โ™‚๏ธ","woman getting haircut":"๐Ÿ’‡โ€โ™€๏ธ","person walking":"๐Ÿšถ","man walking":"๐Ÿšถโ€โ™‚๏ธ","woman walking":"๐Ÿšถโ€โ™€๏ธ","person standing":"๐Ÿง","man standing":"๐Ÿงโ€โ™‚๏ธ","woman standing":"๐Ÿงโ€โ™€๏ธ","person kneeling":"๐ŸงŽ","man kneeling":"๐ŸงŽโ€โ™‚๏ธ","woman kneeling":"๐ŸงŽโ€โ™€๏ธ","person with white cane":"๐Ÿง‘โ€๐Ÿฆฏ","man with white cane":"๐Ÿ‘จโ€๐Ÿฆฏ","woman with white cane":"๐Ÿ‘ฉโ€๐Ÿฆฏ","person in motorized wheelchair":"๐Ÿง‘โ€๐Ÿฆผ","man in motorized wheelchair":"๐Ÿ‘จโ€๐Ÿฆผ","woman in motorized wheelchair":"๐Ÿ‘ฉโ€๐Ÿฆผ","person in manual wheelchair":"๐Ÿง‘โ€๐Ÿฆฝ","man in manual wheelchair":"๐Ÿ‘จโ€๐Ÿฆฝ","woman in manual wheelchair":"๐Ÿ‘ฉโ€๐Ÿฆฝ","person running":"๐Ÿƒ","man running":"๐Ÿƒโ€โ™‚๏ธ","woman running":"๐Ÿƒโ€โ™€๏ธ","woman dancing":"๐Ÿ’ƒ","man dancing":"๐Ÿ•บ","person in suit levitating":"๐Ÿ•ด๏ธ","people with bunny ears":"๐Ÿ‘ฏ","men with bunny ears":"๐Ÿ‘ฏโ€โ™‚๏ธ","women with bunny ears":"๐Ÿ‘ฏโ€โ™€๏ธ","person in steamy room":"๐Ÿง–","man in steamy room":"๐Ÿง–โ€โ™‚๏ธ","woman in steamy room":"๐Ÿง–โ€โ™€๏ธ","person climbing":"๐Ÿง—","man climbing":"๐Ÿง—โ€โ™‚๏ธ","woman climbing":"๐Ÿง—โ€โ™€๏ธ","person fencing":"๐Ÿคบ","horse racing":"๐Ÿ‡","skier":"โ›ท๏ธ","snowboarder":"๐Ÿ‚","person golfing":"๐ŸŒ๏ธ","man golfing":"๐ŸŒ๏ธโ€โ™‚๏ธ","woman golfing":"๐ŸŒ๏ธโ€โ™€๏ธ","person surfing":"๐Ÿ„","man surfing":"๐Ÿ„โ€โ™‚๏ธ","woman surfing":"๐Ÿ„โ€โ™€๏ธ","person rowing boat":"๐Ÿšฃ","man rowing boat":"๐Ÿšฃโ€โ™‚๏ธ","woman rowing boat":"๐Ÿšฃโ€โ™€๏ธ","person swimming":"๐ŸŠ","man swimming":"๐ŸŠโ€โ™‚๏ธ","woman swimming":"๐ŸŠโ€โ™€๏ธ","person bouncing ball":"โ›น๏ธ","man bouncing ball":"โ›น๏ธโ€โ™‚๏ธ","woman bouncing ball":"โ›น๏ธโ€โ™€๏ธ","person lifting weights":"๐Ÿ‹๏ธ","man lifting weights":"๐Ÿ‹๏ธโ€โ™‚๏ธ","woman lifting weights":"๐Ÿ‹๏ธโ€โ™€๏ธ","person biking":"๐Ÿšด","man biking":"๐Ÿšดโ€โ™‚๏ธ","woman biking":"๐Ÿšดโ€โ™€๏ธ","person mountain biking":"๐Ÿšต","man mountain biking":"๐Ÿšตโ€โ™‚๏ธ","woman mountain biking":"๐Ÿšตโ€โ™€๏ธ","person cartwheeling":"๐Ÿคธ","man cartwheeling":"๐Ÿคธโ€โ™‚๏ธ","woman cartwheeling":"๐Ÿคธโ€โ™€๏ธ","people wrestling":"๐Ÿคผ","men wrestling":"๐Ÿคผโ€โ™‚๏ธ","women wrestling":"๐Ÿคผโ€โ™€๏ธ","person playing water polo":"๐Ÿคฝ","man playing water polo":"๐Ÿคฝโ€โ™‚๏ธ","woman playing water polo":"๐Ÿคฝโ€โ™€๏ธ","person playing handball":"๐Ÿคพ","man playing handball":"๐Ÿคพโ€โ™‚๏ธ","woman playing handball":"๐Ÿคพโ€โ™€๏ธ","person juggling":"๐Ÿคน","man juggling":"๐Ÿคนโ€โ™‚๏ธ","woman juggling":"๐Ÿคนโ€โ™€๏ธ","person in lotus position":"๐Ÿง˜","man in lotus position":"๐Ÿง˜โ€โ™‚๏ธ","woman in lotus position":"๐Ÿง˜โ€โ™€๏ธ","person taking bath":"๐Ÿ›€","person in bed":"๐Ÿ›Œ","people holding hands":"๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘","women holding hands":"๐Ÿ‘ญ","woman and man holding hands":"๐Ÿ‘ซ","men holding hands":"๐Ÿ‘ฌ","kiss":"๐Ÿ’","kiss woman, man":"๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ","kiss man, man":"๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ","kiss woman, woman":"๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ","couple with heart":"๐Ÿ’‘","couple with heart woman, man":"๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ","couple with heart man, man":"๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ","couple with heart woman, woman":"๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ","family":"๐Ÿ‘ช","family man, woman, boy":"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ","family man, woman, girl":"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง","family man, woman, girl, boy":"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ","family man, woman, boy, boy":"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ","family man, woman, girl, girl":"๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง","family man, man, boy":"๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ","family man, man, girl":"๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง","family man, man, girl, boy":"๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ","family man, man, boy, boy":"๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ","family man, man, girl, girl":"๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง","family woman, woman, boy":"๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ","family woman, woman, girl":"๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง","family woman, woman, girl, boy":"๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ","family woman, woman, boy, boy":"๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ","family woman, woman, girl, girl":"๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง","family man, boy":"๐Ÿ‘จโ€๐Ÿ‘ฆ","family man, boy, boy":"๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ","family man, girl":"๐Ÿ‘จโ€๐Ÿ‘ง","family man, girl, boy":"๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ","family man, girl, girl":"๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง","family woman, boy":"๐Ÿ‘ฉโ€๐Ÿ‘ฆ","family woman, boy, boy":"๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ","family woman, girl":"๐Ÿ‘ฉโ€๐Ÿ‘ง","family woman, girl, boy":"๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ","family woman, girl, girl":"๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง","speaking head":"๐Ÿ—ฃ๏ธ","bust in silhouette":"๐Ÿ‘ค","busts in silhouette":"๐Ÿ‘ฅ","people hugging":"๐Ÿซ‚","footprints":"๐Ÿ‘ฃ"},"Animals & Nature":{"monkey face":"๐Ÿต","monkey":"๐Ÿ’","gorilla":"๐Ÿฆ","orangutan":"๐Ÿฆง","dog face":"๐Ÿถ","dog":"๐Ÿ•","guide dog":"๐Ÿฆฎ","service dog":"๐Ÿ•โ€๐Ÿฆบ","poodle":"๐Ÿฉ","wolf":"๐Ÿบ","fox":"๐ŸฆŠ","raccoon":"๐Ÿฆ","cat face":"๐Ÿฑ","cat":"๐Ÿˆ","black cat":"๐Ÿˆโ€โฌ›","lion":"๐Ÿฆ","tiger face":"๐Ÿฏ","tiger":"๐Ÿ…","leopard":"๐Ÿ†","horse face":"๐Ÿด","moose":"๐ŸซŽ","donkey":"๐Ÿซ","horse":"๐ŸŽ","unicorn":"๐Ÿฆ„","zebra":"๐Ÿฆ“","deer":"๐ŸฆŒ","bison":"๐Ÿฆฌ","cow face":"๐Ÿฎ","ox":"๐Ÿ‚","water buffalo":"๐Ÿƒ","cow":"๐Ÿ„","pig face":"๐Ÿท","pig":"๐Ÿ–","boar":"๐Ÿ—","pig nose":"๐Ÿฝ","ram":"๐Ÿ","ewe":"๐Ÿ‘","goat":"๐Ÿ","camel":"๐Ÿช","two-hump camel":"๐Ÿซ","llama":"๐Ÿฆ™","giraffe":"๐Ÿฆ’","elephant":"๐Ÿ˜","mammoth":"๐Ÿฆฃ","rhinoceros":"๐Ÿฆ","hippopotamus":"๐Ÿฆ›","mouse face":"๐Ÿญ","mouse":"๐Ÿ","rat":"๐Ÿ€","hamster":"๐Ÿน","rabbit face":"๐Ÿฐ","rabbit":"๐Ÿ‡","chipmunk":"๐Ÿฟ๏ธ","beaver":"๐Ÿฆซ","hedgehog":"๐Ÿฆ”","bat":"๐Ÿฆ‡","bear":"๐Ÿป","polar bear":"๐Ÿปโ€โ„๏ธ","koala":"๐Ÿจ","panda":"๐Ÿผ","sloth":"๐Ÿฆฅ","otter":"๐Ÿฆฆ","skunk":"๐Ÿฆจ","kangaroo":"๐Ÿฆ˜","badger":"๐Ÿฆก","paw prints":"๐Ÿพ","turkey":"๐Ÿฆƒ","chicken":"๐Ÿ”","rooster":"๐Ÿ“","hatching chick":"๐Ÿฃ","baby chick":"๐Ÿค","front-facing baby chick":"๐Ÿฅ","bird":"๐Ÿฆ","penguin":"๐Ÿง","dove":"๐Ÿ•Š๏ธ","eagle":"๐Ÿฆ…","duck":"๐Ÿฆ†","swan":"๐Ÿฆข","owl":"๐Ÿฆ‰","dodo":"๐Ÿฆค","feather":"๐Ÿชถ","flamingo":"๐Ÿฆฉ","peacock":"๐Ÿฆš","parrot":"๐Ÿฆœ","wing":"๐Ÿชฝ","black bird":"๐Ÿฆโ€โฌ›","goose":"๐Ÿชฟ","frog":"๐Ÿธ","crocodile":"๐ŸŠ","turtle":"๐Ÿข","lizard":"๐ŸฆŽ","snake":"๐Ÿ","dragon face":"๐Ÿฒ","dragon":"๐Ÿ‰","sauropod":"๐Ÿฆ•","T-Rex":"๐Ÿฆ–","spouting whale":"๐Ÿณ","whale":"๐Ÿ‹","dolphin":"๐Ÿฌ","seal":"๐Ÿฆญ","fish":"๐ŸŸ","tropical fish":"๐Ÿ ","blowfish":"๐Ÿก","shark":"๐Ÿฆˆ","octopus":"๐Ÿ™","spiral shell":"๐Ÿš","coral":"๐Ÿชธ","jellyfish":"๐Ÿชผ","snail":"๐ŸŒ","butterfly":"๐Ÿฆ‹","bug":"๐Ÿ›","ant":"๐Ÿœ","honeybee":"๐Ÿ","beetle":"๐Ÿชฒ","lady beetle":"๐Ÿž","cricket":"๐Ÿฆ—","cockroach":"๐Ÿชณ","spider":"๐Ÿ•ท๏ธ","spider web":"๐Ÿ•ธ๏ธ","scorpion":"๐Ÿฆ‚","mosquito":"๐ŸฆŸ","fly":"๐Ÿชฐ","worm":"๐Ÿชฑ","microbe":"๐Ÿฆ ","bouquet":"๐Ÿ’","cherry blossom":"๐ŸŒธ","white flower":"๐Ÿ’ฎ","lotus":"๐Ÿชท","rosette":"๐Ÿต๏ธ","rose":"๐ŸŒน","wilted flower":"๐Ÿฅ€","hibiscus":"๐ŸŒบ","sunflower":"๐ŸŒป","blossom":"๐ŸŒผ","tulip":"๐ŸŒท","hyacinth":"๐Ÿชป","seedling":"๐ŸŒฑ","potted plant":"๐Ÿชด","evergreen tree":"๐ŸŒฒ","deciduous tree":"๐ŸŒณ","palm tree":"๐ŸŒด","cactus":"๐ŸŒต","sheaf of rice":"๐ŸŒพ","herb":"๐ŸŒฟ","shamrock":"โ˜˜๏ธ","four leaf clover":"๐Ÿ€","maple leaf":"๐Ÿ","fallen leaf":"๐Ÿ‚","leaf fluttering in wind":"๐Ÿƒ","empty nest":"๐Ÿชน","nest with eggs":"๐Ÿชบ","mushroom":"๐Ÿ„"},"Food & Drink":{"grapes":"๐Ÿ‡","melon":"๐Ÿˆ","watermelon":"๐Ÿ‰","tangerine":"๐ŸŠ","lemon":"๐Ÿ‹","banana":"๐ŸŒ","pineapple":"๐Ÿ","mango":"๐Ÿฅญ","red apple":"๐ŸŽ","green apple":"๐Ÿ","pear":"๐Ÿ","peach":"๐Ÿ‘","cherries":"๐Ÿ’","strawberry":"๐Ÿ“","blueberries":"๐Ÿซ","kiwi fruit":"๐Ÿฅ","tomato":"๐Ÿ…","olive":"๐Ÿซ’","coconut":"๐Ÿฅฅ","avocado":"๐Ÿฅ‘","eggplant":"๐Ÿ†","potato":"๐Ÿฅ”","carrot":"๐Ÿฅ•","ear of corn":"๐ŸŒฝ","hot pepper":"๐ŸŒถ๏ธ","bell pepper":"๐Ÿซ‘","cucumber":"๐Ÿฅ’","leafy green":"๐Ÿฅฌ","broccoli":"๐Ÿฅฆ","garlic":"๐Ÿง„","onion":"๐Ÿง…","peanuts":"๐Ÿฅœ","beans":"๐Ÿซ˜","chestnut":"๐ŸŒฐ","ginger root":"๐Ÿซš","pea pod":"๐Ÿซ›","bread":"๐Ÿž","croissant":"๐Ÿฅ","baguette bread":"๐Ÿฅ–","flatbread":"๐Ÿซ“","pretzel":"๐Ÿฅจ","bagel":"๐Ÿฅฏ","pancakes":"๐Ÿฅž","waffle":"๐Ÿง‡","cheese wedge":"๐Ÿง€","meat on bone":"๐Ÿ–","poultry leg":"๐Ÿ—","cut of meat":"๐Ÿฅฉ","bacon":"๐Ÿฅ“","hamburger":"๐Ÿ”","french fries":"๐ŸŸ","pizza":"๐Ÿ•","hot dog":"๐ŸŒญ","sandwich":"๐Ÿฅช","taco":"๐ŸŒฎ","burrito":"๐ŸŒฏ","tamale":"๐Ÿซ”","stuffed flatbread":"๐Ÿฅ™","falafel":"๐Ÿง†","egg":"๐Ÿฅš","cooking":"๐Ÿณ","shallow pan of food":"๐Ÿฅ˜","pot of food":"๐Ÿฒ","fondue":"๐Ÿซ•","bowl with spoon":"๐Ÿฅฃ","green salad":"๐Ÿฅ—","popcorn":"๐Ÿฟ","butter":"๐Ÿงˆ","salt":"๐Ÿง‚","canned food":"๐Ÿฅซ","bento box":"๐Ÿฑ","rice cracker":"๐Ÿ˜","rice ball":"๐Ÿ™","cooked rice":"๐Ÿš","curry rice":"๐Ÿ›","steaming bowl":"๐Ÿœ","spaghetti":"๐Ÿ","roasted sweet potato":"๐Ÿ ","oden":"๐Ÿข","sushi":"๐Ÿฃ","fried shrimp":"๐Ÿค","fish cake with swirl":"๐Ÿฅ","moon cake":"๐Ÿฅฎ","dango":"๐Ÿก","dumpling":"๐ŸฅŸ","fortune cookie":"๐Ÿฅ ","takeout box":"๐Ÿฅก","crab":"๐Ÿฆ€","lobster":"๐Ÿฆž","shrimp":"๐Ÿฆ","squid":"๐Ÿฆ‘","oyster":"๐Ÿฆช","soft ice cream":"๐Ÿฆ","shaved ice":"๐Ÿง","ice cream":"๐Ÿจ","doughnut":"๐Ÿฉ","cookie":"๐Ÿช","birthday cake":"๐ŸŽ‚","shortcake":"๐Ÿฐ","cupcake":"๐Ÿง","pie":"๐Ÿฅง","chocolate bar":"๐Ÿซ","candy":"๐Ÿฌ","lollipop":"๐Ÿญ","custard":"๐Ÿฎ","honey pot":"๐Ÿฏ","baby bottle":"๐Ÿผ","glass of milk":"๐Ÿฅ›","hot beverage":"โ˜•","teapot":"๐Ÿซ–","teacup without handle":"๐Ÿต","sake":"๐Ÿถ","bottle with popping cork":"๐Ÿพ","wine glass":"๐Ÿท","cocktail glass":"๐Ÿธ","tropical drink":"๐Ÿน","beer mug":"๐Ÿบ","clinking beer mugs":"๐Ÿป","clinking glasses":"๐Ÿฅ‚","tumbler glass":"๐Ÿฅƒ","pouring liquid":"๐Ÿซ—","cup with straw":"๐Ÿฅค","bubble tea":"๐Ÿง‹","beverage box":"๐Ÿงƒ","mate":"๐Ÿง‰","ice":"๐ŸงŠ","chopsticks":"๐Ÿฅข","fork and knife with plate":"๐Ÿฝ๏ธ","fork and knife":"๐Ÿด","spoon":"๐Ÿฅ„","kitchen knife":"๐Ÿ”ช","jar":"๐Ÿซ™","amphora":"๐Ÿบ"},"Travel & Places":{"globe showing Europe-Africa":"๐ŸŒ","globe showing Americas":"๐ŸŒŽ","globe showing Asia-Australia":"๐ŸŒ","globe with meridians":"๐ŸŒ","world map":"๐Ÿ—บ๏ธ","map of Japan":"๐Ÿ—พ","compass":"๐Ÿงญ","snow-capped mountain":"๐Ÿ”๏ธ","mountain":"โ›ฐ๏ธ","volcano":"๐ŸŒ‹","mount fuji":"๐Ÿ—ป","camping":"๐Ÿ•๏ธ","beach with umbrella":"๐Ÿ–๏ธ","desert":"๐Ÿœ๏ธ","desert island":"๐Ÿ๏ธ","national park":"๐Ÿž๏ธ","stadium":"๐ŸŸ๏ธ","classical building":"๐Ÿ›๏ธ","building construction":"๐Ÿ—๏ธ","brick":"๐Ÿงฑ","rock":"๐Ÿชจ","wood":"๐Ÿชต","hut":"๐Ÿ›–","houses":"๐Ÿ˜๏ธ","derelict house":"๐Ÿš๏ธ","house":"๐Ÿ ","house with garden":"๐Ÿก","office building":"๐Ÿข","Japanese post office":"๐Ÿฃ","post office":"๐Ÿค","hospital":"๐Ÿฅ","bank":"๐Ÿฆ","hotel":"๐Ÿจ","love hotel":"๐Ÿฉ","convenience store":"๐Ÿช","school":"๐Ÿซ","department store":"๐Ÿฌ","factory":"๐Ÿญ","Japanese castle":"๐Ÿฏ","castle":"๐Ÿฐ","wedding":"๐Ÿ’’","Tokyo tower":"๐Ÿ—ผ","Statue of Liberty":"๐Ÿ—ฝ","church":"โ›ช","mosque":"๐Ÿ•Œ","hindu temple":"๐Ÿ›•","synagogue":"๐Ÿ•","shinto shrine":"โ›ฉ๏ธ","kaaba":"๐Ÿ•‹","fountain":"โ›ฒ","tent":"โ›บ","foggy":"๐ŸŒ","night with stars":"๐ŸŒƒ","cityscape":"๐Ÿ™๏ธ","sunrise over mountains":"๐ŸŒ„","sunrise":"๐ŸŒ…","cityscape at dusk":"๐ŸŒ†","sunset":"๐ŸŒ‡","bridge at night":"๐ŸŒ‰","hot springs":"โ™จ๏ธ","carousel horse":"๐ŸŽ ","playground slide":"๐Ÿ›","ferris wheel":"๐ŸŽก","roller coaster":"๐ŸŽข","barber pole":"๐Ÿ’ˆ","circus tent":"๐ŸŽช","locomotive":"๐Ÿš‚","railway car":"๐Ÿšƒ","high-speed train":"๐Ÿš„","bullet train":"๐Ÿš…","train":"๐Ÿš†","metro":"๐Ÿš‡","light rail":"๐Ÿšˆ","station":"๐Ÿš‰","tram":"๐ŸšŠ","monorail":"๐Ÿš","mountain railway":"๐Ÿšž","tram car":"๐Ÿš‹","bus":"๐ŸšŒ","oncoming bus":"๐Ÿš","trolleybus":"๐ŸšŽ","minibus":"๐Ÿš","ambulance":"๐Ÿš‘","fire engine":"๐Ÿš’","police car":"๐Ÿš“","oncoming police car":"๐Ÿš”","taxi":"๐Ÿš•","oncoming taxi":"๐Ÿš–","automobile":"๐Ÿš—","oncoming automobile":"๐Ÿš˜","sport utility vehicle":"๐Ÿš™","pickup truck":"๐Ÿ›ป","delivery truck":"๐Ÿšš","articulated lorry":"๐Ÿš›","tractor":"๐Ÿšœ","racing car":"๐ŸŽ๏ธ","motorcycle":"๐Ÿ๏ธ","motor scooter":"๐Ÿ›ต","manual wheelchair":"๐Ÿฆฝ","motorized wheelchair":"๐Ÿฆผ","auto rickshaw":"๐Ÿ›บ","bicycle":"๐Ÿšฒ","kick scooter":"๐Ÿ›ด","skateboard":"๐Ÿ›น","roller skate":"๐Ÿ›ผ","bus stop":"๐Ÿš","motorway":"๐Ÿ›ฃ๏ธ","railway track":"๐Ÿ›ค๏ธ","oil drum":"๐Ÿ›ข๏ธ","fuel pump":"โ›ฝ","wheel":"๐Ÿ›ž","police car light":"๐Ÿšจ","horizontal traffic light":"๐Ÿšฅ","vertical traffic light":"๐Ÿšฆ","stop sign":"๐Ÿ›‘","construction":"๐Ÿšง","anchor":"โš“","ring buoy":"๐Ÿ›Ÿ","sailboat":"โ›ต","canoe":"๐Ÿ›ถ","speedboat":"๐Ÿšค","passenger ship":"๐Ÿ›ณ๏ธ","ferry":"โ›ด๏ธ","motor boat":"๐Ÿ›ฅ๏ธ","ship":"๐Ÿšข","airplane":"โœˆ๏ธ","small airplane":"๐Ÿ›ฉ๏ธ","airplane departure":"๐Ÿ›ซ","airplane arrival":"๐Ÿ›ฌ","parachute":"๐Ÿช‚","seat":"๐Ÿ’บ","helicopter":"๐Ÿš","suspension railway":"๐ŸšŸ","mountain cableway":"๐Ÿš ","aerial tramway":"๐Ÿšก","satellite":"๐Ÿ›ฐ๏ธ","rocket":"๐Ÿš€","flying saucer":"๐Ÿ›ธ","bellhop bell":"๐Ÿ›Ž๏ธ","luggage":"๐Ÿงณ","hourglass done":"โŒ›","hourglass not done":"โณ","watch":"โŒš","alarm clock":"โฐ","stopwatch":"โฑ๏ธ","timer clock":"โฒ๏ธ","mantelpiece clock":"๐Ÿ•ฐ๏ธ","twelve oโ€™clock":"๐Ÿ•›","twelve-thirty":"๐Ÿ•ง","one oโ€™clock":"๐Ÿ•","one-thirty":"๐Ÿ•œ","two oโ€™clock":"๐Ÿ•‘","two-thirty":"๐Ÿ•","three oโ€™clock":"๐Ÿ•’","three-thirty":"๐Ÿ•ž","four oโ€™clock":"๐Ÿ•“","four-thirty":"๐Ÿ•Ÿ","five oโ€™clock":"๐Ÿ•”","five-thirty":"๐Ÿ• ","six oโ€™clock":"๐Ÿ••","six-thirty":"๐Ÿ•ก","seven oโ€™clock":"๐Ÿ•–","seven-thirty":"๐Ÿ•ข","eight oโ€™clock":"๐Ÿ•—","eight-thirty":"๐Ÿ•ฃ","nine oโ€™clock":"๐Ÿ•˜","nine-thirty":"๐Ÿ•ค","ten oโ€™clock":"๐Ÿ•™","ten-thirty":"๐Ÿ•ฅ","eleven oโ€™clock":"๐Ÿ•š","eleven-thirty":"๐Ÿ•ฆ","new moon":"๐ŸŒ‘","waxing crescent moon":"๐ŸŒ’","first quarter moon":"๐ŸŒ“","waxing gibbous moon":"๐ŸŒ”","full moon":"๐ŸŒ•","waning gibbous moon":"๐ŸŒ–","last quarter moon":"๐ŸŒ—","waning crescent moon":"๐ŸŒ˜","crescent moon":"๐ŸŒ™","new moon face":"๐ŸŒš","first quarter moon face":"๐ŸŒ›","last quarter moon face":"๐ŸŒœ","thermometer":"๐ŸŒก๏ธ","sun":"โ˜€๏ธ","full moon face":"๐ŸŒ","sun with face":"๐ŸŒž","ringed planet":"๐Ÿช","star":"โญ","glowing star":"๐ŸŒŸ","shooting star":"๐ŸŒ ","milky way":"๐ŸŒŒ","cloud":"โ˜๏ธ","sun behind cloud":"โ›…","cloud with lightning and rain":"โ›ˆ๏ธ","sun behind small cloud":"๐ŸŒค๏ธ","sun behind large cloud":"๐ŸŒฅ๏ธ","sun behind rain cloud":"๐ŸŒฆ๏ธ","cloud with rain":"๐ŸŒง๏ธ","cloud with snow":"๐ŸŒจ๏ธ","cloud with lightning":"๐ŸŒฉ๏ธ","tornado":"๐ŸŒช๏ธ","fog":"๐ŸŒซ๏ธ","wind face":"๐ŸŒฌ๏ธ","cyclone":"๐ŸŒ€","rainbow":"๐ŸŒˆ","closed umbrella":"๐ŸŒ‚","umbrella":"โ˜‚๏ธ","umbrella with rain drops":"โ˜”","umbrella on ground":"โ›ฑ๏ธ","high voltage":"โšก","snowflake":"โ„๏ธ","snowman":"โ˜ƒ๏ธ","snowman without snow":"โ›„","comet":"โ˜„๏ธ","fire":"๐Ÿ”ฅ","droplet":"๐Ÿ’ง","water wave":"๐ŸŒŠ"},"Activities":{"jack-o-lantern":"๐ŸŽƒ","Christmas tree":"๐ŸŽ„","fireworks":"๐ŸŽ†","sparkler":"๐ŸŽ‡","firecracker":"๐Ÿงจ","sparkles":"โœจ","balloon":"๐ŸŽˆ","party popper":"๐ŸŽ‰","confetti ball":"๐ŸŽŠ","tanabata tree":"๐ŸŽ‹","pine decoration":"๐ŸŽ","Japanese dolls":"๐ŸŽŽ","carp streamer":"๐ŸŽ","wind chime":"๐ŸŽ","moon viewing ceremony":"๐ŸŽ‘","red envelope":"๐Ÿงง","ribbon":"๐ŸŽ€","wrapped gift":"๐ŸŽ","reminder ribbon":"๐ŸŽ—๏ธ","admission tickets":"๐ŸŽŸ๏ธ","ticket":"๐ŸŽซ","military medal":"๐ŸŽ–๏ธ","trophy":"๐Ÿ†","sports medal":"๐Ÿ…","1st place medal":"๐Ÿฅ‡","2nd place medal":"๐Ÿฅˆ","3rd place medal":"๐Ÿฅ‰","soccer ball":"โšฝ","baseball":"โšพ","softball":"๐ŸฅŽ","basketball":"๐Ÿ€","volleyball":"๐Ÿ","american football":"๐Ÿˆ","rugby football":"๐Ÿ‰","tennis":"๐ŸŽพ","flying disc":"๐Ÿฅ","bowling":"๐ŸŽณ","cricket game":"๐Ÿ","field hockey":"๐Ÿ‘","ice hockey":"๐Ÿ’","lacrosse":"๐Ÿฅ","ping pong":"๐Ÿ“","badminton":"๐Ÿธ","boxing glove":"๐ŸฅŠ","martial arts uniform":"๐Ÿฅ‹","goal net":"๐Ÿฅ…","flag in hole":"โ›ณ","ice skate":"โ›ธ๏ธ","fishing pole":"๐ŸŽฃ","diving mask":"๐Ÿคฟ","running shirt":"๐ŸŽฝ","skis":"๐ŸŽฟ","sled":"๐Ÿ›ท","curling stone":"๐ŸฅŒ","bullseye":"๐ŸŽฏ","yo-yo":"๐Ÿช€","kite":"๐Ÿช","water pistol":"๐Ÿ”ซ","pool 8 ball":"๐ŸŽฑ","crystal ball":"๐Ÿ”ฎ","magic wand":"๐Ÿช„","video game":"๐ŸŽฎ","joystick":"๐Ÿ•น๏ธ","slot machine":"๐ŸŽฐ","game die":"๐ŸŽฒ","puzzle piece":"๐Ÿงฉ","teddy bear":"๐Ÿงธ","piรฑata":"๐Ÿช…","mirror ball":"๐Ÿชฉ","nesting dolls":"๐Ÿช†","spade suit":"โ™ ๏ธ","heart suit":"โ™ฅ๏ธ","diamond suit":"โ™ฆ๏ธ","club suit":"โ™ฃ๏ธ","chess pawn":"โ™Ÿ๏ธ","joker":"๐Ÿƒ","mahjong red dragon":"๐Ÿ€„","flower playing cards":"๐ŸŽด","performing arts":"๐ŸŽญ","framed picture":"๐Ÿ–ผ๏ธ","artist palette":"๐ŸŽจ","thread":"๐Ÿงต","sewing needle":"๐Ÿชก","yarn":"๐Ÿงถ","knot":"๐Ÿชข"},"Objects":{"glasses":"๐Ÿ‘“","sunglasses":"๐Ÿ•ถ๏ธ","goggles":"๐Ÿฅฝ","lab coat":"๐Ÿฅผ","safety vest":"๐Ÿฆบ","necktie":"๐Ÿ‘”","t-shirt":"๐Ÿ‘•","jeans":"๐Ÿ‘–","scarf":"๐Ÿงฃ","gloves":"๐Ÿงค","coat":"๐Ÿงฅ","socks":"๐Ÿงฆ","dress":"๐Ÿ‘—","kimono":"๐Ÿ‘˜","sari":"๐Ÿฅป","one-piece swimsuit":"๐Ÿฉฑ","briefs":"๐Ÿฉฒ","shorts":"๐Ÿฉณ","bikini":"๐Ÿ‘™","womanโ€™s clothes":"๐Ÿ‘š","folding hand fan":"๐Ÿชญ","purse":"๐Ÿ‘›","handbag":"๐Ÿ‘œ","clutch bag":"๐Ÿ‘","shopping bags":"๐Ÿ›๏ธ","backpack":"๐ŸŽ’","thong sandal":"๐Ÿฉด","manโ€™s shoe":"๐Ÿ‘ž","running shoe":"๐Ÿ‘Ÿ","hiking boot":"๐Ÿฅพ","flat shoe":"๐Ÿฅฟ","high-heeled shoe":"๐Ÿ‘ ","womanโ€™s sandal":"๐Ÿ‘ก","ballet shoes":"๐Ÿฉฐ","womanโ€™s boot":"๐Ÿ‘ข","hair pick":"๐Ÿชฎ","crown":"๐Ÿ‘‘","womanโ€™s hat":"๐Ÿ‘’","top hat":"๐ŸŽฉ","graduation cap":"๐ŸŽ“","billed cap":"๐Ÿงข","military helmet":"๐Ÿช–","rescue workerโ€™s helmet":"โ›‘๏ธ","prayer beads":"๐Ÿ“ฟ","lipstick":"๐Ÿ’„","ring":"๐Ÿ’","gem stone":"๐Ÿ’Ž","muted speaker":"๐Ÿ”‡","speaker low volume":"๐Ÿ”ˆ","speaker medium volume":"๐Ÿ”‰","speaker high volume":"๐Ÿ”Š","loudspeaker":"๐Ÿ“ข","megaphone":"๐Ÿ“ฃ","postal horn":"๐Ÿ“ฏ","bell":"๐Ÿ””","bell with slash":"๐Ÿ”•","musical score":"๐ŸŽผ","musical note":"๐ŸŽต","musical notes":"๐ŸŽถ","studio microphone":"๐ŸŽ™๏ธ","level slider":"๐ŸŽš๏ธ","control knobs":"๐ŸŽ›๏ธ","microphone":"๐ŸŽค","headphone":"๐ŸŽง","radio":"๐Ÿ“ป","saxophone":"๐ŸŽท","accordion":"๐Ÿช—","guitar":"๐ŸŽธ","musical keyboard":"๐ŸŽน","trumpet":"๐ŸŽบ","violin":"๐ŸŽป","banjo":"๐Ÿช•","drum":"๐Ÿฅ","long drum":"๐Ÿช˜","maracas":"๐Ÿช‡","flute":"๐Ÿชˆ","mobile phone":"๐Ÿ“ฑ","mobile phone with arrow":"๐Ÿ“ฒ","telephone":"โ˜Ž๏ธ","telephone receiver":"๐Ÿ“ž","pager":"๐Ÿ“Ÿ","fax machine":"๐Ÿ“ ","battery":"๐Ÿ”‹","low battery":"๐Ÿชซ","electric plug":"๐Ÿ”Œ","laptop":"๐Ÿ’ป","desktop computer":"๐Ÿ–ฅ๏ธ","printer":"๐Ÿ–จ๏ธ","keyboard":"โŒจ๏ธ","computer mouse":"๐Ÿ–ฑ๏ธ","trackball":"๐Ÿ–ฒ๏ธ","computer disk":"๐Ÿ’ฝ","floppy disk":"๐Ÿ’พ","optical disk":"๐Ÿ’ฟ","dvd":"๐Ÿ“€","abacus":"๐Ÿงฎ","movie camera":"๐ŸŽฅ","film frames":"๐ŸŽž๏ธ","film projector":"๐Ÿ“ฝ๏ธ","clapper board":"๐ŸŽฌ","television":"๐Ÿ“บ","camera":"๐Ÿ“ท","camera with flash":"๐Ÿ“ธ","video camera":"๐Ÿ“น","videocassette":"๐Ÿ“ผ","magnifying glass tilted left":"๐Ÿ”","magnifying glass tilted right":"๐Ÿ”Ž","candle":"๐Ÿ•ฏ๏ธ","light bulb":"๐Ÿ’ก","flashlight":"๐Ÿ”ฆ","red paper lantern":"๐Ÿฎ","diya lamp":"๐Ÿช”","notebook with decorative cover":"๐Ÿ“”","closed book":"๐Ÿ“•","open book":"๐Ÿ“–","green book":"๐Ÿ“—","blue book":"๐Ÿ“˜","orange book":"๐Ÿ“™","books":"๐Ÿ“š","notebook":"๐Ÿ““","ledger":"๐Ÿ“’","page with curl":"๐Ÿ“ƒ","scroll":"๐Ÿ“œ","page facing up":"๐Ÿ“„","newspaper":"๐Ÿ“ฐ","rolled-up newspaper":"๐Ÿ—ž๏ธ","bookmark tabs":"๐Ÿ“‘","bookmark":"๐Ÿ”–","label":"๐Ÿท๏ธ","money bag":"๐Ÿ’ฐ","coin":"๐Ÿช™","yen banknote":"๐Ÿ’ด","dollar banknote":"๐Ÿ’ต","euro banknote":"๐Ÿ’ถ","pound banknote":"๐Ÿ’ท","money with wings":"๐Ÿ’ธ","credit card":"๐Ÿ’ณ","receipt":"๐Ÿงพ","chart increasing with yen":"๐Ÿ’น","envelope":"โœ‰๏ธ","e-mail":"๐Ÿ“ง","incoming envelope":"๐Ÿ“จ","envelope with arrow":"๐Ÿ“ฉ","outbox tray":"๐Ÿ“ค","inbox tray":"๐Ÿ“ฅ","package":"๐Ÿ“ฆ","closed mailbox with raised flag":"๐Ÿ“ซ","closed mailbox with lowered flag":"๐Ÿ“ช","open mailbox with raised flag":"๐Ÿ“ฌ","open mailbox with lowered flag":"๐Ÿ“ญ","postbox":"๐Ÿ“ฎ","ballot box with ballot":"๐Ÿ—ณ๏ธ","pencil":"โœ๏ธ","black nib":"โœ’๏ธ","fountain pen":"๐Ÿ–‹๏ธ","pen":"๐Ÿ–Š๏ธ","paintbrush":"๐Ÿ–Œ๏ธ","crayon":"๐Ÿ–๏ธ","memo":"๐Ÿ“","briefcase":"๐Ÿ’ผ","file folder":"๐Ÿ“","open file folder":"๐Ÿ“‚","card index dividers":"๐Ÿ—‚๏ธ","calendar":"๐Ÿ“…","tear-off calendar":"๐Ÿ“†","spiral notepad":"๐Ÿ—’๏ธ","spiral calendar":"๐Ÿ—“๏ธ","card index":"๐Ÿ“‡","chart increasing":"๐Ÿ“ˆ","chart decreasing":"๐Ÿ“‰","bar chart":"๐Ÿ“Š","clipboard":"๐Ÿ“‹","pushpin":"๐Ÿ“Œ","round pushpin":"๐Ÿ“","paperclip":"๐Ÿ“Ž","linked paperclips":"๐Ÿ–‡๏ธ","straight ruler":"๐Ÿ“","triangular ruler":"๐Ÿ“","scissors":"โœ‚๏ธ","card file box":"๐Ÿ—ƒ๏ธ","file cabinet":"๐Ÿ—„๏ธ","wastebasket":"๐Ÿ—‘๏ธ","locked":"๐Ÿ”’","unlocked":"๐Ÿ”“","locked with pen":"๐Ÿ”","locked with key":"๐Ÿ”","key":"๐Ÿ”‘","old key":"๐Ÿ—๏ธ","hammer":"๐Ÿ”จ","axe":"๐Ÿช“","pick":"โ›๏ธ","hammer and pick":"โš’๏ธ","hammer and wrench":"๐Ÿ› ๏ธ","dagger":"๐Ÿ—ก๏ธ","crossed swords":"โš”๏ธ","bomb":"๐Ÿ’ฃ","boomerang":"๐Ÿชƒ","bow and arrow":"๐Ÿน","shield":"๐Ÿ›ก๏ธ","carpentry saw":"๐Ÿชš","wrench":"๐Ÿ”ง","screwdriver":"๐Ÿช›","nut and bolt":"๐Ÿ”ฉ","gear":"โš™๏ธ","clamp":"๐Ÿ—œ๏ธ","balance scale":"โš–๏ธ","white cane":"๐Ÿฆฏ","link":"๐Ÿ”—","chains":"โ›“๏ธ","hook":"๐Ÿช","toolbox":"๐Ÿงฐ","magnet":"๐Ÿงฒ","ladder":"๐Ÿชœ","alembic":"โš—๏ธ","test tube":"๐Ÿงช","petri dish":"๐Ÿงซ","dna":"๐Ÿงฌ","microscope":"๐Ÿ”ฌ","telescope":"๐Ÿ”ญ","satellite antenna":"๐Ÿ“ก","syringe":"๐Ÿ’‰","drop of blood":"๐Ÿฉธ","pill":"๐Ÿ’Š","adhesive bandage":"๐Ÿฉน","crutch":"๐Ÿฉผ","stethoscope":"๐Ÿฉบ","x-ray":"๐Ÿฉป","door":"๐Ÿšช","elevator":"๐Ÿ›—","mirror":"๐Ÿชž","window":"๐ŸชŸ","bed":"๐Ÿ›๏ธ","couch and lamp":"๐Ÿ›‹๏ธ","chair":"๐Ÿช‘","toilet":"๐Ÿšฝ","plunger":"๐Ÿช ","shower":"๐Ÿšฟ","bathtub":"๐Ÿ›","mouse trap":"๐Ÿชค","razor":"๐Ÿช’","lotion bottle":"๐Ÿงด","safety pin":"๐Ÿงท","broom":"๐Ÿงน","basket":"๐Ÿงบ","roll of paper":"๐Ÿงป","bucket":"๐Ÿชฃ","soap":"๐Ÿงผ","bubbles":"๐Ÿซง","toothbrush":"๐Ÿชฅ","sponge":"๐Ÿงฝ","fire extinguisher":"๐Ÿงฏ","shopping cart":"๐Ÿ›’","cigarette":"๐Ÿšฌ","coffin":"โšฐ๏ธ","headstone":"๐Ÿชฆ","funeral urn":"โšฑ๏ธ","nazar amulet":"๐Ÿงฟ","hamsa":"๐Ÿชฌ","moai":"๐Ÿ—ฟ","placard":"๐Ÿชง","identification card":"๐Ÿชช"},"Symbols":{"ATM sign":"๐Ÿง","litter in bin sign":"๐Ÿšฎ","potable water":"๐Ÿšฐ","wheelchair symbol":"โ™ฟ","menโ€™s room":"๐Ÿšน","womenโ€™s room":"๐Ÿšบ","restroom":"๐Ÿšป","baby symbol":"๐Ÿšผ","water closet":"๐Ÿšพ","passport control":"๐Ÿ›‚","customs":"๐Ÿ›ƒ","baggage claim":"๐Ÿ›„","left luggage":"๐Ÿ›…","warning":"โš ๏ธ","children crossing":"๐Ÿšธ","no entry":"โ›”","prohibited":"๐Ÿšซ","no bicycles":"๐Ÿšณ","no smoking":"๐Ÿšญ","no littering":"๐Ÿšฏ","non-potable water":"๐Ÿšฑ","no pedestrians":"๐Ÿšท","no mobile phones":"๐Ÿ“ต","no one under eighteen":"๐Ÿ”ž","radioactive":"โ˜ข๏ธ","biohazard":"โ˜ฃ๏ธ","up arrow":"โฌ†๏ธ","up-right arrow":"โ†—๏ธ","right arrow":"โžก๏ธ","down-right arrow":"โ†˜๏ธ","down arrow":"โฌ‡๏ธ","down-left arrow":"โ†™๏ธ","left arrow":"โฌ…๏ธ","up-left arrow":"โ†–๏ธ","up-down arrow":"โ†•๏ธ","left-right arrow":"โ†”๏ธ","right arrow curving left":"โ†ฉ๏ธ","left arrow curving right":"โ†ช๏ธ","right arrow curving up":"โคด๏ธ","right arrow curving down":"โคต๏ธ","clockwise vertical arrows":"๐Ÿ”ƒ","counterclockwise arrows button":"๐Ÿ”„","BACK arrow":"๐Ÿ”™","END arrow":"๐Ÿ”š","ON! arrow":"๐Ÿ”›","SOON arrow":"๐Ÿ”œ","TOP arrow":"๐Ÿ”","place of worship":"๐Ÿ›","atom symbol":"โš›๏ธ","om":"๐Ÿ•‰๏ธ","star of David":"โœก๏ธ","wheel of dharma":"โ˜ธ๏ธ","yin yang":"โ˜ฏ๏ธ","latin cross":"โœ๏ธ","orthodox cross":"โ˜ฆ๏ธ","star and crescent":"โ˜ช๏ธ","peace symbol":"โ˜ฎ๏ธ","menorah":"๐Ÿ•Ž","dotted six-pointed star":"๐Ÿ”ฏ","khanda":"๐Ÿชฏ","Aries":"โ™ˆ","Taurus":"โ™‰","Gemini":"โ™Š","Cancer":"โ™‹","Leo":"โ™Œ","Virgo":"โ™","Libra":"โ™Ž","Scorpio":"โ™","Sagittarius":"โ™","Capricorn":"โ™‘","Aquarius":"โ™’","Pisces":"โ™“","Ophiuchus":"โ›Ž","shuffle tracks button":"๐Ÿ”€","repeat button":"๐Ÿ”","repeat single button":"๐Ÿ”‚","play button":"โ–ถ๏ธ","fast-forward button":"โฉ","next track button":"โญ๏ธ","play or pause button":"โฏ๏ธ","reverse button":"โ—€๏ธ","fast reverse button":"โช","last track button":"โฎ๏ธ","upwards button":"๐Ÿ”ผ","fast up button":"โซ","downwards button":"๐Ÿ”ฝ","fast down button":"โฌ","pause button":"โธ๏ธ","stop button":"โน๏ธ","record button":"โบ๏ธ","eject button":"โ๏ธ","cinema":"๐ŸŽฆ","dim button":"๐Ÿ”…","bright button":"๐Ÿ”†","antenna bars":"๐Ÿ“ถ","wireless":"๐Ÿ›œ","vibration mode":"๐Ÿ“ณ","mobile phone off":"๐Ÿ“ด","female sign":"โ™€๏ธ","male sign":"โ™‚๏ธ","transgender symbol":"โšง๏ธ","multiply":"โœ–๏ธ","plus":"โž•","minus":"โž–","divide":"โž—","heavy equals sign":"๐ŸŸฐ","infinity":"โ™พ๏ธ","double exclamation mark":"โ€ผ๏ธ","exclamation question mark":"โ‰๏ธ","red question mark":"โ“","white question mark":"โ”","white exclamation mark":"โ•","red exclamation mark":"โ—","wavy dash":"ใ€ฐ๏ธ","currency exchange":"๐Ÿ’ฑ","heavy dollar sign":"๐Ÿ’ฒ","medical symbol":"โš•๏ธ","recycling symbol":"โ™ป๏ธ","fleur-de-lis":"โšœ๏ธ","trident emblem":"๐Ÿ”ฑ","name badge":"๐Ÿ“›","Japanese symbol for beginner":"๐Ÿ”ฐ","hollow red circle":"โญ•","check mark button":"โœ…","check box with check":"โ˜‘๏ธ","check mark":"โœ”๏ธ","cross mark":"โŒ","cross mark button":"โŽ","curly loop":"โžฐ","double curly loop":"โžฟ","part alternation mark":"ใ€ฝ๏ธ","eight-spoked asterisk":"โœณ๏ธ","eight-pointed star":"โœด๏ธ","sparkle":"โ‡๏ธ","copyright":"ยฉ๏ธ","registered":"ยฎ๏ธ","trade mark":"โ„ข๏ธ","keycap #":"#๏ธโƒฃ","keycap *":"*๏ธโƒฃ","keycap 0":"0๏ธโƒฃ","keycap 1":"1๏ธโƒฃ","keycap 2":"2๏ธโƒฃ","keycap 3":"3๏ธโƒฃ","keycap 4":"4๏ธโƒฃ","keycap 5":"5๏ธโƒฃ","keycap 6":"6๏ธโƒฃ","keycap 7":"7๏ธโƒฃ","keycap 8":"8๏ธโƒฃ","keycap 9":"9๏ธโƒฃ","keycap 10":"๐Ÿ”Ÿ","input latin uppercase":"๐Ÿ” ","input latin lowercase":"๐Ÿ”ก","input numbers":"๐Ÿ”ข","input symbols":"๐Ÿ”ฃ","input latin letters":"๐Ÿ”ค","A button (blood type)":"๐Ÿ…ฐ๏ธ","AB button (blood type)":"๐Ÿ†Ž","B button (blood type)":"๐Ÿ…ฑ๏ธ","CL button":"๐Ÿ†‘","COOL button":"๐Ÿ†’","FREE button":"๐Ÿ†“","information":"โ„น๏ธ","ID button":"๐Ÿ†”","circled M":"โ“‚๏ธ","NEW button":"๐Ÿ†•","NG button":"๐Ÿ†–","O button (blood type)":"๐Ÿ…พ๏ธ","OK button":"๐Ÿ†—","P button":"๐Ÿ…ฟ๏ธ","SOS button":"๐Ÿ†˜","UP! button":"๐Ÿ†™","VS button":"๐Ÿ†š","Japanese โ€œhereโ€ button":"๐Ÿˆ","Japanese โ€œservice chargeโ€ button":"๐Ÿˆ‚๏ธ","Japanese โ€œmonthly amountโ€ button":"๐Ÿˆท๏ธ","Japanese โ€œnot free of chargeโ€ button":"๐Ÿˆถ","Japanese โ€œreservedโ€ button":"๐Ÿˆฏ","Japanese โ€œbargainโ€ button":"๐Ÿ‰","Japanese โ€œdiscountโ€ button":"๐Ÿˆน","Japanese โ€œfree of chargeโ€ button":"๐Ÿˆš","Japanese โ€œprohibitedโ€ button":"๐Ÿˆฒ","Japanese โ€œacceptableโ€ button":"๐Ÿ‰‘","Japanese โ€œapplicationโ€ button":"๐Ÿˆธ","Japanese โ€œpassing gradeโ€ button":"๐Ÿˆด","Japanese โ€œvacancyโ€ button":"๐Ÿˆณ","Japanese โ€œcongratulationsโ€ button":"ใŠ—๏ธ","Japanese โ€œsecretโ€ button":"ใŠ™๏ธ","Japanese โ€œopen for businessโ€ button":"๐Ÿˆบ","Japanese โ€œno vacancyโ€ button":"๐Ÿˆต","red circle":"๐Ÿ”ด","orange circle":"๐ŸŸ ","yellow circle":"๐ŸŸก","green circle":"๐ŸŸข","blue circle":"๐Ÿ”ต","purple circle":"๐ŸŸฃ","brown circle":"๐ŸŸค","black circle":"โšซ","white circle":"โšช","red square":"๐ŸŸฅ","orange square":"๐ŸŸง","yellow square":"๐ŸŸจ","green square":"๐ŸŸฉ","blue square":"๐ŸŸฆ","purple square":"๐ŸŸช","brown square":"๐ŸŸซ","black large square":"โฌ›","white large square":"โฌœ","black medium square":"โ—ผ๏ธ","white medium square":"โ—ป๏ธ","black medium-small square":"โ—พ","white medium-small square":"โ—ฝ","black small square":"โ–ช๏ธ","white small square":"โ–ซ๏ธ","large orange diamond":"๐Ÿ”ถ","large blue diamond":"๐Ÿ”ท","small orange diamond":"๐Ÿ”ธ","small blue diamond":"๐Ÿ”น","red triangle pointed up":"๐Ÿ”บ","red triangle pointed down":"๐Ÿ”ป","diamond with a dot":"๐Ÿ’ ","radio button":"๐Ÿ”˜","white square button":"๐Ÿ”ณ","black square button":"๐Ÿ”ฒ"},"Flags":{"chequered flag":"๐Ÿ","triangular flag":"๐Ÿšฉ","crossed flags":"๐ŸŽŒ","black flag":"๐Ÿด","white flag":"๐Ÿณ๏ธ","rainbow flag":"๐Ÿณ๏ธโ€๐ŸŒˆ","transgender flag":"๐Ÿณ๏ธโ€โšง๏ธ","pirate flag":"๐Ÿดโ€โ˜ ๏ธ","flag Ascension Island":"๐Ÿ‡ฆ๐Ÿ‡จ","flag Andorra":"๐Ÿ‡ฆ๐Ÿ‡ฉ","flag United Arab Emirates":"๐Ÿ‡ฆ๐Ÿ‡ช","flag Afghanistan":"๐Ÿ‡ฆ๐Ÿ‡ซ","flag Antigua & Barbuda":"๐Ÿ‡ฆ๐Ÿ‡ฌ","flag Anguilla":"๐Ÿ‡ฆ๐Ÿ‡ฎ","flag Albania":"๐Ÿ‡ฆ๐Ÿ‡ฑ","flag Armenia":"๐Ÿ‡ฆ๐Ÿ‡ฒ","flag Angola":"๐Ÿ‡ฆ๐Ÿ‡ด","flag Antarctica":"๐Ÿ‡ฆ๐Ÿ‡ถ","flag Argentina":"๐Ÿ‡ฆ๐Ÿ‡ท","flag American Samoa":"๐Ÿ‡ฆ๐Ÿ‡ธ","flag Austria":"๐Ÿ‡ฆ๐Ÿ‡น","flag Australia":"๐Ÿ‡ฆ๐Ÿ‡บ","flag Aruba":"๐Ÿ‡ฆ๐Ÿ‡ผ","flag ร…land Islands":"๐Ÿ‡ฆ๐Ÿ‡ฝ","flag Azerbaijan":"๐Ÿ‡ฆ๐Ÿ‡ฟ","flag Bosnia & Herzegovina":"๐Ÿ‡ง๐Ÿ‡ฆ","flag Barbados":"๐Ÿ‡ง๐Ÿ‡ง","flag Bangladesh":"๐Ÿ‡ง๐Ÿ‡ฉ","flag Belgium":"๐Ÿ‡ง๐Ÿ‡ช","flag Burkina Faso":"๐Ÿ‡ง๐Ÿ‡ซ","flag Bulgaria":"๐Ÿ‡ง๐Ÿ‡ฌ","flag Bahrain":"๐Ÿ‡ง๐Ÿ‡ญ","flag Burundi":"๐Ÿ‡ง๐Ÿ‡ฎ","flag Benin":"๐Ÿ‡ง๐Ÿ‡ฏ","flag St. Barthรฉlemy":"๐Ÿ‡ง๐Ÿ‡ฑ","flag Bermuda":"๐Ÿ‡ง๐Ÿ‡ฒ","flag Brunei":"๐Ÿ‡ง๐Ÿ‡ณ","flag Bolivia":"๐Ÿ‡ง๐Ÿ‡ด","flag Caribbean Netherlands":"๐Ÿ‡ง๐Ÿ‡ถ","flag Brazil":"๐Ÿ‡ง๐Ÿ‡ท","flag Bahamas":"๐Ÿ‡ง๐Ÿ‡ธ","flag Bhutan":"๐Ÿ‡ง๐Ÿ‡น","flag Bouvet Island":"๐Ÿ‡ง๐Ÿ‡ป","flag Botswana":"๐Ÿ‡ง๐Ÿ‡ผ","flag Belarus":"๐Ÿ‡ง๐Ÿ‡พ","flag Belize":"๐Ÿ‡ง๐Ÿ‡ฟ","flag Canada":"๐Ÿ‡จ๐Ÿ‡ฆ","flag Cocos (Keeling) Islands":"๐Ÿ‡จ๐Ÿ‡จ","flag Congo - Kinshasa":"๐Ÿ‡จ๐Ÿ‡ฉ","flag Central African Republic":"๐Ÿ‡จ๐Ÿ‡ซ","flag Congo - Brazzaville":"๐Ÿ‡จ๐Ÿ‡ฌ","flag Switzerland":"๐Ÿ‡จ๐Ÿ‡ญ","flag Cรดte dโ€™Ivoire":"๐Ÿ‡จ๐Ÿ‡ฎ","flag Cook Islands":"๐Ÿ‡จ๐Ÿ‡ฐ","flag Chile":"๐Ÿ‡จ๐Ÿ‡ฑ","flag Cameroon":"๐Ÿ‡จ๐Ÿ‡ฒ","flag China":"๐Ÿ‡จ๐Ÿ‡ณ","flag Colombia":"๐Ÿ‡จ๐Ÿ‡ด","flag Clipperton Island":"๐Ÿ‡จ๐Ÿ‡ต","flag Costa Rica":"๐Ÿ‡จ๐Ÿ‡ท","flag Cuba":"๐Ÿ‡จ๐Ÿ‡บ","flag Cape Verde":"๐Ÿ‡จ๐Ÿ‡ป","flag Curaรงao":"๐Ÿ‡จ๐Ÿ‡ผ","flag Christmas Island":"๐Ÿ‡จ๐Ÿ‡ฝ","flag Cyprus":"๐Ÿ‡จ๐Ÿ‡พ","flag Czechia":"๐Ÿ‡จ๐Ÿ‡ฟ","flag Germany":"๐Ÿ‡ฉ๐Ÿ‡ช","flag Diego Garcia":"๐Ÿ‡ฉ๐Ÿ‡ฌ","flag Djibouti":"๐Ÿ‡ฉ๐Ÿ‡ฏ","flag Denmark":"๐Ÿ‡ฉ๐Ÿ‡ฐ","flag Dominica":"๐Ÿ‡ฉ๐Ÿ‡ฒ","flag Dominican Republic":"๐Ÿ‡ฉ๐Ÿ‡ด","flag Algeria":"๐Ÿ‡ฉ๐Ÿ‡ฟ","flag Ceuta & Melilla":"๐Ÿ‡ช๐Ÿ‡ฆ","flag Ecuador":"๐Ÿ‡ช๐Ÿ‡จ","flag Estonia":"๐Ÿ‡ช๐Ÿ‡ช","flag Egypt":"๐Ÿ‡ช๐Ÿ‡ฌ","flag Western Sahara":"๐Ÿ‡ช๐Ÿ‡ญ","flag Eritrea":"๐Ÿ‡ช๐Ÿ‡ท","flag Spain":"๐Ÿ‡ช๐Ÿ‡ธ","flag Ethiopia":"๐Ÿ‡ช๐Ÿ‡น","flag European Union":"๐Ÿ‡ช๐Ÿ‡บ","flag Finland":"๐Ÿ‡ซ๐Ÿ‡ฎ","flag Fiji":"๐Ÿ‡ซ๐Ÿ‡ฏ","flag Falkland Islands":"๐Ÿ‡ซ๐Ÿ‡ฐ","flag Micronesia":"๐Ÿ‡ซ๐Ÿ‡ฒ","flag Faroe Islands":"๐Ÿ‡ซ๐Ÿ‡ด","flag France":"๐Ÿ‡ซ๐Ÿ‡ท","flag Gabon":"๐Ÿ‡ฌ๐Ÿ‡ฆ","flag United Kingdom":"๐Ÿ‡ฌ๐Ÿ‡ง","flag Grenada":"๐Ÿ‡ฌ๐Ÿ‡ฉ","flag Georgia":"๐Ÿ‡ฌ๐Ÿ‡ช","flag French Guiana":"๐Ÿ‡ฌ๐Ÿ‡ซ","flag Guernsey":"๐Ÿ‡ฌ๐Ÿ‡ฌ","flag Ghana":"๐Ÿ‡ฌ๐Ÿ‡ญ","flag Gibraltar":"๐Ÿ‡ฌ๐Ÿ‡ฎ","flag Greenland":"๐Ÿ‡ฌ๐Ÿ‡ฑ","flag Gambia":"๐Ÿ‡ฌ๐Ÿ‡ฒ","flag Guinea":"๐Ÿ‡ฌ๐Ÿ‡ณ","flag Guadeloupe":"๐Ÿ‡ฌ๐Ÿ‡ต","flag Equatorial Guinea":"๐Ÿ‡ฌ๐Ÿ‡ถ","flag Greece":"๐Ÿ‡ฌ๐Ÿ‡ท","flag South Georgia & South Sandwich Islands":"๐Ÿ‡ฌ๐Ÿ‡ธ","flag Guatemala":"๐Ÿ‡ฌ๐Ÿ‡น","flag Guam":"๐Ÿ‡ฌ๐Ÿ‡บ","flag Guinea-Bissau":"๐Ÿ‡ฌ๐Ÿ‡ผ","flag Guyana":"๐Ÿ‡ฌ๐Ÿ‡พ","flag Hong Kong SAR China":"๐Ÿ‡ญ๐Ÿ‡ฐ","flag Heard & McDonald Islands":"๐Ÿ‡ญ๐Ÿ‡ฒ","flag Honduras":"๐Ÿ‡ญ๐Ÿ‡ณ","flag Croatia":"๐Ÿ‡ญ๐Ÿ‡ท","flag Haiti":"๐Ÿ‡ญ๐Ÿ‡น","flag Hungary":"๐Ÿ‡ญ๐Ÿ‡บ","flag Canary Islands":"๐Ÿ‡ฎ๐Ÿ‡จ","flag Indonesia":"๐Ÿ‡ฎ๐Ÿ‡ฉ","flag Ireland":"๐Ÿ‡ฎ๐Ÿ‡ช","flag Israel":"๐Ÿ‡ฎ๐Ÿ‡ฑ","flag Isle of Man":"๐Ÿ‡ฎ๐Ÿ‡ฒ","flag India":"๐Ÿ‡ฎ๐Ÿ‡ณ","flag British Indian Ocean Territory":"๐Ÿ‡ฎ๐Ÿ‡ด","flag Iraq":"๐Ÿ‡ฎ๐Ÿ‡ถ","flag Iran":"๐Ÿ‡ฎ๐Ÿ‡ท","flag Iceland":"๐Ÿ‡ฎ๐Ÿ‡ธ","flag Italy":"๐Ÿ‡ฎ๐Ÿ‡น","flag Jersey":"๐Ÿ‡ฏ๐Ÿ‡ช","flag Jamaica":"๐Ÿ‡ฏ๐Ÿ‡ฒ","flag Jordan":"๐Ÿ‡ฏ๐Ÿ‡ด","flag Japan":"๐Ÿ‡ฏ๐Ÿ‡ต","flag Kenya":"๐Ÿ‡ฐ๐Ÿ‡ช","flag Kyrgyzstan":"๐Ÿ‡ฐ๐Ÿ‡ฌ","flag Cambodia":"๐Ÿ‡ฐ๐Ÿ‡ญ","flag Kiribati":"๐Ÿ‡ฐ๐Ÿ‡ฎ","flag Comoros":"๐Ÿ‡ฐ๐Ÿ‡ฒ","flag St. Kitts & Nevis":"๐Ÿ‡ฐ๐Ÿ‡ณ","flag North Korea":"๐Ÿ‡ฐ๐Ÿ‡ต","flag South Korea":"๐Ÿ‡ฐ๐Ÿ‡ท","flag Kuwait":"๐Ÿ‡ฐ๐Ÿ‡ผ","flag Cayman Islands":"๐Ÿ‡ฐ๐Ÿ‡พ","flag Kazakhstan":"๐Ÿ‡ฐ๐Ÿ‡ฟ","flag Laos":"๐Ÿ‡ฑ๐Ÿ‡ฆ","flag Lebanon":"๐Ÿ‡ฑ๐Ÿ‡ง","flag St. Lucia":"๐Ÿ‡ฑ๐Ÿ‡จ","flag Liechtenstein":"๐Ÿ‡ฑ๐Ÿ‡ฎ","flag Sri Lanka":"๐Ÿ‡ฑ๐Ÿ‡ฐ","flag Liberia":"๐Ÿ‡ฑ๐Ÿ‡ท","flag Lesotho":"๐Ÿ‡ฑ๐Ÿ‡ธ","flag Lithuania":"๐Ÿ‡ฑ๐Ÿ‡น","flag Luxembourg":"๐Ÿ‡ฑ๐Ÿ‡บ","flag Latvia":"๐Ÿ‡ฑ๐Ÿ‡ป","flag Libya":"๐Ÿ‡ฑ๐Ÿ‡พ","flag Morocco":"๐Ÿ‡ฒ๐Ÿ‡ฆ","flag Monaco":"๐Ÿ‡ฒ๐Ÿ‡จ","flag Moldova":"๐Ÿ‡ฒ๐Ÿ‡ฉ","flag Montenegro":"๐Ÿ‡ฒ๐Ÿ‡ช","flag St. Martin":"๐Ÿ‡ฒ๐Ÿ‡ซ","flag Madagascar":"๐Ÿ‡ฒ๐Ÿ‡ฌ","flag Marshall Islands":"๐Ÿ‡ฒ๐Ÿ‡ญ","flag North Macedonia":"๐Ÿ‡ฒ๐Ÿ‡ฐ","flag Mali":"๐Ÿ‡ฒ๐Ÿ‡ฑ","flag Myanmar (Burma)":"๐Ÿ‡ฒ๐Ÿ‡ฒ","flag Mongolia":"๐Ÿ‡ฒ๐Ÿ‡ณ","flag Macao SAR China":"๐Ÿ‡ฒ๐Ÿ‡ด","flag Northern Mariana Islands":"๐Ÿ‡ฒ๐Ÿ‡ต","flag Martinique":"๐Ÿ‡ฒ๐Ÿ‡ถ","flag Mauritania":"๐Ÿ‡ฒ๐Ÿ‡ท","flag Montserrat":"๐Ÿ‡ฒ๐Ÿ‡ธ","flag Malta":"๐Ÿ‡ฒ๐Ÿ‡น","flag Mauritius":"๐Ÿ‡ฒ๐Ÿ‡บ","flag Maldives":"๐Ÿ‡ฒ๐Ÿ‡ป","flag Malawi":"๐Ÿ‡ฒ๐Ÿ‡ผ","flag Mexico":"๐Ÿ‡ฒ๐Ÿ‡ฝ","flag Malaysia":"๐Ÿ‡ฒ๐Ÿ‡พ","flag Mozambique":"๐Ÿ‡ฒ๐Ÿ‡ฟ","flag Namibia":"๐Ÿ‡ณ๐Ÿ‡ฆ","flag New Caledonia":"๐Ÿ‡ณ๐Ÿ‡จ","flag Niger":"๐Ÿ‡ณ๐Ÿ‡ช","flag Norfolk Island":"๐Ÿ‡ณ๐Ÿ‡ซ","flag Nigeria":"๐Ÿ‡ณ๐Ÿ‡ฌ","flag Nicaragua":"๐Ÿ‡ณ๐Ÿ‡ฎ","flag Netherlands":"๐Ÿ‡ณ๐Ÿ‡ฑ","flag Norway":"๐Ÿ‡ณ๐Ÿ‡ด","flag Nepal":"๐Ÿ‡ณ๐Ÿ‡ต","flag Nauru":"๐Ÿ‡ณ๐Ÿ‡ท","flag Niue":"๐Ÿ‡ณ๐Ÿ‡บ","flag New Zealand":"๐Ÿ‡ณ๐Ÿ‡ฟ","flag Oman":"๐Ÿ‡ด๐Ÿ‡ฒ","flag Panama":"๐Ÿ‡ต๐Ÿ‡ฆ","flag Peru":"๐Ÿ‡ต๐Ÿ‡ช","flag French Polynesia":"๐Ÿ‡ต๐Ÿ‡ซ","flag Papua New Guinea":"๐Ÿ‡ต๐Ÿ‡ฌ","flag Philippines":"๐Ÿ‡ต๐Ÿ‡ญ","flag Pakistan":"๐Ÿ‡ต๐Ÿ‡ฐ","flag Poland":"๐Ÿ‡ต๐Ÿ‡ฑ","flag St. Pierre & Miquelon":"๐Ÿ‡ต๐Ÿ‡ฒ","flag Pitcairn Islands":"๐Ÿ‡ต๐Ÿ‡ณ","flag Puerto Rico":"๐Ÿ‡ต๐Ÿ‡ท","flag Palestinian Territories":"๐Ÿ‡ต๐Ÿ‡ธ","flag Portugal":"๐Ÿ‡ต๐Ÿ‡น","flag Palau":"๐Ÿ‡ต๐Ÿ‡ผ","flag Paraguay":"๐Ÿ‡ต๐Ÿ‡พ","flag Qatar":"๐Ÿ‡ถ๐Ÿ‡ฆ","flag Rรฉunion":"๐Ÿ‡ท๐Ÿ‡ช","flag Romania":"๐Ÿ‡ท๐Ÿ‡ด","flag Serbia":"๐Ÿ‡ท๐Ÿ‡ธ","flag Russia":"๐Ÿ‡ท๐Ÿ‡บ","flag Rwanda":"๐Ÿ‡ท๐Ÿ‡ผ","flag Saudi Arabia":"๐Ÿ‡ธ๐Ÿ‡ฆ","flag Solomon Islands":"๐Ÿ‡ธ๐Ÿ‡ง","flag Seychelles":"๐Ÿ‡ธ๐Ÿ‡จ","flag Sudan":"๐Ÿ‡ธ๐Ÿ‡ฉ","flag Sweden":"๐Ÿ‡ธ๐Ÿ‡ช","flag Singapore":"๐Ÿ‡ธ๐Ÿ‡ฌ","flag St. Helena":"๐Ÿ‡ธ๐Ÿ‡ญ","flag Slovenia":"๐Ÿ‡ธ๐Ÿ‡ฎ","flag Svalbard & Jan Mayen":"๐Ÿ‡ธ๐Ÿ‡ฏ","flag Slovakia":"๐Ÿ‡ธ๐Ÿ‡ฐ","flag Sierra Leone":"๐Ÿ‡ธ๐Ÿ‡ฑ","flag San Marino":"๐Ÿ‡ธ๐Ÿ‡ฒ","flag Senegal":"๐Ÿ‡ธ๐Ÿ‡ณ","flag Somalia":"๐Ÿ‡ธ๐Ÿ‡ด","flag Suriname":"๐Ÿ‡ธ๐Ÿ‡ท","flag South Sudan":"๐Ÿ‡ธ๐Ÿ‡ธ","flag Sรฃo Tomรฉ & Prรญncipe":"๐Ÿ‡ธ๐Ÿ‡น","flag El Salvador":"๐Ÿ‡ธ๐Ÿ‡ป","flag Sint Maarten":"๐Ÿ‡ธ๐Ÿ‡ฝ","flag Syria":"๐Ÿ‡ธ๐Ÿ‡พ","flag Eswatini":"๐Ÿ‡ธ๐Ÿ‡ฟ","flag Tristan da Cunha":"๐Ÿ‡น๐Ÿ‡ฆ","flag Turks & Caicos Islands":"๐Ÿ‡น๐Ÿ‡จ","flag Chad":"๐Ÿ‡น๐Ÿ‡ฉ","flag French Southern Territories":"๐Ÿ‡น๐Ÿ‡ซ","flag Togo":"๐Ÿ‡น๐Ÿ‡ฌ","flag Thailand":"๐Ÿ‡น๐Ÿ‡ญ","flag Tajikistan":"๐Ÿ‡น๐Ÿ‡ฏ","flag Tokelau":"๐Ÿ‡น๐Ÿ‡ฐ","flag Timor-Leste":"๐Ÿ‡น๐Ÿ‡ฑ","flag Turkmenistan":"๐Ÿ‡น๐Ÿ‡ฒ","flag Tunisia":"๐Ÿ‡น๐Ÿ‡ณ","flag Tonga":"๐Ÿ‡น๐Ÿ‡ด","flag Turkey":"๐Ÿ‡น๐Ÿ‡ท","flag Trinidad & Tobago":"๐Ÿ‡น๐Ÿ‡น","flag Tuvalu":"๐Ÿ‡น๐Ÿ‡ป","flag Taiwan":"๐Ÿ‡น๐Ÿ‡ผ","flag Tanzania":"๐Ÿ‡น๐Ÿ‡ฟ","flag Ukraine":"๐Ÿ‡บ๐Ÿ‡ฆ","flag Uganda":"๐Ÿ‡บ๐Ÿ‡ฌ","flag U.S. Outlying Islands":"๐Ÿ‡บ๐Ÿ‡ฒ","flag United Nations":"๐Ÿ‡บ๐Ÿ‡ณ","flag United States":"๐Ÿ‡บ๐Ÿ‡ธ","flag Uruguay":"๐Ÿ‡บ๐Ÿ‡พ","flag Uzbekistan":"๐Ÿ‡บ๐Ÿ‡ฟ","flag Vatican City":"๐Ÿ‡ป๐Ÿ‡ฆ","flag St. Vincent & Grenadines":"๐Ÿ‡ป๐Ÿ‡จ","flag Venezuela":"๐Ÿ‡ป๐Ÿ‡ช","flag British Virgin Islands":"๐Ÿ‡ป๐Ÿ‡ฌ","flag U.S. Virgin Islands":"๐Ÿ‡ป๐Ÿ‡ฎ","flag Vietnam":"๐Ÿ‡ป๐Ÿ‡ณ","flag Vanuatu":"๐Ÿ‡ป๐Ÿ‡บ","flag Wallis & Futuna":"๐Ÿ‡ผ๐Ÿ‡ซ","flag Samoa":"๐Ÿ‡ผ๐Ÿ‡ธ","flag Kosovo":"๐Ÿ‡ฝ๐Ÿ‡ฐ","flag Yemen":"๐Ÿ‡พ๐Ÿ‡ช","flag Mayotte":"๐Ÿ‡พ๐Ÿ‡น","flag South Africa":"๐Ÿ‡ฟ๐Ÿ‡ฆ","flag Zambia":"๐Ÿ‡ฟ๐Ÿ‡ฒ","flag Zimbabwe":"๐Ÿ‡ฟ๐Ÿ‡ผ","flag England":"๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ","flag Scotland":"๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ","flag Wales":"๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ"}} \ No newline at end of file diff --git a/apps/gg/gpx.js b/apps/gg/gpx.js deleted file mode 100644 index 93421d88..00000000 --- a/apps/gg/gpx.js +++ /dev/null @@ -1,81 +0,0 @@ -function xml_parse(xml) { - let result; - let path = []; - let tag_begin; - let text_begin; - for (let i = 0; i < xml.length; i++) { - let c = xml.charAt(i); - if (!tag_begin && c == '<') { - if (i > text_begin && path.length) { - let value = xml.substring(text_begin, i); - if (!/^\s*$/.test(value)) { - path[path.length - 1].value = value; - } - } - tag_begin = i + 1; - } else if (tag_begin && c == '>') { - let tag = xml.substring(tag_begin, i).trim(); - if (tag.startsWith('?') && tag.endsWith('?')) { - /* Ignore directives. */ - } else if (tag.startsWith('/')) { - path.pop(); - } else { - let parts = tag.split(' '); - let attributes = {}; - for (let j = 1; j < parts.length; j++) { - let eq = parts[j].indexOf('='); - let value = parts[j].substring(eq + 1); - if (value.startsWith('"') && value.endsWith('"')) { - value = value.substring(1, value.length - 1); - } - attributes[parts[j].substring(0, eq)] = value; - } - let next = {name: parts[0], children: [], attributes: attributes}; - if (path.length) { - path[path.length - 1].children.push(next); - } else { - result = next; - } - if (!tag.endsWith('/')) { - path.push(next); - } - } - tag_begin = undefined; - text_begin = i + 1; - } - } - return result; -} - -function* xml_each(node, name) { - for (let child of node.children) { - if (child.name == name) { - yield child; - } - } -} - -export function gpx_parse(xml) { - let result = {segments: []}; - let tree = xml_parse(xml); - if (tree?.name == 'gpx') { - for (let trk of xml_each(tree, 'trk')) { - for (let trkseg of xml_each(trk, 'trkseg')) { - let segment = []; - for (let trkpt of xml_each(trkseg, 'trkpt')) { - segment.push({lat: parseFloat(trkpt.attributes.lat), lon: parseFloat(trkpt.attributes.lon)}); - } - result.segments.push(segment); - } - } - } - for (let metadata of xml_each(tree, 'metadata')) { - for (let link of xml_each(metadata, 'link')) { - result.link = link.attributes.href; - } - for (let time of xml_each(metadata, 'time')) { - result.time = time.value; - } - } - return result; -} \ No newline at end of file diff --git a/apps/gg/handler.js b/apps/gg/handler.js deleted file mode 100644 index 647e81cd..00000000 --- a/apps/gg/handler.js +++ /dev/null @@ -1,21 +0,0 @@ -import * as strava from './strava.js'; - -async function main() { - print('handler running'); - let r = await strava.authorization_code(request.query.code); - print('state =', request.query.state); - print('body = ', r.body); - if (request.query.state && r.body) { - let shared_db = await shared_database('state'); - await shared_db.set(request.query.state, utf8Decode(r.body)); - } - await respond({ - data: r.body, - content_type: 'text/plain', - headers: { - Location: 'https://tildefriends.net/~cory/gg/', - }, - status_code: 307, - }); -} -main(); \ No newline at end of file diff --git a/apps/gg/index.html b/apps/gg/index.html deleted file mode 100644 index 57d98809..00000000 --- a/apps/gg/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/gg/leaflet.css b/apps/gg/leaflet.css deleted file mode 100644 index ea8c2f7d..00000000 --- a/apps/gg/leaflet.css +++ /dev/null @@ -1,661 +0,0 @@ -/* required styles */ - -.leaflet-pane, -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-tile-container, -.leaflet-pane > svg, -.leaflet-pane > canvas, -.leaflet-zoom-box, -.leaflet-image-layer, -.leaflet-layer { - position: absolute; - left: 0; - top: 0; - } -.leaflet-container { - overflow: hidden; - } -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-user-drag: none; - } -/* Prevents IE11 from highlighting tiles in blue */ -.leaflet-tile::selection { - background: transparent; -} -/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ -.leaflet-safari .leaflet-tile { - image-rendering: -webkit-optimize-contrast; - } -/* hack that prevents hw layers "stretching" when loading new tiles */ -.leaflet-safari .leaflet-tile-container { - width: 1600px; - height: 1600px; - -webkit-transform-origin: 0 0; - } -.leaflet-marker-icon, -.leaflet-marker-shadow { - display: block; - } -/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ -/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ -.leaflet-container .leaflet-overlay-pane svg { - max-width: none !important; - max-height: none !important; - } -.leaflet-container .leaflet-marker-pane img, -.leaflet-container .leaflet-shadow-pane img, -.leaflet-container .leaflet-tile-pane img, -.leaflet-container img.leaflet-image-layer, -.leaflet-container .leaflet-tile { - max-width: none !important; - max-height: none !important; - width: auto; - padding: 0; - } - -.leaflet-container img.leaflet-tile { - /* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */ - mix-blend-mode: plus-lighter; -} - -.leaflet-container.leaflet-touch-zoom { - -ms-touch-action: pan-x pan-y; - touch-action: pan-x pan-y; - } -.leaflet-container.leaflet-touch-drag { - -ms-touch-action: pinch-zoom; - /* Fallback for FF which doesn't support pinch-zoom */ - touch-action: none; - touch-action: pinch-zoom; -} -.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { - -ms-touch-action: none; - touch-action: none; -} -.leaflet-container { - -webkit-tap-highlight-color: transparent; -} -.leaflet-container a { - -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); -} -.leaflet-tile { - filter: inherit; - visibility: hidden; - } -.leaflet-tile-loaded { - visibility: inherit; - } -.leaflet-zoom-box { - width: 0; - height: 0; - -moz-box-sizing: border-box; - box-sizing: border-box; - z-index: 800; - } -/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ -.leaflet-overlay-pane svg { - -moz-user-select: none; - } - -.leaflet-pane { z-index: 400; } - -.leaflet-tile-pane { z-index: 200; } -.leaflet-overlay-pane { z-index: 400; } -.leaflet-shadow-pane { z-index: 500; } -.leaflet-marker-pane { z-index: 600; } -.leaflet-tooltip-pane { z-index: 650; } -.leaflet-popup-pane { z-index: 700; } - -.leaflet-map-pane canvas { z-index: 100; } -.leaflet-map-pane svg { z-index: 200; } - -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - - -/* control positioning */ - -.leaflet-control { - position: relative; - z-index: 800; - pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ - pointer-events: auto; - } -.leaflet-top, -.leaflet-bottom { - position: absolute; - z-index: 1000; - pointer-events: none; - } -.leaflet-top { - top: 0; - } -.leaflet-right { - right: 0; - } -.leaflet-bottom { - bottom: 0; - } -.leaflet-left { - left: 0; - } -.leaflet-control { - float: left; - clear: both; - } -.leaflet-right .leaflet-control { - float: right; - } -.leaflet-top .leaflet-control { - margin-top: 10px; - } -.leaflet-bottom .leaflet-control { - margin-bottom: 10px; - } -.leaflet-left .leaflet-control { - margin-left: 10px; - } -.leaflet-right .leaflet-control { - margin-right: 10px; - } - - -/* zoom and fade animations */ - -.leaflet-fade-anim .leaflet-popup { - opacity: 0; - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; - } -.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { - opacity: 1; - } -.leaflet-zoom-animated { - -webkit-transform-origin: 0 0; - -ms-transform-origin: 0 0; - transform-origin: 0 0; - } -svg.leaflet-zoom-animated { - will-change: transform; -} - -.leaflet-zoom-anim .leaflet-zoom-animated { - -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); - -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); - transition: transform 0.25s cubic-bezier(0,0,0.25,1); - } -.leaflet-zoom-anim .leaflet-tile, -.leaflet-pan-anim .leaflet-tile { - -webkit-transition: none; - -moz-transition: none; - transition: none; - } - -.leaflet-zoom-anim .leaflet-zoom-hide { - visibility: hidden; - } - - -/* cursors */ - -.leaflet-interactive { - cursor: pointer; - } -.leaflet-grab { - cursor: -webkit-grab; - cursor: -moz-grab; - cursor: grab; - } -.leaflet-crosshair, -.leaflet-crosshair .leaflet-interactive { - cursor: crosshair; - } -.leaflet-popup-pane, -.leaflet-control { - cursor: auto; - } -.leaflet-dragging .leaflet-grab, -.leaflet-dragging .leaflet-grab .leaflet-interactive, -.leaflet-dragging .leaflet-marker-draggable { - cursor: move; - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - cursor: grabbing; - } - -/* marker & overlays interactivity */ -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-image-layer, -.leaflet-pane > svg path, -.leaflet-tile-container { - pointer-events: none; - } - -.leaflet-marker-icon.leaflet-interactive, -.leaflet-image-layer.leaflet-interactive, -.leaflet-pane > svg path.leaflet-interactive, -svg.leaflet-image-layer.leaflet-interactive path { - pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ - pointer-events: auto; - } - -/* visual tweaks */ - -.leaflet-container { - background: #ddd; - outline-offset: 1px; - } -.leaflet-container a { - color: #0078A8; - } -.leaflet-zoom-box { - border: 2px dotted #38f; - background: rgba(255,255,255,0.5); - } - - -/* general typography */ -.leaflet-container { - font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; - font-size: 12px; - font-size: 0.75rem; - line-height: 1.5; - } - - -/* general toolbar styles */ - -.leaflet-bar { - box-shadow: 0 1px 5px rgba(0,0,0,0.65); - border-radius: 4px; - } -.leaflet-bar a { - background-color: #fff; - border-bottom: 1px solid #ccc; - width: 26px; - height: 26px; - line-height: 26px; - display: block; - text-align: center; - text-decoration: none; - color: black; - } -.leaflet-bar a, -.leaflet-control-layers-toggle { - background-position: 50% 50%; - background-repeat: no-repeat; - display: block; - } -.leaflet-bar a:hover, -.leaflet-bar a:focus { - background-color: #f4f4f4; - } -.leaflet-bar a:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } -.leaflet-bar a:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom: none; - } -.leaflet-bar a.leaflet-disabled { - cursor: default; - background-color: #f4f4f4; - color: #bbb; - } - -.leaflet-touch .leaflet-bar a { - width: 30px; - height: 30px; - line-height: 30px; - } -.leaflet-touch .leaflet-bar a:first-child { - border-top-left-radius: 2px; - border-top-right-radius: 2px; - } -.leaflet-touch .leaflet-bar a:last-child { - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - } - -/* zoom control */ - -.leaflet-control-zoom-in, -.leaflet-control-zoom-out { - font: bold 18px 'Lucida Console', Monaco, monospace; - text-indent: 1px; - } - -.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { - font-size: 22px; - } - - -/* layers control */ - -.leaflet-control-layers { - box-shadow: 0 1px 5px rgba(0,0,0,0.4); - background: #fff; - border-radius: 5px; - } -.leaflet-control-layers-toggle { - background-image: url(images/layers.png); - width: 36px; - height: 36px; - } -.leaflet-retina .leaflet-control-layers-toggle { - background-image: url(images/layers-2x.png); - background-size: 26px 26px; - } -.leaflet-touch .leaflet-control-layers-toggle { - width: 44px; - height: 44px; - } -.leaflet-control-layers .leaflet-control-layers-list, -.leaflet-control-layers-expanded .leaflet-control-layers-toggle { - display: none; - } -.leaflet-control-layers-expanded .leaflet-control-layers-list { - display: block; - position: relative; - } -.leaflet-control-layers-expanded { - padding: 6px 10px 6px 6px; - color: #333; - background: #fff; - } -.leaflet-control-layers-scrollbar { - overflow-y: scroll; - overflow-x: hidden; - padding-right: 5px; - } -.leaflet-control-layers-selector { - margin-top: 2px; - position: relative; - top: 1px; - } -.leaflet-control-layers label { - display: block; - font-size: 13px; - font-size: 1.08333em; - } -.leaflet-control-layers-separator { - height: 0; - border-top: 1px solid #ddd; - margin: 5px -10px 5px -6px; - } - -/* Default icon URLs */ -.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */ - background-image: url(images/marker-icon.png); - } - - -/* attribution and scale controls */ - -.leaflet-container .leaflet-control-attribution { - background: #fff; - background: rgba(255, 255, 255, 0.8); - margin: 0; - } -.leaflet-control-attribution, -.leaflet-control-scale-line { - padding: 0 5px; - color: #333; - line-height: 1.4; - } -.leaflet-control-attribution a { - text-decoration: none; - } -.leaflet-control-attribution a:hover, -.leaflet-control-attribution a:focus { - text-decoration: underline; - } -.leaflet-attribution-flag { - display: inline !important; - vertical-align: baseline !important; - width: 1em; - height: 0.6669em; - } -.leaflet-left .leaflet-control-scale { - margin-left: 5px; - } -.leaflet-bottom .leaflet-control-scale { - margin-bottom: 5px; - } -.leaflet-control-scale-line { - border: 2px solid #777; - border-top: none; - line-height: 1.1; - padding: 2px 5px 1px; - white-space: nowrap; - -moz-box-sizing: border-box; - box-sizing: border-box; - background: rgba(255, 255, 255, 0.8); - text-shadow: 1px 1px #fff; - } -.leaflet-control-scale-line:not(:first-child) { - border-top: 2px solid #777; - border-bottom: none; - margin-top: -2px; - } -.leaflet-control-scale-line:not(:first-child):not(:last-child) { - border-bottom: 2px solid #777; - } - -.leaflet-touch .leaflet-control-attribution, -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - box-shadow: none; - } -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - border: 2px solid rgba(0,0,0,0.2); - background-clip: padding-box; - } - - -/* popup */ - -.leaflet-popup { - position: absolute; - text-align: center; - margin-bottom: 20px; - } -.leaflet-popup-content-wrapper { - padding: 1px; - text-align: left; - border-radius: 12px; - } -.leaflet-popup-content { - margin: 13px 24px 13px 20px; - line-height: 1.3; - font-size: 13px; - font-size: 1.08333em; - min-height: 1px; - } -.leaflet-popup-content p { - margin: 17px 0; - margin: 1.3em 0; - } -.leaflet-popup-tip-container { - width: 40px; - height: 20px; - position: absolute; - left: 50%; - margin-top: -1px; - margin-left: -20px; - overflow: hidden; - pointer-events: none; - } -.leaflet-popup-tip { - width: 17px; - height: 17px; - padding: 1px; - - margin: -10px auto 0; - pointer-events: auto; - - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - } -.leaflet-popup-content-wrapper, -.leaflet-popup-tip { - background: white; - color: #333; - box-shadow: 0 3px 14px rgba(0,0,0,0.4); - } -.leaflet-container a.leaflet-popup-close-button { - position: absolute; - top: 0; - right: 0; - border: none; - text-align: center; - width: 24px; - height: 24px; - font: 16px/24px Tahoma, Verdana, sans-serif; - color: #757575; - text-decoration: none; - background: transparent; - } -.leaflet-container a.leaflet-popup-close-button:hover, -.leaflet-container a.leaflet-popup-close-button:focus { - color: #585858; - } -.leaflet-popup-scrolled { - overflow: auto; - } - -.leaflet-oldie .leaflet-popup-content-wrapper { - -ms-zoom: 1; - } -.leaflet-oldie .leaflet-popup-tip { - width: 24px; - margin: 0 auto; - - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - } - -.leaflet-oldie .leaflet-control-zoom, -.leaflet-oldie .leaflet-control-layers, -.leaflet-oldie .leaflet-popup-content-wrapper, -.leaflet-oldie .leaflet-popup-tip { - border: 1px solid #999; - } - - -/* div icon */ - -.leaflet-div-icon { - background: #fff; - border: 1px solid #666; - } - - -/* Tooltip */ -/* Base styles for the element that has a tooltip */ -.leaflet-tooltip { - position: absolute; - padding: 6px; - background-color: #fff; - border: 1px solid #fff; - border-radius: 3px; - color: #222; - white-space: nowrap; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - pointer-events: none; - box-shadow: 0 1px 3px rgba(0,0,0,0.4); - } -.leaflet-tooltip.leaflet-interactive { - cursor: pointer; - pointer-events: auto; - } -.leaflet-tooltip-top:before, -.leaflet-tooltip-bottom:before, -.leaflet-tooltip-left:before, -.leaflet-tooltip-right:before { - position: absolute; - pointer-events: none; - border: 6px solid transparent; - background: transparent; - content: ""; - } - -/* Directions */ - -.leaflet-tooltip-bottom { - margin-top: 6px; -} -.leaflet-tooltip-top { - margin-top: -6px; -} -.leaflet-tooltip-bottom:before, -.leaflet-tooltip-top:before { - left: 50%; - margin-left: -6px; - } -.leaflet-tooltip-top:before { - bottom: 0; - margin-bottom: -12px; - border-top-color: #fff; - } -.leaflet-tooltip-bottom:before { - top: 0; - margin-top: -12px; - margin-left: -6px; - border-bottom-color: #fff; - } -.leaflet-tooltip-left { - margin-left: -6px; -} -.leaflet-tooltip-right { - margin-left: 6px; -} -.leaflet-tooltip-left:before, -.leaflet-tooltip-right:before { - top: 50%; - margin-top: -6px; - } -.leaflet-tooltip-left:before { - right: 0; - margin-right: -12px; - border-left-color: #fff; - } -.leaflet-tooltip-right:before { - left: 0; - margin-left: -12px; - border-right-color: #fff; - } - -/* Printing */ - -@media print { - /* Prevent printers from removing background-images of controls. */ - .leaflet-control { - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - } \ No newline at end of file diff --git a/apps/gg/leaflet.js b/apps/gg/leaflet.js deleted file mode 100644 index a3bf693d..00000000 --- a/apps/gg/leaflet.js +++ /dev/null @@ -1,6 +0,0 @@ -/* @preserve - * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com - * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){"use strict";function l(t){for(var e,i,n=1,o=arguments.length;n=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>e.x&&n.xe.y&&n.y=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>e.lat&&n.late.lng&&n.lng","http://www.w3.org/2000/svg"===(Wt.firstChild&&Wt.firstChild.namespaceURI));function y(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:pt,ielt9:mt,edge:n,webkit:ft,android:gt,android23:vt,androidStock:yt,opera:xt,chrome:wt,gecko:bt,safari:Pt,phantom:Lt,opera12:o,win:Tt,ie3d:Mt,webkit3d:zt,gecko3d:_t,any3d:Ct,mobile:Zt,mobileWebkit:St,mobileWebkit3d:Et,msPointer:kt,pointer:Ot,touch:Bt,touchNative:At,mobileOpera:It,mobileGecko:Rt,retina:Nt,passiveEvents:Dt,canvas:jt,svg:Ht,vml:!Ht&&function(){try{var t=document.createElement("div"),e=(t.innerHTML='',t.firstChild);return e.style.behavior="url(#default#VML)",e&&"object"==typeof e.adj}catch(t){return!1}}(),inlineSvg:Wt,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Ft=b.msPointer?"MSPointerDown":"pointerdown",Ut=b.msPointer?"MSPointerMove":"pointermove",Vt=b.msPointer?"MSPointerUp":"pointerup",qt=b.msPointer?"MSPointerCancel":"pointercancel",Gt={touchstart:Ft,touchmove:Ut,touchend:Vt,touchcancel:qt},Kt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e);ee(t,e)},touchmove:ee,touchend:ee,touchcancel:ee},Yt={},Xt=!1;function Jt(t,e,i){return"touchstart"!==e||Xt||(document.addEventListener(Ft,$t,!0),document.addEventListener(Ut,Qt,!0),document.addEventListener(Vt,te,!0),document.addEventListener(qt,te,!0),Xt=!0),Kt[e]?(i=Kt[e].bind(this,i),t.addEventListener(Gt[e],i,!1),i):(console.warn("wrong event specified:",e),u)}function $t(t){Yt[t.pointerId]=t}function Qt(t){Yt[t.pointerId]&&(Yt[t.pointerId]=t)}function te(t){delete Yt[t.pointerId]}function ee(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var i in e.touches=[],Yt)e.touches.push(Yt[i]);e.changedTouches=[e],t(e)}}var ie=200;function ne(t,i){t.addEventListener("dblclick",i);var n,o=0;function e(t){var e;1!==t.detail?n=t.detail:"mouse"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((e=Ne(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!e.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((e=Date.now())-o<=ie?2===++n&&i(function(t){var e,i,n={};for(i in t)e=t[i],n[i]=e&&e.bind?e.bind(t):e;return(t=n).type="dblclick",n.detail=2,n.isTrusted=!1,n._simulated=!0,n}(t)):n=1,o=e))}return t.addEventListener("click",e),{dblclick:i,simDblclick:e}}var oe,se,re,ae,he,le,ue=we(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ce=we(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===ce||"OTransition"===ce?ce+"End":"transitionend";function _e(t){return"string"==typeof t?document.getElementById(t):t}function pe(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return"auto"===(i=i&&"auto"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){t=document.createElement(t);return t.className=e||"",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function me(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function fe(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ge(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function ve(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=xe(t)).length&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=F(e),o=0,s=n.length;othis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=this.getPixelBounds(),i=_([s.min.add(i),s.max.subtract(n)]),s=i.getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round(),n=n.subtract(o);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(a(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),"geolocation"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:"Geolocation not supported."}),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 e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)"number"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire("locationfound",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.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(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){e=P("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane);return t&&(this._panes[t]=e),e},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 s(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,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=_(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){t=this._getTopLeftPoint(t,e);return new f(t,t.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,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(w(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){t=m(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(w(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(w(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(w(t),w(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(m(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(w(t)))},mouseEventToContainerPoint:function(t){return De(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=_e(t);if(!t)throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");S(t,"scroll",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,"leaflet-container"+(b.touch?" leaflet-touch":"")+(b.retina?" leaflet-retina":"")+(b.ielt9?" leaflet-oldie":"")+(b.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":"")),pe(t,"position"));"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(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),Z(this._mapPane,new p(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(M(t.markerPane,"leaflet-zoom-hide"),M(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire("viewreset"),n&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire("zoom",i):((o||i&&i.pinch)&&this.fire("zoom",i),this.fire("move",i)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(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 e=t?k:S;e((this._targets[h(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=x(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,e){for(var i,n=[],o="mouseout"===e||"mouseover"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[h(s)])&&("click"===e||"preclick"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!We(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n=n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(i)||("mousedown"===(e=t.type)&&Me(i),this._fireDOMEvent(t,e))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,i){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;sthis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;x(function(){this._moveStart(!0,i.noMoveStart||!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(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 Ue(t){return new B(t)}var B=et.extend({options:{position:"topright"},initialize:function(t){c(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,"leaflet-control"),-1!==i.indexOf("bottom")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(T(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",e=document.createElement("div");return e.innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement("label"),n=this._map.hasLayer(t.layer),n=(t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=n):e=this._createRadioElement("leaflet-base-layers_"+h(this),n),this._layerControlInputs.push(e),e.layerId=h(t.layer),S(e,"click",this._onInputClick,this),document.createElement("span")),o=(n.innerHTML=" "+t.name,document.createElement("span"));return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){if(!this._preventClick){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;se.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section,e=(this._preventClick=!0,S(t,"click",O),this.expand(),this);setTimeout(function(){k(t,"click",O),e._preventClick=!1})}})),qe=B.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=P("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},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,e,i,n,o){i=P("a",i,n);return i.innerHTML=t,i.href="#",i.title=e,i.setAttribute("role","button"),i.setAttribute("aria-label",e),Ie(i),S(i,"click",Re),S(i,"click",o,this),S(i,"click",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}}),Ge=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new qe,this.addControl(this.zoomControl))}),B.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=P("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P("div",e,i)),t.imperial&&(this._iScale=P("div",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,t=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(t)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+" m":e/1e3+" km",e/t)},_updateImperial:function(t){var e,i,t=3.2808399*t;5280'+(b.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){c(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P("div","leaflet-control-attribution"),Ie(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].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,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(", ")),this._container.innerHTML=i.join(' ')}}}),n=(A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new Ke).addTo(this)}),B.Layers=Ve,B.Zoom=qe,B.Scale=Ge,B.Attribution=Ke,Ue.layers=function(t,e,i){return new Ve(t,e,i)},Ue.zoom=function(t){return new qe(t)},Ue.scale=function(t){return new Ge(t)},Ue.attribution=function(t){return new Ke(t)},et.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}})),ft=(n.addTo=function(t,e){return t.addHandler(e,this),this},{Events:e}),Ye=b.touch?"touchstart mousedown":"mousedown",Xe=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){c(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Xe._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,ve(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Xe._dragging===this&&this.finishDrag():Xe._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Xe._dragging=this)._preventOutline&&Me(this._element),Le(),re(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=Ce(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=Pe(this._element),this._parentScale=Ze(e),i="mousedown"===t.type,S(document,i?"mousemove":"touchmove",this._onMove,this),S(document,i?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1e&&(i.push(t[n]),o=n);oe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i}function ri(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||yi.prototype._containsPoint.call(this,t,!0)}});var wi=ci.extend({initialize:function(t,e){c(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=d(t)?t:t.features;if(o){for(e=0,i=o.length;es.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),Ii=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Bi,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Bi,this._popup,t,e),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 ci||(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 e;this._popup&&this._map&&(Re(t),e=t.layer||t.target,this._popup._source!==e||e instanceof fi?(this._popup._source=e,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)}}),Ai.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Ai.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){Ai.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=Ai.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=P("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i="top"===s?(e=r/2,a):"bottom"===s?(e=r/2,0):(e="center"===s?r/2:"right"===s?0:"left"===s?r:i.xthis.options.maxZoom||nthis.options.maxZoom||void 0!==this.options.minZoom&&oi.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new s(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var t=t.split(":"),e=new p(+t[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=u,t.onmousemove=u,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&x(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),b.ielt9||!this._map._fadeAnimated?x(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var Di=Ni.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=c(this,e)).detectRetina&&b.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),zt={_initContainer:function(){this._container=P("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Wi.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=Vi("shape");M(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=Vi("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[h(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Vi("stroke")),o.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=d(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Vi("fill")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){fe(t._container)},_bringToBack:function(t){ge(t._container)}},qi=b.vml?Vi:ct,Gi=Wi.extend({_initContainer:function(){this._container=qi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=qi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Wi.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),Z(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update"))},_initPath:function(t){var e=t._path=qi("path");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,"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){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute("stroke",t.color),e.setAttribute("stroke-opacity",t.opacity),e.setAttribute("stroke-width",t.weight),e.setAttribute("stroke-linecap",t.lineCap),e.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?e.setAttribute("stroke-dasharray",t.dashArray):e.removeAttribute("stroke-dasharray"),t.dashOffset?e.setAttribute("stroke-dashoffset",t.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),t.fill?(e.setAttribute("fill",t.fillColor||t.color),e.setAttribute("fill-opacity",t.fillOpacity),e.setAttribute("fill-rule",t.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n="a"+i+","+(Math.max(Math.round(t._radiusY),1)||i)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+n+2*i+",0 "+n+2*-i+",0 ";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){fe(t._path)},_bringToBack:function(t){ge(t._path)}});function Ki(t){return b.svg||b.vml?new Gi(t):null}b.vml&&Gi.include(zt),A.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){var e;return"overlayPane"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&Ui(t)||Ki(t)}});var Yi=xi.extend({initialize:function(t,e){xi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Gi.create=qi,Gi.pointsToPath=dt,wi.geometryToLayer=bi,wi.coordsToLatLng=Li,wi.coordsToLatLngs=Ti,wi.latLngToCoords=Mi,wi.latLngsToCoords=zi,wi.getFeature=Ci,wi.asFeature=Zi,A.mergeOptions({boxZoom:!0});var _t=n.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(){S(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){k(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(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(),re(),Le(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P("div","leaflet-zoom-box",this._container),M(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var t=new f(this._point,this._startPoint),e=t.getSize();Z(this._box,t.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(T(this._box),z(this._container,"leaflet-crosshair")),ae(),Te(),k(document,{contextmenu:Re,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(a(this._resetState,this),0),t=new s(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())}}),Ct=(A.addInitHook("addHandler","boxZoom",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),Zt=(A.addInitHook("addHandler","doubleClickZoom",Ct),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Xe(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))),M(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,"leaflet-grab"),z(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,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=_(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,e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,n=(n+e+i)%t-e-i,t=Math.abs(o+i)e.getMaxZoom()&&1new n("string"==typeof t?t:t+"",void 0,i),o=(t,...s)=>{const e=1===t.length?t[0]:s.reduce(((s,i,e)=>s+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[e+1]),t[0]);return new n(e,t,i)},h=(i,e)=>{if(s)i.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet));else for(const s of e){const e=document.createElement("style"),n=t.litNonce;void 0!==n&&e.setAttribute("nonce",n),e.textContent=s.cssText,i.appendChild(e)}},c=s?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let s="";for(const i of t.cssRules)s+=i.cssText;return r(s)})(t):t -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */,{is:l,defineProperty:a,getOwnPropertyDescriptor:u,getOwnPropertyNames:d,getOwnPropertySymbols:f,getPrototypeOf:p}=Object,v=globalThis,y=v.trustedTypes,m=y?y.emptyScript:"",b=v.reactiveElementPolyfillSupport,g=(t,s)=>t,w={toAttribute(t,s){switch(s){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},_=(t,s)=>!l(t,s),S={attribute:!0,type:String,converter:w,reflect:!1,hasChanged:_};Symbol.metadata??=Symbol("metadata"),v.litPropertyMetadata??=new WeakMap;class $ extends HTMLElement{static addInitializer(t){this.o(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this.u&&[...this.u.keys()]}static createProperty(t,s=S){if(s.state&&(s.attribute=!1),this.o(),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),e=this.getPropertyDescriptor(t,i,s);void 0!==e&&a(this.prototype,t,e)}}static getPropertyDescriptor(t,s,i){const{get:e,set:n}=u(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t}};return{get(){return e?.call(this)},set(s){const r=e?.call(this);n.call(this,s),this.requestUpdate(t,r,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??S}static o(){if(this.hasOwnProperty(g("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(g("finalized")))return;if(this.finalized=!0,this.o(),this.hasOwnProperty(g("properties"))){const t=this.properties,s=[...d(t),...f(t)];for(const i of s)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i)}this.u=new Map;for(const[t,s]of this.elementProperties){const i=this.p(t,s);void 0!==i&&this.u.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const s=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)s.unshift(c(t))}else void 0!==t&&s.push(c(t));return s}static p(t,s){const i=s.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this.v=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this.m=null,this._()}_(){this.S=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this.$(),this.requestUpdate(),this.constructor.l?.forEach((t=>t(this)))}addController(t){(this.P??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this.P?.delete(t)}$(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this.v=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return h(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this.P?.forEach((t=>t.hostConnected?.()))}enableUpdating(t){}disconnectedCallback(){this.P?.forEach((t=>t.hostDisconnected?.()))}attributeChangedCallback(t,s,i){this._$AK(t,i)}C(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor.p(t,i);if(void 0!==e&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:w).toAttribute(s,i.type);this.m=t,null==n?this.removeAttribute(e):this.setAttribute(e,n),this.m=null}}_$AK(t,s){const i=this.constructor,e=i.u.get(t);if(void 0!==e&&this.m!==e){const t=i.getPropertyOptions(e),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:w;this.m=e,this[e]=n.fromAttribute(s,t.type),this.m=null}}requestUpdate(t,s,i){if(void 0!==t){if(i??=this.constructor.getPropertyOptions(t),!(i.hasChanged??_)(this[t],s))return;this.T(t,s,i)}!1===this.isUpdatePending&&(this.S=this.A())}T(t,s,i){this._$AL.has(t)||this._$AL.set(t,s),!0===i.reflect&&this.m!==t&&(this.M??=new Set).add(t)}async A(){this.isUpdatePending=!0;try{await this.S}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this.v){for(const[t,s]of this.v)this[t]=s;this.v=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t)!0!==i.wrapped||this._$AL.has(s)||void 0===this[s]||this.T(s,this[s],i)}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this.P?.forEach((t=>t.hostUpdate?.())),this.update(s)):this.k()}catch(s){throw t=!1,this.k(),s}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this.P?.forEach((t=>t.hostUpdated?.())),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}k(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this.S}shouldUpdate(t){return!0}update(t){this.M&&=this.M.forEach((t=>this.C(t,this[t]))),this.k()}updated(t){}firstUpdated(t){}}$.elementStyles=[],$.shadowRootOptions={mode:"open"},$[g("elementProperties")]=new Map,$[g("finalized")]=new Map,b?.({ReactiveElement:$}),(v.reactiveElementVersions??=[]).push("2.0.4"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const T=globalThis,x=T.trustedTypes,E=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,C="$lit$",P=`lit$${(Math.random()+"").slice(9)}$`,A="?"+P,k=`<${A}>`,M=document,U=()=>M.createComment(""),V=t=>null===t||"object"!=typeof t&&"function"!=typeof t,O=Array.isArray,R=t=>O(t)||"function"==typeof t?.[Symbol.iterator],N="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,L=/-->/g,j=/>/g,H=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),I=/'/g,D=/"/g,B=/^(?:script|style|textarea|title)$/i,W=t=>(s,...i)=>({_$litType$:t,strings:s,values:i}),Z=W(1),q=W(2),F=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),J=new WeakMap,K=M.createTreeWalker(M,129);function Y(t,s){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==E?E.createHTML(s):s}const Q=(t,s)=>{const i=t.length-1,e=[];let n,r=2===s?"":"",o=z;for(let s=0;s"===c[0]?(o=n??z,l=-1):void 0===c[1]?l=-2:(l=o.lastIndex-c[2].length,h=c[1],o=void 0===c[3]?H:'"'===c[3]?D:I):o===D||o===I?o=H:o===L||o===j?o=z:(o=H,n=void 0);const u=o===H&&t[s+1].startsWith("/>")?" ":"";r+=o===z?i+k:l>=0?(e.push(h),i.slice(0,l)+C+i.slice(l)+P+u):i+P+(-2===l?s:u)}return[Y(t,r+(t[i]||"")+(2===s?"":"")),e]};class X{constructor({strings:t,_$litType$:s},i){let e;this.parts=[];let n=0,r=0;const o=t.length-1,h=this.parts,[c,l]=Q(t,s);if(this.el=X.createElement(c,i),K.currentNode=this.el.content,2===s){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(e=K.nextNode())&&h.length0){e.textContent=x?x.emptyScript:"";for(let i=0;i2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,s=this,i,e){const n=this.strings;let r=!1;if(void 0===n)t=tt(this,t,s,0),r=!V(t)||t!==this._$AH&&t!==F,r&&(this._$AH=t);else{const e=t;let o,h;for(t=n[0],o=0;o{const e=i?.renderBefore??s;let n=e._$litPart$;if(void 0===n){const t=i?.renderBefore??null;e._$litPart$=n=new it(s.insertBefore(U(),t),t,void 0,i??{})}return n._$AI(t),n}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class ut extends ${constructor(){super(...arguments),this.renderOptions={host:this},this.ht=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const s=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this.ht=at(s,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this.ht?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this.ht?.setConnected(!1)}render(){return F}}ut._$litElement$=!0,ut[("finalized","finalized")]=!0,globalThis.litElementHydrateSupport?.({LitElement:ut});const dt=globalThis.litElementPolyfillSupport;dt?.({LitElement:ut});const ft={_$AK:(t,s,i)=>{t._$AK(s,i)},_$AL:t=>t._$AL};(globalThis.litElementVersions??=[]).push("4.0.4"); -/** - * @license - * Copyright 2022 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const pt=!1,{Y:vt}=ct,yt=t=>null===t||"object"!=typeof t&&"function"!=typeof t,mt={HTML:1,SVG:2},bt=(t,s)=>void 0===s?void 0!==t?._$litType$:t?._$litType$===s,gt=t=>null!=t?._$litType$?.h,wt=t=>void 0!==t?._$litDirective$,_t=t=>t?._$litDirective$,St=t=>void 0===t.strings,$t=()=>document.createComment(""),Tt=(t,s,i)=>{const e=t._$AA.parentNode,n=void 0===s?t._$AB:s._$AA;if(void 0===i){const s=e.insertBefore($t(),n),r=e.insertBefore($t(),n);i=new vt(s,r,t,t.options)}else{const s=i._$AB.nextSibling,r=i._$AM,o=r!==t;if(o){let s;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(s=t._$AU)!==r._$AU&&i._$AP(s)}if(s!==n||o){let t=i._$AA;for(;t!==s;){const s=t.nextSibling;e.insertBefore(t,n),t=s}}}return i},xt=(t,s,i=t)=>(t._$AI(s,i),t),Et={},Ct=(t,s=Et)=>t._$AH=s,Pt=t=>t._$AH,At=t=>{t._$AP?.(!1,!0);let s=t._$AA;const i=t._$AB.nextSibling;for(;s!==i;){const t=s.nextSibling;s.remove(),s=t}},kt=t=>{t._$AR()},Mt={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},Ut=t=>(...s)=>({_$litDirective$:t,values:s});class Vt{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,s,i){this.nt=t,this._$AM=s,this.rt=i}_$AS(t,s){return this.update(t,s)}update(t,s){return this.render(...s)}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Ot=(t,s)=>{const i=t._$AN;if(void 0===i)return!1;for(const t of i)t._$AO?.(s,!1),Ot(t,s);return!0},Rt=t=>{let s,i;do{if(void 0===(s=t._$AM))break;i=s._$AN,i.delete(t),t=s}while(0===i?.size)},Nt=t=>{for(let s;s=t._$AM;t=s){let i=s._$AN;if(void 0===i)s._$AN=i=new Set;else if(i.has(t))break;i.add(t),jt(s)}};function zt(t){void 0!==this._$AN?(Rt(this),this._$AM=t,Nt(this)):this._$AM=t}function Lt(t,s=!1,i=0){const e=this._$AH,n=this._$AN;if(void 0!==n&&0!==n.size)if(s)if(Array.isArray(e))for(let t=i;t{2==t.type&&(t._$AP??=Lt,t._$AQ??=zt)};class Ht extends Vt{constructor(){super(...arguments),this._$AN=void 0}_$AT(t,s,i){super._$AT(t,s,i),Nt(this),this.isConnected=t._$AU}_$AO(t,s=!0){t!==this.isConnected&&(this.isConnected=t,t?this.reconnected?.():this.disconnected?.()),s&&(Ot(this,t),Rt(this))}setValue(t){if(St(this.nt))this.nt._$AI(t,this);else{const s=[...this.nt._$AH];s[this.rt]=t,this.nt._$AI(s,this,0)}}disconnected(){}reconnected(){}} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class It{constructor(t){this.ct=t}disconnect(){this.ct=void 0}reconnect(t){this.ct=t}deref(){return this.ct}}class Dt{constructor(){this.lt=void 0,this.ut=void 0}get(){return this.lt}pause(){this.lt??=new Promise((t=>this.ut=t))}resume(){this.ut?.(),this.lt=this.ut=void 0}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class Bt extends Ht{constructor(){super(...arguments),this.dt=new It(this),this.ft=new Dt}render(t,s){return F}update(t,[s,i]){if(this.isConnected||this.disconnected(),s===this.vt)return F;this.vt=s;let e=0;const{dt:n,ft:r}=this;return(async(t,s)=>{for await(const i of t)if(!1===await s(i))return})(s,(async t=>{for(;r.get();)await r.get();const o=n.deref();if(void 0!==o){if(o.vt!==s)return!1;void 0!==i&&(t=i(t,e)),o.commitValue(t,e),e++}return!0})),F}commitValue(t,s){this.setValue(t)}disconnected(){this.dt.disconnect(),this.ft.pause()}reconnected(){this.dt.reconnect(this),this.ft.resume()}}const Wt=Ut(Bt),Zt=Ut( -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -class extends Bt{constructor(t){if(super(t),2!==t.type)throw Error("asyncAppend can only be used in child expressions")}update(t,s){return this.ht=t,super.update(t,s)}commitValue(t,s){0===s&&kt(this.ht);const i=Tt(this.ht);xt(i,t)}}),qt=t=>gt(t)?t._$litType$.h:t.strings,Ft=Ut(class extends Vt{constructor(t){super(t),this.yt=new WeakMap}render(t){return[t]}update(t,[s]){const i=bt(this.bt)?qt(this.bt):null,e=bt(s)?qt(s):null;if(null!==i&&(null===e||i!==e)){const s=Pt(t).pop();let e=this.yt.get(i);if(void 0===e){const t=document.createDocumentFragment();e=at(G,t),e.setConnected(!1),this.yt.set(i,e)}Ct(e,[s]),Tt(e,void 0,s)}if(null!==e){if(null===i||i!==e){const s=this.yt.get(e);if(void 0!==s){const i=Pt(s).pop();kt(t),Tt(t,void 0,i),Ct(t,[i])}}this.bt=s}else this.bt=void 0;return this.render(s)}}),Gt=(t,s,i)=>{for(const i of s)if(i[0]===t)return(0,i[1])();return i?.()},Jt=Ut( -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -class extends Vt{constructor(t){if(super(t),1!==t.type||"class"!==t.name||t.strings?.length>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter((s=>t[s])).join(" ")+" "}update(t,[s]){if(void 0===this.gt){this.gt=new Set,void 0!==t.strings&&(this.wt=new Set(t.strings.join(" ").split(/\s/).filter((t=>""!==t))));for(const t in s)s[t]&&!this.wt?.has(t)&&this.gt.add(t);return this.render(s)}const i=t.element.classList;for(const t of this.gt)t in s||(i.remove(t),this.gt.delete(t));for(const t in s){const e=!!s[t];e===this.gt.has(t)||this.wt?.has(t)||(e?(i.add(t),this.gt.add(t)):(i.remove(t),this.gt.delete(t)))}return F}}),Kt={},Yt=Ut(class extends Vt{constructor(){super(...arguments),this._t=Kt}render(t,s){return s()}update(t,[s,i]){if(Array.isArray(s)){if(Array.isArray(this._t)&&this._t.length===s.length&&s.every(((t,s)=>t===this._t[s])))return F}else if(this._t===s)return F;return this._t=Array.isArray(s)?Array.from(s):s,this.render(s,i)}}),Qt=t=>t??G -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */;function*Xt(t,s){const i="function"==typeof s;if(void 0!==t){let e=-1;for(const n of t)e>-1&&(yield i?s(e):s),e++,yield n}} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const ts=Ut(class extends Vt{constructor(){super(...arguments),this.key=G}render(t,s){return this.key=t,s}update(t,[s,i]){return s!==this.key&&(Ct(t),this.key=s),i}}),ss=Ut( -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -class extends Vt{constructor(t){if(super(t),3!==t.type&&1!==t.type&&4!==t.type)throw Error("The `live` directive is not allowed on child or event bindings");if(!St(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[s]){if(s===F||s===G)return s;const i=t.element,e=t.name;if(3===t.type){if(s===i[e])return F}else if(4===t.type){if(!!s===i.hasAttribute(e))return F}else if(1===t.type&&i.getAttribute(e)===s+"")return F;return Ct(t),s}}); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function*is(t,s){if(void 0!==t){let i=0;for(const e of t)yield s(e,i++)}} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function*es(t,s,i=1){const e=void 0===s?0:t;s??=t;for(let t=e;i>0?tnew rs;class rs{}const os=new WeakMap,hs=Ut(class extends Ht{render(t){return G}update(t,[s]){const i=s!==this.ct;return i&&void 0!==this.ct&&this.St(void 0),(i||this.$t!==this.Tt)&&(this.ct=s,this.xt=t.options?.host,this.St(this.Tt=t.element)),G}St(t){if("function"==typeof this.ct){const s=this.xt??globalThis;let i=os.get(s);void 0===i&&(i=new WeakMap,os.set(s,i)),void 0!==i.get(this.ct)&&this.ct.call(this.xt,void 0),i.set(this.ct,t),void 0!==t&&this.ct.call(this.xt,t)}else this.ct.value=t}get $t(){return"function"==typeof this.ct?os.get(this.xt??globalThis)?.get(this.ct):this.ct?.value}disconnected(){this.$t===this.Tt&&this.St(void 0)}reconnected(){this.St(this.Tt)}}),cs=(t,s,i)=>{const e=new Map;for(let n=s;n<=i;n++)e.set(t[n],n);return e},ls=Ut(class extends Vt{constructor(t){if(super(t),2!==t.type)throw Error("repeat() can only be used in text expressions")}Et(t,s,i){let e;void 0===i?i=s:void 0!==s&&(e=s);const n=[],r=[];let o=0;for(const s of t)n[o]=e?e(s,o):o,r[o]=i(s,o),o++;return{values:r,keys:n}}render(t,s,i){return this.Et(t,s,i).values}update(t,[s,i,e]){const n=Pt(t),{values:r,keys:o}=this.Et(s,i,e);if(!Array.isArray(n))return this.Ct=o,r;const h=this.Ct??=[],c=[];let l,a,u=0,d=n.length-1,f=0,p=r.length-1;for(;u<=d&&f<=p;)if(null===n[u])u++;else if(null===n[d])d--;else if(h[u]===o[f])c[f]=xt(n[u],r[f]),u++,f++;else if(h[d]===o[p])c[p]=xt(n[d],r[p]),d--,p--;else if(h[u]===o[p])c[p]=xt(n[u],r[p]),Tt(t,c[p+1],n[u]),u++,p--;else if(h[d]===o[f])c[f]=xt(n[d],r[f]),Tt(t,n[u],n[d]),d--,f++;else if(void 0===l&&(l=cs(o,f,p),a=cs(h,u,d)),l.has(h[u]))if(l.has(h[d])){const s=a.get(o[f]),i=void 0!==s?n[s]:null;if(null===i){const s=Tt(t,n[u]);xt(s,r[f]),c[f]=s}else c[f]=xt(i,r[f]),Tt(t,n[u],i),n[s]=null;f++}else At(n[d]),d--;else At(n[u]),u++;for(;f<=p;){const s=Tt(t,c[p+1]);xt(s,r[f]),c[f++]=s}for(;u<=d;){const t=n[u++];null!==t&&At(t)}return this.Ct=o,Ct(t,c),F}}),as="important",us=" !"+as,ds=Ut(class extends Vt{constructor(t){if(super(t),1!==t.type||"style"!==t.name||t.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((s,i)=>{const e=t[i];return null==e?s:s+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${e};`}),"")}update(t,[s]){const{style:i}=t.element;if(void 0===this.Pt)return this.Pt=new Set(Object.keys(s)),this.render(s);for(const t of this.Pt)null==s[t]&&(this.Pt.delete(t),t.includes("-")?i.removeProperty(t):i[t]=null);for(const t in s){const e=s[t];if(null!=e){this.Pt.add(t);const s="string"==typeof e&&e.endsWith(us);t.includes("-")||s?i.setProperty(t,s?e.slice(0,-11):e,s?as:""):i[t]=e}}return F}}),fs=Ut( -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -class extends Vt{constructor(t){if(super(t),2!==t.type)throw Error("templateContent can only be used in child bindings")}render(t){return this.At===t?F:(this.At=t,document.importNode(t.content,!0))}});class ps extends Vt{constructor(t){if(super(t),this.bt=G,2!==t.type)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(t){if(t===G||null==t)return this.kt=void 0,this.bt=t;if(t===F)return t;if("string"!=typeof t)throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.bt)return this.kt;this.bt=t;const s=[t];return s.raw=s,this.kt={_$litType$:this.constructor.resultType,strings:s,values:[]}}}ps.directiveName="unsafeHTML",ps.resultType=1;const vs=Ut(ps); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class ys extends ps{}ys.directiveName="unsafeSVG",ys.resultType=2;const ms=Ut(ys),bs=t=>!yt(t)&&"function"==typeof t.then,gs=1073741823; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class ws extends Ht{constructor(){super(...arguments),this.Mt=gs,this.Ut=[],this.dt=new It(this),this.ft=new Dt}render(...t){return t.find((t=>!bs(t)))??F}update(t,s){const i=this.Ut;let e=i.length;this.Ut=s;const n=this.dt,r=this.ft;this.isConnected||this.disconnected();for(let t=0;tthis.Mt);t++){const o=s[t];if(!bs(o))return this.Mt=t,o;t{for(;r.get();)await r.get();const s=n.deref();if(void 0!==s){const i=s.Ut.indexOf(o);i>-1&&i{if(t?.r===$s)return t?._$litStatic$},xs=t=>({_$litStatic$:t,r:$s}),Es=(t,...s)=>({_$litStatic$:s.reduce(((s,i,e)=>s+(t=>{if(void 0!==t._$litStatic$)return t._$litStatic$;throw Error(`Value passed to 'literal' function must be a 'literal' result: ${t}. Use 'unsafeStatic' to pass non-literal values, but\n take care to ensure page security.`)})(i)+t[e+1]),t[0]),r:$s}),Cs=new Map,Ps=t=>(s,...i)=>{const e=i.length;let n,r;const o=[],h=[];let c,l=0,a=!1;for(;l;\n\n/**\n * A single CSSResult, CSSStyleSheet, or an array or nested arrays of those.\n */\nexport type CSSResultGroup = CSSResultOrNative | CSSResultArray;\n\nconst constructionToken = Symbol();\n\nconst cssTagCache = new WeakMap();\n\n/**\n * A container for a string of CSS text, that may be used to create a CSSStyleSheet.\n *\n * CSSResult is the return value of `css`-tagged template literals and\n * `unsafeCSS()`. In order to ensure that CSSResults are only created via the\n * `css` tag and `unsafeCSS()`, CSSResult cannot be constructed directly.\n */\nexport class CSSResult {\n // This property needs to remain unminified.\n ['_$cssResult$'] = true;\n readonly cssText: string;\n private _styleSheet?: CSSStyleSheet;\n private _strings: TemplateStringsArray | undefined;\n\n private constructor(\n cssText: string,\n strings: TemplateStringsArray | undefined,\n safeToken: symbol\n ) {\n if (safeToken !== constructionToken) {\n throw new Error(\n 'CSSResult is not constructable. Use `unsafeCSS` or `css` instead.'\n );\n }\n this.cssText = cssText;\n this._strings = strings;\n }\n\n // This is a getter so that it's lazy. In practice, this means stylesheets\n // are not created until the first element instance is made.\n get styleSheet(): CSSStyleSheet | undefined {\n // If `supportsAdoptingStyleSheets` is true then we assume CSSStyleSheet is\n // constructable.\n let styleSheet = this._styleSheet;\n const strings = this._strings;\n if (supportsAdoptingStyleSheets && styleSheet === undefined) {\n const cacheable = strings !== undefined && strings.length === 1;\n if (cacheable) {\n styleSheet = cssTagCache.get(strings);\n }\n if (styleSheet === undefined) {\n (this._styleSheet = styleSheet = new CSSStyleSheet()).replaceSync(\n this.cssText\n );\n if (cacheable) {\n cssTagCache.set(strings, styleSheet);\n }\n }\n }\n return styleSheet;\n }\n\n toString(): string {\n return this.cssText;\n }\n}\n\ntype ConstructableCSSResult = CSSResult & {\n new (\n cssText: string,\n strings: TemplateStringsArray | undefined,\n safeToken: symbol\n ): CSSResult;\n};\n\nconst textFromCSSResult = (value: CSSResultGroup | number) => {\n // This property needs to remain unminified.\n if ((value as CSSResult)['_$cssResult$'] === true) {\n return (value as CSSResult).cssText;\n } else if (typeof value === 'number') {\n return value;\n } else {\n throw new Error(\n `Value passed to 'css' function must be a 'css' function result: ` +\n `${value}. Use 'unsafeCSS' to pass non-literal values, but take care ` +\n `to ensure page security.`\n );\n }\n};\n\n/**\n * Wrap a value for interpolation in a {@linkcode css} tagged template literal.\n *\n * This is unsafe because untrusted CSS text can be used to phone home\n * or exfiltrate data to an attacker controlled site. Take care to only use\n * this with trusted input.\n */\nexport const unsafeCSS = (value: unknown) =>\n new (CSSResult as ConstructableCSSResult)(\n typeof value === 'string' ? value : String(value),\n undefined,\n constructionToken\n );\n\n/**\n * A template literal tag which can be used with LitElement's\n * {@linkcode LitElement.styles} property to set element styles.\n *\n * For security reasons, only literal string values and number may be used in\n * embedded expressions. To incorporate non-literal values {@linkcode unsafeCSS}\n * may be used inside an expression.\n */\nexport const css = (\n strings: TemplateStringsArray,\n ...values: (CSSResultGroup | number)[]\n): CSSResult => {\n const cssText =\n strings.length === 1\n ? strings[0]\n : values.reduce(\n (acc, v, idx) => acc + textFromCSSResult(v) + strings[idx + 1],\n strings[0]\n );\n return new (CSSResult as ConstructableCSSResult)(\n cssText,\n strings,\n constructionToken\n );\n};\n\n/**\n * Applies the given styles to a `shadowRoot`. When Shadow DOM is\n * available but `adoptedStyleSheets` is not, styles are appended to the\n * `shadowRoot` to [mimic spec behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets).\n * Note, when shimming is used, any styles that are subsequently placed into\n * the shadowRoot should be placed *before* any shimmed adopted styles. This\n * will match spec behavior that gives adopted sheets precedence over styles in\n * shadowRoot.\n */\nexport const adoptStyles = (\n renderRoot: ShadowRoot,\n styles: Array\n) => {\n if (supportsAdoptingStyleSheets) {\n (renderRoot as ShadowRoot).adoptedStyleSheets = styles.map((s) =>\n s instanceof CSSStyleSheet ? s : s.styleSheet!\n );\n } else {\n for (const s of styles) {\n const style = document.createElement('style');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nonce = (global as any)['litNonce'];\n if (nonce !== undefined) {\n style.setAttribute('nonce', nonce);\n }\n style.textContent = (s as CSSResult).cssText;\n renderRoot.appendChild(style);\n }\n }\n};\n\nconst cssResultFromStyleSheet = (sheet: CSSStyleSheet) => {\n let cssText = '';\n for (const rule of sheet.cssRules) {\n cssText += rule.cssText;\n }\n return unsafeCSS(cssText);\n};\n\nexport const getCompatibleStyle =\n supportsAdoptingStyleSheets ||\n (NODE_MODE && global.CSSStyleSheet === undefined)\n ? (s: CSSResultOrNative) => s\n : (s: CSSResultOrNative) =>\n s instanceof CSSStyleSheet ? cssResultFromStyleSheet(s) : s;\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Use this module if you want to create your own base class extending\n * {@link ReactiveElement}.\n * @packageDocumentation\n */\n\nimport {\n getCompatibleStyle,\n adoptStyles,\n CSSResultGroup,\n CSSResultOrNative,\n} from './css-tag.js';\nimport type {\n ReactiveController,\n ReactiveControllerHost,\n} from './reactive-controller.js';\n\n// In the Node build, this import will be injected by Rollup:\n// import {HTMLElement, customElements} from '@lit-labs/ssr-dom-shim';\n\nexport * from './css-tag.js';\nexport type {\n ReactiveController,\n ReactiveControllerHost,\n} from './reactive-controller.js';\n\n/**\n * Removes the `readonly` modifier from properties in the union K.\n *\n * This is a safer way to cast a value to a type with a mutable version of a\n * readonly field, than casting to an interface with the field re-declared\n * because it preserves the type of all the fields and warns on typos.\n */\ntype Mutable = Omit & {\n -readonly [P in keyof Pick]: P extends K ? T[P] : never;\n};\n\n// TODO (justinfagnani): Add `hasOwn` here when we ship ES2022\nconst {\n is,\n defineProperty,\n getOwnPropertyDescriptor,\n getOwnPropertyNames,\n getOwnPropertySymbols,\n getPrototypeOf,\n} = Object;\n\nconst NODE_MODE = false;\n\n// Lets a minifier replace globalThis references with a minified name\nconst global = globalThis;\n\nif (NODE_MODE) {\n global.customElements ??= customElements;\n}\n\nconst DEV_MODE = true;\n\nlet issueWarning: (code: string, warning: string) => void;\n\nconst trustedTypes = (global as unknown as {trustedTypes?: {emptyScript: ''}})\n .trustedTypes;\n\n// Temporary workaround for https://crbug.com/993268\n// Currently, any attribute starting with \"on\" is considered to be a\n// TrustedScript source. Such boolean attributes must be set to the equivalent\n// trusted emptyScript value.\nconst emptyStringForBooleanAttribute = trustedTypes\n ? (trustedTypes.emptyScript as unknown as '')\n : '';\n\nconst polyfillSupport = DEV_MODE\n ? global.reactiveElementPolyfillSupportDevMode\n : global.reactiveElementPolyfillSupport;\n\nif (DEV_MODE) {\n // Ensure warnings are issued only 1x, even if multiple versions of Lit\n // are loaded.\n const issuedWarnings: Set = (global.litIssuedWarnings ??=\n new Set());\n\n // Issue a warning, if we haven't already.\n issueWarning = (code: string, warning: string) => {\n warning += ` See https://lit.dev/msg/${code} for more information.`;\n if (!issuedWarnings.has(warning)) {\n console.warn(warning);\n issuedWarnings.add(warning);\n }\n };\n\n issueWarning(\n 'dev-mode',\n `Lit is in dev mode. Not recommended for production!`\n );\n\n // Issue polyfill support warning.\n if (global.ShadyDOM?.inUse && polyfillSupport === undefined) {\n issueWarning(\n 'polyfill-support-missing',\n `Shadow DOM is being polyfilled via \\`ShadyDOM\\` but ` +\n `the \\`polyfill-support\\` module has not been loaded.`\n );\n }\n}\n\n/**\n * Contains types that are part of the unstable debug API.\n *\n * Everything in this API is not stable and may change or be removed in the future,\n * even on patch releases.\n */\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace ReactiveUnstable {\n /**\n * When Lit is running in dev mode and `window.emitLitDebugLogEvents` is true,\n * we will emit 'lit-debug' events to window, with live details about the update and render\n * lifecycle. These can be useful for writing debug tooling and visualizations.\n *\n * Please be aware that running with window.emitLitDebugLogEvents has performance overhead,\n * making certain operations that are normally very cheap (like a no-op render) much slower,\n * because we must copy data and dispatch events.\n */\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace DebugLog {\n export type Entry = Update;\n export interface Update {\n kind: 'update';\n }\n }\n}\n\ninterface DebugLoggingWindow {\n // Even in dev mode, we generally don't want to emit these events, as that's\n // another level of cost, so only emit them when DEV_MODE is true _and_ when\n // window.emitLitDebugEvents is true.\n emitLitDebugLogEvents?: boolean;\n}\n\n/**\n * Useful for visualizing and logging insights into what the Lit template system is doing.\n *\n * Compiled out of prod mode builds.\n */\nconst debugLogEvent = DEV_MODE\n ? (event: ReactiveUnstable.DebugLog.Entry) => {\n const shouldEmit = (global as unknown as DebugLoggingWindow)\n .emitLitDebugLogEvents;\n if (!shouldEmit) {\n return;\n }\n global.dispatchEvent(\n new CustomEvent('lit-debug', {\n detail: event,\n })\n );\n }\n : undefined;\n\n/*\n * When using Closure Compiler, JSCompiler_renameProperty(property, object) is\n * replaced at compile time by the munged name for object[property]. We cannot\n * alias this function, so we have to use a small shim that has the same\n * behavior when not compiling.\n */\n/*@__INLINE__*/\nconst JSCompiler_renameProperty =

    (\n prop: P,\n _obj: unknown\n): P => prop;\n\n/**\n * Converts property values to and from attribute values.\n */\nexport interface ComplexAttributeConverter {\n /**\n * Called to convert an attribute value to a property\n * value.\n */\n fromAttribute?(value: string | null, type?: TypeHint): Type;\n\n /**\n * Called to convert a property value to an attribute\n * value.\n *\n * It returns unknown instead of string, to be compatible with\n * https://github.com/WICG/trusted-types (and similar efforts).\n */\n toAttribute?(value: Type, type?: TypeHint): unknown;\n}\n\ntype AttributeConverter =\n | ComplexAttributeConverter\n | ((value: string | null, type?: TypeHint) => Type);\n\n/**\n * Defines options for a property accessor.\n */\nexport interface PropertyDeclaration {\n /**\n * When set to `true`, indicates the property is internal private state. The\n * property should not be set by users. When using TypeScript, this property\n * should be marked as `private` or `protected`, and it is also a common\n * practice to use a leading `_` in the name. The property is not added to\n * `observedAttributes`.\n */\n readonly state?: boolean;\n\n /**\n * Indicates how and whether the property becomes an observed attribute.\n * If the value is `false`, the property is not added to `observedAttributes`.\n * If true or absent, the lowercased property name is observed (e.g. `fooBar`\n * becomes `foobar`). If a string, the string value is observed (e.g\n * `attribute: 'foo-bar'`).\n */\n readonly attribute?: boolean | string;\n\n /**\n * Indicates the type of the property. This is used only as a hint for the\n * `converter` to determine how to convert the attribute\n * to/from a property.\n */\n readonly type?: TypeHint;\n\n /**\n * Indicates how to convert the attribute to/from a property. If this value\n * is a function, it is used to convert the attribute value a the property\n * value. If it's an object, it can have keys for `fromAttribute` and\n * `toAttribute`. If no `toAttribute` function is provided and\n * `reflect` is set to `true`, the property value is set directly to the\n * attribute. A default `converter` is used if none is provided; it supports\n * `Boolean`, `String`, `Number`, `Object`, and `Array`. Note,\n * when a property changes and the converter is used to update the attribute,\n * the property is never updated again as a result of the attribute changing,\n * and vice versa.\n */\n readonly converter?: AttributeConverter;\n\n /**\n * Indicates if the property should reflect to an attribute.\n * If `true`, when the property is set, the attribute is set using the\n * attribute name determined according to the rules for the `attribute`\n * property option and the value of the property converted using the rules\n * from the `converter` property option.\n */\n readonly reflect?: boolean;\n\n /**\n * A function that indicates if a property should be considered changed when\n * it is set. The function should take the `newValue` and `oldValue` and\n * return `true` if an update should be requested.\n */\n hasChanged?(value: Type, oldValue: Type): boolean;\n\n /**\n * Indicates whether an accessor will be created for this property. By\n * default, an accessor will be generated for this property that requests an\n * update when set. If this flag is `true`, no accessor will be created, and\n * it will be the user's responsibility to call\n * `this.requestUpdate(propertyName, oldValue)` to request an update when\n * the property changes.\n */\n readonly noAccessor?: boolean;\n\n /**\n * Whether this property is wrapping accessors. This is set by `@property`\n * to control the initial value change and reflection logic.\n *\n * @internal\n */\n wrapped?: boolean;\n}\n\n/**\n * Map of properties to PropertyDeclaration options. For each property an\n * accessor is made, and the property is processed according to the\n * PropertyDeclaration options.\n */\nexport interface PropertyDeclarations {\n readonly [key: string]: PropertyDeclaration;\n}\n\ntype PropertyDeclarationMap = Map;\n\ntype AttributeMap = Map;\n\n/**\n * A Map of property keys to values.\n *\n * Takes an optional type parameter T, which when specified as a non-any,\n * non-unknown type, will make the Map more strongly-typed, associating the map\n * keys with their corresponding value type on T.\n *\n * Use `PropertyValues` when overriding ReactiveElement.update() and\n * other lifecycle methods in order to get stronger type-checking on keys\n * and values.\n */\n// This type is conditional so that if the parameter T is not specified, or\n// is `any`, the type will include `Map`. Since T is not\n// given in the uses of PropertyValues in this file, all uses here fallback to\n// meaning `Map`, but if a developer uses\n// `PropertyValues` (or any other value for T) they will get a\n// strongly-typed Map type.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PropertyValues = T extends object\n ? PropertyValueMap\n : Map;\n\n/**\n * Do not use, instead prefer {@linkcode PropertyValues}.\n */\n// This type must be exported such that JavaScript generated by the Google\n// Closure Compiler can import a type reference.\nexport interface PropertyValueMap extends Map {\n get(k: K): T[K] | undefined;\n set(key: K, value: T[K]): this;\n has(k: K): boolean;\n delete(k: K): boolean;\n}\n\nexport const defaultConverter: ComplexAttributeConverter = {\n toAttribute(value: unknown, type?: unknown): unknown {\n switch (type) {\n case Boolean:\n value = value ? emptyStringForBooleanAttribute : null;\n break;\n case Object:\n case Array:\n // if the value is `null` or `undefined` pass this through\n // to allow removing/no change behavior.\n value = value == null ? value : JSON.stringify(value);\n break;\n }\n return value;\n },\n\n fromAttribute(value: string | null, type?: unknown) {\n let fromValue: unknown = value;\n switch (type) {\n case Boolean:\n fromValue = value !== null;\n break;\n case Number:\n fromValue = value === null ? null : Number(value);\n break;\n case Object:\n case Array:\n // Do *not* generate exception when invalid JSON is set as elements\n // don't normally complain on being mis-configured.\n // TODO(sorvell): Do generate exception in *dev mode*.\n try {\n // Assert to adhere to Bazel's \"must type assert JSON parse\" rule.\n fromValue = JSON.parse(value!) as unknown;\n } catch (e) {\n fromValue = null;\n }\n break;\n }\n return fromValue;\n },\n};\n\nexport interface HasChanged {\n (value: unknown, old: unknown): boolean;\n}\n\n/**\n * Change function that returns true if `value` is different from `oldValue`.\n * This method is used as the default for a property's `hasChanged` function.\n */\nexport const notEqual: HasChanged = (value: unknown, old: unknown): boolean =>\n !is(value, old);\n\nconst defaultPropertyDeclaration: PropertyDeclaration = {\n attribute: true,\n type: String,\n converter: defaultConverter,\n reflect: false,\n hasChanged: notEqual,\n};\n\n/**\n * A string representing one of the supported dev mode warning categories.\n */\nexport type WarningKind =\n | 'change-in-update'\n | 'migration'\n | 'async-perform-update';\n\nexport type Initializer = (element: ReactiveElement) => void;\n\n// Temporary, until google3 is on TypeScript 5.2\ndeclare global {\n interface SymbolConstructor {\n readonly metadata: unique symbol;\n }\n}\n\n// Ensure metadata is enabled. TypeScript does not polyfill\n// Symbol.metadata, so we must ensure that it exists.\n(Symbol as {metadata: symbol}).metadata ??= Symbol('metadata');\n\ndeclare global {\n // This is public global API, do not change!\n // eslint-disable-next-line no-var\n var litPropertyMetadata: WeakMap<\n object,\n Map\n >;\n}\n\n// Map from a class's metadata object to property options\n// Note that we must use nullish-coalescing assignment so that we only use one\n// map even if we load multiple version of this module.\nglobal.litPropertyMetadata ??= new WeakMap<\n object,\n Map\n>();\n\n/**\n * Base element class which manages element properties and attributes. When\n * properties change, the `update` method is asynchronously called. This method\n * should be supplied by subclasses to render updates as desired.\n * @noInheritDoc\n */\nexport abstract class ReactiveElement\n // In the Node build, this `extends` clause will be substituted with\n // `(globalThis.HTMLElement ?? HTMLElement)`.\n //\n // This way, we will first prefer any global `HTMLElement` polyfill that the\n // user has assigned, and then fall back to the `HTMLElement` shim which has\n // been imported (see note at the top of this file about how this import is\n // generated by Rollup). Note that the `HTMLElement` variable has been\n // shadowed by this import, so it no longer refers to the global.\n extends HTMLElement\n implements ReactiveControllerHost\n{\n // Note: these are patched in only in DEV_MODE.\n /**\n * Read or set all the enabled warning categories for this class.\n *\n * This property is only used in development builds.\n *\n * @nocollapse\n * @category dev-mode\n */\n static enabledWarnings?: WarningKind[];\n\n /**\n * Enable the given warning category for this class.\n *\n * This method only exists in development builds, so it should be accessed\n * with a guard like:\n *\n * ```ts\n * // Enable for all ReactiveElement subclasses\n * ReactiveElement.enableWarning?.('migration');\n *\n * // Enable for only MyElement and subclasses\n * MyElement.enableWarning?.('migration');\n * ```\n *\n * @nocollapse\n * @category dev-mode\n */\n static enableWarning?: (warningKind: WarningKind) => void;\n\n /**\n * Disable the given warning category for this class.\n *\n * This method only exists in development builds, so it should be accessed\n * with a guard like:\n *\n * ```ts\n * // Disable for all ReactiveElement subclasses\n * ReactiveElement.disableWarning?.('migration');\n *\n * // Disable for only MyElement and subclasses\n * MyElement.disableWarning?.('migration');\n * ```\n *\n * @nocollapse\n * @category dev-mode\n */\n static disableWarning?: (warningKind: WarningKind) => void;\n\n /**\n * Adds an initializer function to the class that is called during instance\n * construction.\n *\n * This is useful for code that runs against a `ReactiveElement`\n * subclass, such as a decorator, that needs to do work for each\n * instance, such as setting up a `ReactiveController`.\n *\n * ```ts\n * const myDecorator = (target: typeof ReactiveElement, key: string) => {\n * target.addInitializer((instance: ReactiveElement) => {\n * // This is run during construction of the element\n * new MyController(instance);\n * });\n * }\n * ```\n *\n * Decorating a field will then cause each instance to run an initializer\n * that adds a controller:\n *\n * ```ts\n * class MyElement extends LitElement {\n * @myDecorator foo;\n * }\n * ```\n *\n * Initializers are stored per-constructor. Adding an initializer to a\n * subclass does not add it to a superclass. Since initializers are run in\n * constructors, initializers will run in order of the class hierarchy,\n * starting with superclasses and progressing to the instance's class.\n *\n * @nocollapse\n */\n static addInitializer(initializer: Initializer) {\n this.__prepare();\n (this._initializers ??= []).push(initializer);\n }\n\n static _initializers?: Initializer[];\n\n /*\n * Due to closure compiler ES6 compilation bugs, @nocollapse is required on\n * all static methods and properties with initializers. Reference:\n * - https://github.com/google/closure-compiler/issues/1776\n */\n\n /**\n * Maps attribute names to properties; for example `foobar` attribute to\n * `fooBar` property. Created lazily on user subclasses when finalizing the\n * class.\n * @nocollapse\n */\n private static __attributeToPropertyMap: AttributeMap;\n\n /**\n * Marks class as having been finalized, which includes creating properties\n * from `static properties`, but does *not* include all properties created\n * from decorators.\n * @nocollapse\n */\n protected static finalized: true | undefined;\n\n /**\n * Memoized list of all element properties, including any superclass\n * properties. Created lazily on user subclasses when finalizing the class.\n *\n * @nocollapse\n * @category properties\n */\n static elementProperties: PropertyDeclarationMap;\n\n /**\n * User-supplied object that maps property names to `PropertyDeclaration`\n * objects containing options for configuring reactive properties. When\n * a reactive property is set the element will update and render.\n *\n * By default properties are public fields, and as such, they should be\n * considered as primarily settable by element users, either via attribute or\n * the property itself.\n *\n * Generally, properties that are changed by the element should be private or\n * protected fields and should use the `state: true` option. Properties\n * marked as `state` do not reflect from the corresponding attribute\n *\n * However, sometimes element code does need to set a public property. This\n * should typically only be done in response to user interaction, and an event\n * should be fired informing the user; for example, a checkbox sets its\n * `checked` property when clicked and fires a `changed` event. Mutating\n * public properties should typically not be done for non-primitive (object or\n * array) properties. In other cases when an element needs to manage state, a\n * private property set with the `state: true` option should be used. When\n * needed, state properties can be initialized via public properties to\n * facilitate complex interactions.\n * @nocollapse\n * @category properties\n */\n static properties: PropertyDeclarations;\n\n /**\n * Memoized list of all element styles.\n * Created lazily on user subclasses when finalizing the class.\n * @nocollapse\n * @category styles\n */\n static elementStyles: Array = [];\n\n /**\n * Array of styles to apply to the element. The styles should be defined\n * using the {@linkcode css} tag function, via constructible stylesheets, or\n * imported from native CSS module scripts.\n *\n * Note on Content Security Policy:\n *\n * Element styles are implemented with ` -

    - `; - - let content; - switch (this.tab) { - case 'map': - content = html`
    `; - break; - case 'news': - content = this.render_news(); - break; - case 'friends': - content = html`
    Friends
    `; - break; - case 'store': - content = this.render_store(); - break; - } - - return html` - - -
    - ${header} -
    ${content}
    - ${navigation} -
    - `; - } -} -customElements.define('gg-app', GgAppElement); \ No newline at end of file diff --git a/apps/gg/strava.js b/apps/gg/strava.js deleted file mode 100644 index 8c78b412..00000000 --- a/apps/gg/strava.js +++ /dev/null @@ -1,20 +0,0 @@ -const k_client_id = '28276'; -const k_client_secret = '3123f1f5afe132d9731111066d1d17bdb22ef27e'; -const k_access_token = 'f753e77764c26252bd2d80e7c5cc17ace51a8864'; -const k_refresh_token = 'f58d8e1b5a3ec3bf96e681589d5014f9a294f5a4'; -const k_redirect_url = 'https://tildefriends.net/~cory/gg/login'; - -export async function refresh_token(token) { - let r = await fetch('https://www.strava.com/api/v3/oauth/token', { - method: 'POST', - body: `client_id=${k_client_id}&client_secret=${k_client_secret}&refresh_token=${token.refresh_token}&grant_type=refresh_token`, - }); - return r?.body ? JSON.parse(utf8Decode(r.body)) : undefined; -} - -export async function authorization_code(code) { - return await fetch('https://www.strava.com/api/v3/oauth/token', { - method: 'POST', - body: `client_id=${k_client_id}&client_secret=${k_client_secret}&code=${code}&grant_type=authorization_code`, - }); -} \ No newline at end of file diff --git a/apps/identity.json b/apps/identity.json index 91328bcb..a4c28e41 100644 --- a/apps/identity.json +++ b/apps/identity.json @@ -1,5 +1,5 @@ { - "type": "tildefriends-app", - "emoji": "๐Ÿชช", - "previous": "&kgukkyDk1RxgfzgMH6H/0QeDPIuwPZypLuAFax21ljk=.sha256" -} \ No newline at end of file + "type": "tildefriends-app", + "emoji": "๐Ÿชช", + "previous": "&kgukkyDk1RxgfzgMH6H/0QeDPIuwPZypLuAFax21ljk=.sha256" +} diff --git a/apps/identity/app.js b/apps/identity/app.js index 2aa50363..a56e5572 100644 --- a/apps/identity/app.js +++ b/apps/identity/app.js @@ -18,7 +18,8 @@ tfrpc.register(async function reload() { async function main() { let ids = await ssb.getIdentities(); - await app.setDocument(` + await app.setDocument( + ` + + - \ No newline at end of file + diff --git a/apps/issues/script.js b/apps/issues/script.js index 1c9c2d32..cdb37b91 100644 --- a/apps/issues/script.js +++ b/apps/issues/script.js @@ -31,7 +31,12 @@ class TfIdPickerElement extends LitElement { if (this.ids) { return html` `; } else { @@ -57,13 +62,15 @@ class TfComposeElement extends LitElement { } submit() { - this.dispatchEvent(new CustomEvent('tf-submit', { - bubbles: true, - composed: true, - detail: { - value: this.renderRoot.getElementById('input').value, - }, - })); + this.dispatchEvent( + new CustomEvent('tf-submit', { + bubbles: true, + composed: true, + detail: { + value: this.renderRoot.getElementById('input').value, + }, + }) + ); this.renderRoot.getElementById('input').value = ''; this.input(); } @@ -96,7 +103,8 @@ class TfIssuesAppElement extends LitElement { async load() { let issues = {}; - let messages = await tfrpc.rpc.query(` + let messages = await tfrpc.rpc.query( + ` WITH issues AS (SELECT messages.* FROM messages_refs JOIN messages ON messages.id = messages_refs.message WHERE messages_refs.ref = ? AND json_extract(messages.content, '$.type') = 'issue'), @@ -107,7 +115,9 @@ class TfIssuesAppElement extends LitElement { SELECT * FROM issues UNION SELECT * FROM edits ORDER BY timestamp - `, [k_project]); + `, + [k_project] + ); for (let message of messages) { let content = JSON.parse(message.content); switch (content.type) { @@ -123,7 +133,7 @@ class TfIssuesAppElement extends LitElement { break; case 'issue-edit': case 'post': - for (let issue of (content.issues || [])) { + for (let issue of content.issues || []) { if (issues[issue.link]) { if (issue.open !== undefined) { issues[issue.link].open = issue.open; @@ -136,7 +146,9 @@ class TfIssuesAppElement extends LitElement { break; } } - this.issues = Object.values(issues).sort((x, y) => (y.open - x.open) || (y.created - x.created)); + this.issues = Object.values(issues).sort( + (x, y) => y.open - x.open || y.created - x.created + ); if (this.selected) { for (let issue of this.issues) { if (issue.id == this.selected.id) { @@ -150,11 +162,20 @@ class TfIssuesAppElement extends LitElement { return html` ${issue.open ? 'โ˜ open' : 'โ˜‘ closed'} - ${issue.author} - this.selected = issue}> + + ${issue.author} + + (this.selected = issue)} + > ${issue.text.split('\n')?.[0]} - ${new Date(issue.updated ?? issue.created).toLocaleDateString()} + + ${new Date(issue.updated ?? issue.created).toLocaleDateString()} + `; } @@ -170,13 +191,21 @@ class TfIssuesAppElement extends LitElement {
    ${new Date(update.timestamp).toLocaleString()}
    ${update.author}
    ${message}
    -
    ${update.open !== undefined ? (update.open ? 'issue opened' : 'issue closed') : undefined}
    +
    + ${update.open !== undefined + ? update.open + ? 'issue opened' + : 'issue closed' + : undefined} +
    `; } async set_open(id, open) { - if (confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`)) { + if ( + confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`) + ) { let whoami = this.shadowRoot.getElementById('picker').selected; await tfrpc.rpc.appendMessage(whoami, { type: 'issue-edit', @@ -207,7 +236,9 @@ class TfIssuesAppElement extends LitElement { type: 'post', text: event.detail.value, root: this.selected.id, - branch: this.selected.updates.length ? this.selected.updates[this.selected.updates.length - 1].id : this.selected.id, + branch: this.selected.updates.length + ? this.selected.updates[this.selected.updates.length - 1].id + : this.selected.id, issues: [ { link: this.selected.id, @@ -226,16 +257,18 @@ class TfIssuesAppElement extends LitElement { return html` ${header}
    - this.selected = undefined}> - ${this.selected.open ? - html` this.set_open(this.selected.id, false)}>` : - html` this.set_open(this.selected.id, true)}>`} + (this.selected = undefined)}> + ${ + this.selected.open + ? html` this.set_open(this.selected.id, false)}>` + : html` this.set_open(this.selected.id, true)}>` + }
    ${new Date(this.selected.created).toLocaleString()}
    ${this.selected.author}
    ${this.selected.id}
    ${unsafeHTML(tfutils.markdown(this.selected.text))}
    - ${this.selected.updates.map(x => this.render_update(x))} + ${this.selected.updates.map((x) => this.render_update(x))} `; } else { @@ -250,11 +283,11 @@ class TfIssuesAppElement extends LitElement { Title Date - ${this.issues.map(x => this.render_issue_table_row(x))} + ${this.issues.map((x) => this.render_issue_table_row(x))} `; } } } -customElements.define('tf-issues-app', TfIssuesAppElement); \ No newline at end of file +customElements.define('tf-issues-app', TfIssuesAppElement); diff --git a/apps/issues/tf-utils.js b/apps/issues/tf-utils.js index a1c7666a..444e2a93 100644 --- a/apps/issues/tf-utils.js +++ b/apps/issues/tf-utils.js @@ -1,20 +1,32 @@ import * as linkify from './commonmark-linkify.js'; function image(node, entering) { - if (node.firstChild?.type === 'text' && - node.firstChild.literal.startsWith('video:')) { + if ( + node.firstChild?.type === 'text' && + node.firstChild.literal.startsWith('video:') + ) { if (entering) { - this.lit('