diff --git a/.gitignore b/.gitignore index 05fbcdb00..047772fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,4 @@ build/ /application.yaml /application.properties .Rproj.user +site/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db7c51427..bc11d35ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,195 +1,3 @@ # Contributing to Armadillo - -## Running Armadillo from source code - -You can run from source code as follows: - -1. Install Java and Docker -2. Checkout the source using `git clone https://github.com/molgenis/molgenis-service-armadillo.git` -3. Optionally copy `application.template.yml` to `application.yml` to change settings (will be .gitignored) -4. Compile and execute the code using `./gradlew run` - -Note: contact MOLGENIS team if you want to contribute and need a testing OIDC config that you can run against localhost. - -# Developing Armadillo - -We use gradle to build: - -## Running locally - -```bash -./gradlew run -``` - -As we now have the option to download the logfile from the application we need to have one to begin with: - -```bash -# Leave out the -a to overwrite instead of append -./gradlew run | tee -a logs/armadillo.log -``` - -## Running tests - -```bash -./gradlew test -``` - -## Upgrade plugins - -Use the commands listed below. - -```bash -./gradlew useLatestVersions -./gradlew useLatestVersionsCheck -``` - -Some plugins need manually editing the build files. - -To help find the right `build.gradle` use the command below with your keyword. - -```bash -find . -type f -name "build.gradle" -exec echo {} \; -exec grep YOUR_KEY_WORD {} \; -``` - -## Upgrading gradle - -```bash -./gradlew wrapper --gradle-version 8.6 -``` - -## Check for updates - -To get a list of new dependencies run - -```bash -./gradlew dependencyUpdates -``` - -## Tools - -We use intellij to develop -* To run or debug in intellij, right click on armadillo/src/main/java/org.molgenis.armdadillo/ArmadilloServiceAppliction and choose 'Run/Debug Armadillo...' -* To run using oidc, create a copy of [application.yml](application.template.yml) in root of your project - -We have a swagger-ui to quickly see and test available web services at http://localhost:8080/swagger-ui/ - -## Components - -We have several components - -- [Armadillo](./armadillo/src/) source -- [UI](./ui/README.md) readme -- [R](./r/) java integration source -- [docker](./docker/README.md) - - [ci](./docker/ci/README.md) -- [scripts](./scripts/README.md) migration - - [install](./scripts/install/README.md) - - [release](./scripts/release/README.md) - - [ops](./scripts/ops/README.md) - - [upgrade](./scripts/upgrade/README.md) - -## Releasing - -Releases are done whenever the version number gets bumped. For more information see the `Commit messages and versioning (Major, Minor, Patch updates)` section below. - -We use mooltiverse [Nyx](https://mooltiverse.github.io/nyx/guide/user/introduction/how-nyx-works/) for changelog and publishing to github. - -Run `./gradlew tasks --group Release` to see all release tasks - -Use `./gradlew nyxMake` to see what is build in [build/distributions](./build/distributions/). - -### Commit messages and versioning (Major, Minor, Patch updates) - -Versionnumbers are updated according to [Semantic versioning](https://semver.org/), using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). - -Please be aware that new releases will only be done when one of the above prefixes is used. - -Other prefixes do not indicate user-facing changes and will therefore not result in a version bump, consequently not resulting in a new (pre) release. - -Each commit with `!` just before the colon `:` is a major update, indicating a breaking change. So use it wisely. You can also add `BREAKING CHANGE:` in the long commit message format. - -- Use `feat!: ...` or `fix!: ...` for a major upgrade, indicating a breaking change. -- Use `feat: ...` for a minor upgrade, indicating a new feature. -- Use `fix: ...` for a patch update, indicating a bugfix. - -### Checking log messages - -As [changelog template](./changelog-notes.tpl) uses the commit message it is good to check their quality. - -List messages to see usage of conventional commits from the past. - -```sh -# How many colon usages -git log --pretty=format:"%s" | cut -d: -f1 | sort | uniq -c | sort -n -``` - -### Building locally - -```sh -./gradlew clean assemble -``` - -## Continuous integration - -- We test on each PR and merges on master -- We build docker compose set for CI testing and demo purposes. - - [CI testing](./docker/ci/README.md) - - Demo zip file is a delivery you as artifact - - Master build have a armadillo-compose.zip for download - -### Local CI build - -``` -./gradlew docker -./docker/bin/prepare.bash ci - -cd build/docker/armadillo-compose -# Follow README.md to see Armadillo and R images run in container -docker compose build -docker compose up -``` - -then run `./release-test.R` against this. - -### Local CI test of armadillo-compose - -Follow [docker CI README.md](./docker/ci/README.md) to run `release-test.R` using `molgenis/r-cicd` image - -## Profile xenon with resourcer whitelisted returns a host.docker.internal error -When developing locally, it might be possible to come across the container error: `Could not resolve host: host.docker.internal`, -especially when developing on a non-supported operating system when resourcer is whitelisted (such as xenon). -Sadly, the only way around this error is to edit the JAVA source code of Armadillo to include starting with an extra host. -To enable this feature, you must edit the private method `installImage` of [DockerService.java](https://github.com/molgenis/molgenis-service-armadillo/blob/master/armadillo/src/main/java/org/molgenis/armadillo/profile/DockerService.java) `CreateContainerCmd cmd` from `.withHostConfig(new HostConfig().withPortBindings(portBindings))` to `.withHostConfig(new HostConfig().withPortBindings(portBindings).withExtraHosts("host.docker.internal:host-gateway"))`. - -Please note that in order for this change to work, you must use Intellij to run Armadillo or compile the new source code. -Also, if you already have a xenon container build and running, stop and remove that container. - -# Developing DataSHIELD packages in Armadillo -As package developer will want to push your new packages into a DataSHIELD profile - -* You can start Armadillo with defaults as described above; then use admin/admin as authentication -* to see what profile are available and has been selected: -``` -curl -u admin:admin http://localhost:8080/profiles -``` -* to change selected profile 'my-profile': -``` -curl -X POST http://localhost:8080/select-profile \ - -H 'Content-Type: application/json' \ - -d 'default' -``` -* to install-packages in DataSHIELD current using admin user: -``` -curl -u admin:admin -v \ --H 'Content-Type: multipart/form-data' \ --F "file=@dsBase_6.3.0.tar.gz" \ --X POST http://localhost:8080/install-package -``` -* to update whitelist of your current profile: -``` -curl -u admin:admin -X POST http://localhost:8080/whitelist/dsBase -``` -* to get whitelist of current profile: -``` -curl -u admin:admin http://localhost:8080/whitelist -``` +We really appreciate anyone wanting to contribute to Armadillo. For more information on doing so, visit our +[Developer guides](https://molgenis.github.io/molgenis-service-armadillo/pages/dev_guides/) \ No newline at end of file diff --git a/README.md b/README.md index b040eb0af..751a300e9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ # Overview -Use MOLGENIS/Armadillo to make data available for privacy protecting federated analysis using [DataSHIELD](https://datashield.org) protocol. Armadillo +Use MOLGENIS Armadillo to make data available for privacy protecting federated analysis using [DataSHIELD](https://datashield.org) protocol. Armadillo service provides the following features: * **manage data projects**. Projects can either hold tabular data in the efficient 'parquet' format or any other file use DataSHIELD 'resources' framework. @@ -16,11 +16,28 @@ service provides the following features: * **configure DataSHIELD analysis profiles**. [DataSHIELD analysis profiles](https://www.datashield.org/help/standard-profiles-and-plaforms) are Docker images that contain a collection of multiple [DataSHIELD analysis packages](https://www.datashield.org/help/community-packages). -![DataSHIELD overview](https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/docs/img/overview-datashield.png) +![DataSHIELD overview](docs/img/ds-complete-setup.png) ## Getting started -For installing and using Armadillo see https://molgenis.github.io/molgenis-service-armadillo/#/ +For installing and using Armadillo see our +[Documentation](https://molgenis.github.io/molgenis-service-armadillo/pages/quick_start/). -For developing and contributing see [Contributing](./CONTRIBUTING.md). +For developing and contributing see [our dev guides](https://molgenis.github.io/molgenis-service-armadillo/pages/dev_guides/). +### Quick start +#### Jar +1. Download the jar from our +[releases page](https://github.com/molgenis/molgenis-service-armadillo/releases). +2. Copy paste the contents of +[application-template.yml](https://github.com/molgenis/molgenis-service-armadillo/blob/master/application.template.yml) +and paste it in a file called `application.yml`, in the same folder as the downloaded jar. +3. To start the application, run `java -jar molgenis-armadillo-x.yy.zz.jar`. +4. Go to `http://localhost:8080` to see the Armadillo UI. + +#### Docker +For testing without having to installing Java you can run using docker: +1. Install [docker-compose](https://docs.docker.com/compose/install/) +2. Download this [docker-compose.yml](docker-compose.yml). +3. Execute `docker-compose up` +4. Once it says 'Started', go to http://localhost:8080 to see your Armadillo running. diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/DataSHIELD-datamanagement/0_Prerequisite.r b/docs/DataSHIELD-datamanagement/0_Prerequisite.r deleted file mode 100644 index fc5d85efd..000000000 --- a/docs/DataSHIELD-datamanagement/0_Prerequisite.r +++ /dev/null @@ -1,19 +0,0 @@ -# install the following packages -install.packages("MolgenisArmadillo") -install.packages("dplyr") -install.packages("arrow") -# After installation make sure you can load the libraries -library(MolgenisArmadillo) -library(dplyr) -library(arrow) -# verify the loaded libraries (see: other attached packages) -sessionInfo() - -# optional, install the following packages to be able to run a simple analyses -# 3_Analyse_data_subset_DSMolgenisArmadillo.r -install.packages("DSI") -install.packages("DSMolgenisArmadillo") -install.packages("dsBaseClient", repos = c("http://cran.datashield.org", "https://cloud.r-project.org/"), dependencies = TRUE) - -library(dsBaseClient) -library(DSMolgenisArmadillo) diff --git a/docs/DataSHIELD-datamanagement/1_MolgenisArmadillo.r b/docs/DataSHIELD-datamanagement/1_MolgenisArmadillo.r deleted file mode 100644 index 7b36b2722..000000000 --- a/docs/DataSHIELD-datamanagement/1_MolgenisArmadillo.r +++ /dev/null @@ -1,118 +0,0 @@ -## Prerequisites -# -# - Run the code in 0_Prerequisite.r -# -library(MolgenisArmadillo) - -## -# To share your data using Armadillo, you first need to login - -## Login -# -# In order to access the files as a data manager, you need to log in. -# The login method needs the URLs of the Armadillo server and the MinIO file server. -# It will open a browser window where you can identify yourself with the ID provider. -# -# If you are unsure how this login function (or any function) works ask R to provide documentation -?armadillo.login - -armadillo.login( - armadillo = "https://armadillo.test.molgenis.org" -) -#token <- armadillo.get_token("https://armadillo.test.molgenis.org") - -# armadillo.login will create a session and store the credentials in the environment. - -## Structure -# To share data via Armadillo you can have a nested structure to save you data. - -# We distinguish: -# - projects -# - folders -# - tables - -### Projects -# Projects are root-folders you can give persons permissions on. -# you can imagine that you will use a separate project for each study you need to support. -# This way you make sure people can not see each others variables. - -### Folders -# Folder objects can be used to version the different tables you want to share in Armadillo. -# This is not mandatory and are free to use the folder level as you see fit. -# In our examples we will go into the versioning part a bit deeper. - -### Tables -# Tables contain the data you want to share. -# This can be all the data on a certain subject, mostly used in consortia or a specific study you want to expose. - -## Sharing data -# Assume you are in a consortia which has core-variables and outcome-variables. -# You want to share and version the whole dataset to all researchers which applied to access your data. - -# First we will create the project. In our case "ipec". - -cohort <- "workshop2" -armadillo.create_project(cohort) - -# Secondly we will load the table(s) we want to upload to Armadillo in the R-environment. -# We have test data which is in `arrow` format, the upload will take any object that has a table like structure to upload into the Armadillo. -# This can be SPSS, STATA, SAS or R-based data as well. - -library(arrow) - -# load the core data (make sure you are working in the correct directory) -nonrep <- arrow::read_parquet("data/nonrep.parquet") -yearlyrep <- arrow::read_parquet("data/yearlyrep.parquet") - -# explore your data (sanity check) -View(nonrep) -dim(nonrep) -names(nonrep) -table(nonrep$recruit_age) - -# The third step is determining the second level, which contains in this case the datamodel-version the type of variables and the data-version. - -# y_y-#variable-type#-x_x - -# y_y = datamodel version -# x_x = data version - -# upload the core variables -armadillo.upload_table(cohort, "2_1-core-1_0", nonrep) -armadillo.upload_table(cohort, "2_1-core-1_0", yearlyrep) - -## Looking at the data -# There are helper functions to help you determine what is in the storage server. -# You can list projects and tables to what's in the storage. - -# list of projects -armadillo.list_projects() - -# listing tables per project -armadillo.list_tables(cohort) - -# You can download the data in the R-environment as well. - -# download table to local R environment -download_nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") - -# check the column names from the local environment -colnames(download_nonrep) - -# check if local data and uploaded data are equal (optional) -setequal(nonrep, download_nonrep) - -# Now you can also take a look at the files in the user interface of the MinIO file server -# open the MinIO server URL in a browser window (used in armadillo.login). - -# > !IMPORTANT: run this part after subsetting the data - -## Deleting the data -# To delete the data you need to throw away the contents first. - -# throw away the core tables -armadillo.delete_table(cohort, "2_1-core-1_0", "nonrep") -armadillo.delete_table(cohort, "2_1-core-1_0", "yearlyrep") - -# Now you can delete the project. -armadillo.delete_project(cohort) diff --git a/docs/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r b/docs/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r deleted file mode 100644 index f9ba1bf7f..000000000 --- a/docs/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r +++ /dev/null @@ -1,95 +0,0 @@ -## Creating data subsets on the Armadillo MiNIO file server -# When researchers request access to your data they may in many cases not be granted access to the whole data set, -# but only to a subset. On the MinIO file server access is regulated on the project level, -# so you will need to create a new project using a subset of the data. -# Here you can see the different relevant steps you need to take to create these subsets. -# -# - Setting up the environment -# - Logging into the servers -# - Exploring the data -# - Making subsets of the data -# - Upload data subsets -# -# - Delete data subsets - -## Setting up the environment -# - Run the code in 0_Prerequisite.r - -# load required libraries -library(MolgenisArmadillo) -library(dplyr) - -## Logging in to the servers -# In order to access the files on the MinIO fileserver you need to log in using the URLs of the Armadillo server and the MinIO fileserver. -# A browser window will be opened where you can identify yourself with the ID provider. - -armadillo.login( - armadillo = "https://armadillo.test.molgenis.org" -) -# A session will be created and the credentials are stored in the environment. - -## Explore the data -# Let's assume you are in a consortium which has core-variables and outcome-variables. -# You want to share a subset of the whole data set with certain researchers that applied for access to your data. - -# List projects on the Armadillo server. -armadillo.list_projects() - -# Next create a study, here called 'subset1'. -# NOTE: change the name of the subset for your own practice run - -study <- "study1" -armadillo.create_project(study) - -# List the tables in a project - -# You want to share data from the cohort you just uploaded (1_MolgenisArmadillo.r). -# Change the cohort_name to your own cohort name here: -cohort = "workshop1" - -# List the available tables within this project. -armadillo.list_tables(cohort) - -# Subset the core variables -# Download the relevant core tables to the local environment - -nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") -yearlyrep <- armadillo.load_table(cohort, "2_1-core-1_0", "yearlyrep") - -# List their variables - -colnames(nonrep) -colnames(yearlyrep) - -# Subset the variables that were requested per table. - -subset_core_nonrep <- nonrep %>% select(child_id, asthma_m, preg_cig, preg_fever, preg_alc) -subset_core_yearlyrep <- yearlyrep %>% select(child_id, cohab_, smk_exp) - -## Uploading the data subset -# Check the variables in the data subset before uploading - -colnames(subset_core_nonrep) -colnames(subset_core_yearlyrep) - -# Upload the data subsets -armadillo.upload_table(study, "2_1-core-1_0", subset_core_nonrep, "nonrep") -armadillo.upload_table(study, "2_1-core-1_0", subset_core_yearlyrep, "yearlyrep") - -# See if tables are uploaded -armadillo.list_tables(study) - -# Now you can also take a look at the files in the user interface of the Armadillo server. In this case: https://armadillo.test.molgenis.org/#/projects - -# > !IMPORTANT: run this part after subsetting the data - -## Deleting the data -# To delete the data you need to throw away the contents first. - -# throw away the core tables -armadillo.delete_table(study, "2_1-core-1_0", "nonrep") -armadillo.delete_table(study, "2_1-core-1_0", "yearlyrep") - -# Now you can delete the project. - -armadillo.delete_project(study) diff --git a/docs/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r b/docs/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r deleted file mode 100644 index f29be2a6d..000000000 --- a/docs/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r +++ /dev/null @@ -1,39 +0,0 @@ -install.packages("DSI") -install.packages("DSMolgenisArmadillo") -install.packages("dsBaseClient", repos = c("http://cran.datashield.org", "https://cloud.r-project.org/"), dependencies = TRUE) - -library(dsBaseClient) -library(DSMolgenisArmadillo) - -# specify server url -armadillo_url <- "https://armadillo.test.molgenis.org" - -# get token from central authentication server -token <- armadillo.get_token(armadillo_url) - - -# build the login dataframe -builder <- DSI::newDSLoginBuilder() -builder$append( - server = "armadillo", - url = armadillo_url, - token = token, - table = "workshop1/2_1-core-1_0/nonrep", - driver = "ArmadilloDriver" -) - -# create loginframe -logindata <- builder$build() - -# login into server -conns <- datashield.login( - logins = logindata, - symbol = "core_nonrep", - variables = c("coh_country"), - assign = TRUE -) - -# calculate the mean -ds.mean("core_nonrep$coh_country", datasources = conns) - -ds.histogram(x = "core_nonrep$coh_country", datasources = conns) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 261e81189..000000000 --- a/docs/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Overview - -Use MOLGENIS/Armadillo to make data available for privacy protecting federated analysis using [DataSHIELD](https://datashield.org) protocol. Armadillo -service provides the following features: -* **manage data projects**. Projects can either hold tabular data in the efficient 'parquet' format or any other file using the DataSHIELD - 'resources' framework. -* **grant users access permission**. We use a central OIDC service like KeyCloak or FusionAuth in combination with a trused identity provider like - Life Sciences AAI to authenticate users. -* **configure DataSHIELD analysis profiles**. [DataSHIELD analysis profiles](https://www.datashield.org/help/standard-profiles-and-plaforms) are - Docker images that contain a collection of multiple [DataSHIELD analysis packages](https://www.datashield.org/help/community-packages). - -![DataSHIELD overview](./img/overview-datashield.png) - -## Older versions - - diff --git a/docs/_cdns/README.md b/docs/_cdns/README.md deleted file mode 100644 index 9f3ae871a..000000000 --- a/docs/_cdns/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Get rid of CDNs - -We need to protect our reseachers and make the docs more performant. - -- `mkdir _cdns` -- Check index.html for external links -- fetch all external references - -> When adding a new docs tree ie `v3.2.0` make sure to follow these same/similar steps - -```bash -wget cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css -wget cdn.jsdelivr.net/npm/docsify@4 --output-document=docsify@4.js -wget cdn.jsdelivr.net/npm/docsify-sidebar-collapse/dist/docsify-sidebar-collapse.min.js -wget unpkg.com/docsify/lib/plugins/search.min.js -``` - -- scan files for external links and change those. -- `vue.css` has link to fonts list - -```bash -wget https://fonts.googleapis.com/css\?family\=Roboto+Mono\|Source+Sans+Pro:300,400,600 --output-document=fonts.css -``` - -- fetch the fonts now listed in `fonts.css` -- fetch those fonts in `./fonts/` - -```bash -wget https://fonts.gstatic.com/s/robotomono/v23/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf -wget https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf -wget https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf -wget https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf -``` - -- Adjust the link to the downloaded fonts - -```bash -vi fonts.css -# replace all paths ie -# https://fonts.gstatic.com/s/sourcesanspro/v22/xxx.ttf -# into -# ./fonts/xxx.ttf -``` diff --git a/docs/_cdns/docsify-sidebar-collapse.min.js b/docs/_cdns/docsify-sidebar-collapse.min.js deleted file mode 100644 index 2b067c7e2..000000000 --- a/docs/_cdns/docsify-sidebar-collapse.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){("object"!=typeof exports||"undefined"==typeof module)&&"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";function e(e,n){var t,a=(n=void 0===n?{}:n).insertAt;e&&"undefined"!=typeof document&&(t=document.head||document.getElementsByTagName("head")[0],(n=document.createElement("style")).type="text/css","top"===a&&t.firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e)))}var t;function a(e){e&&null!=t&&(e=e.getBoundingClientRect().top,document.querySelector(".sidebar").scrollBy(0,e-t))}function n(){requestAnimationFrame(function(){var e=document.querySelector(".app-sub-sidebar > .active");if(e)for(e.parentNode.parentNode.querySelectorAll(".app-sub-sidebar").forEach(function(e){return e.classList.remove("open")});e.parentNode.classList.contains("app-sub-sidebar")&&!e.parentNode.classList.contains("open");)e.parentNode.classList.add("open"),e=e.parentNode})}function o(e){t=e.target.getBoundingClientRect().top;var n=d(e.target,"LI",2);n&&(n.classList.contains("open")?(n.classList.remove("open"),setTimeout(function(){n.classList.add("collapse")},0)):(function(e){if(e)for(e.classList.remove("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.remove("open"),e=e.parentNode}(s()),i(n),setTimeout(function(){n.classList.remove("collapse")},0)),a(n))}function s(){var e=document.querySelector(".sidebar-nav .active");return e||(e=d(document.querySelector('.sidebar-nav a[href="'.concat(decodeURIComponent(location.hash).replace(/ /gi,"%20"),'"]')),"LI",2))&&e.classList.add("active"),e}function i(e){if(e)for(e.classList.add("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.add("open"),e=e.parentNode}function d(e,n,t){if(e&&e.tagName===n)return e;for(var a=0;e;){if(t<++a)return;if(e.parentNode.tagName===n)return e.parentNode;e=e.parentNode}}e(".sidebar-nav > ul > li ul {\n display: none;\n}\n\n.app-sub-sidebar {\n display: none;\n}\n\n.app-sub-sidebar.open {\n display: block;\n}\n\n.sidebar-nav .open > ul:not(.app-sub-sidebar),\n.sidebar-nav .active:not(.collapse) > ul {\n display: block;\n}\n\n/* 抖动 */\n.sidebar-nav li.open:not(.collapse) > ul {\n display: block;\n}\n\n.active + ul.app-sub-sidebar {\n display: block;\n}\n"),document.addEventListener("scroll",n);e("@media screen and (max-width: 768px) {\n /* 移动端适配 */\n .markdown-section {\n max-width: none;\n padding: 16px;\n }\n /* 改变原来按钮热区大小 */\n .sidebar-toggle {\n padding: 0 0 10px 10px;\n }\n /* my pin */\n .sidebar-pin {\n appearance: none;\n outline: none;\n position: fixed;\n bottom: 0;\n border: none;\n width: 40px;\n height: 40px;\n background: transparent;\n }\n}\n");var r,c="DOCSIFY_SIDEBAR_PIN_FLAG";function l(){var e="true"===(e=localStorage.getItem(c));localStorage.setItem(c,!e),e?(document.querySelector(".sidebar").style.transform="translateX(0)",document.querySelector(".content").style.transform="translateX(0)"):(document.querySelector(".sidebar").style.transform="translateX(300px)",document.querySelector(".content").style.transform="translateX(300px)")}768 ul"),1),a(t),n(e)}),e.ready(function(){document.querySelector(".sidebar-nav").addEventListener("click",o)})})}); \ No newline at end of file diff --git a/docs/_cdns/docsify@4.js b/docs/_cdns/docsify@4.js deleted file mode 100644 index 80be8c5fd..000000000 --- a/docs/_cdns/docsify@4.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function c(i){var o=Object.create(null);return function(e){var n=f(e)?e:JSON.stringify(e);return o[n]||(o[n]=i(e))}}var a=c(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),u=Object.prototype.hasOwnProperty,m=Object.assign||function(e){for(var n=arguments,i=1;i=e||n.classList.contains("hidden")?S(h,"add","sticky"):S(h,"remove","sticky"))}function ee(e,n,o,i){var t=[];null!=(n=l(n))&&(t=k(n,"a"));var a,r=decodeURI(e.toURL(e.getCurrentPath()));return t.sort(function(e,n){return n.href.length-e.href.length}).forEach(function(e){var n=decodeURI(e.getAttribute("href")),i=o?e.parentNode:e;e.title=e.title||e.innerText,0!==r.indexOf(n)||a?S(i,"remove","active"):(a=e,S(i,"add","active"))}),i&&(v.title=a?a.title||a.innerText+" - "+J:J),a}function ne(e,n){for(var i=0;ithis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,n,i,o){return(e/=o/2)<1?i/2*e*e+n:-i/2*(--e*(e-2)-1)+n}}]),re);function re(){var e=0c){n=n||p;break}n=p}!n||(r=fe[ve(e,n.getAttribute("data-id"))])&&r!==a&&(a&&a.classList.remove("active"),r.classList.add("active"),a=r,!pe&&h.classList.contains("sticky")&&(e=i.clientHeight,r=a.offsetTop+a.clientHeight+40,a=a.offsetTop>=t.scrollTop&&r<=t.scrollTop+e,i.scrollTop=a?t.scrollTop:+r"']/),xe=/[&<>"']/g,Se=/[<>"']|&(?!#?\w+;)/,Ae=/[<>"']|&(?!#?\w+;)/g,$e={"&":"&","<":"<",">":">",'"':""","'":"'"};var ze=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function Fe(e){return e.replace(ze,function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""})}var Ee=/(^|[^\[])\^/g;var Re=/[^\w:]/g,Te=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var Ce={},je=/^[^:]+:\/*[^/]*$/,Le=/^([^:]+:)[\s\S]*$/,Oe=/^([^:]+:\/*[^/]*)[\s\S]*$/;function qe(e,n){Ce[" "+e]||(je.test(e)?Ce[" "+e]=e+"/":Ce[" "+e]=Pe(e,"/",!0));var i=-1===(e=Ce[" "+e]).indexOf(":");return"//"===n.substring(0,2)?i?n:e.replace(Le,"$1")+n:"/"===n.charAt(0)?i?n:e.replace(Oe,"$1")+n:e+n}function Pe(e,n,i){var o=e.length;if(0===o)return"";for(var t=0;tn)i.splice(n);else for(;i.length>=1,e+=e;return i+e},We=we.defaults,Xe=Be,Qe=Ze,Je=Me,Ke=Ve;function en(e,n,i){var o=n.href,t=n.title?Je(n.title):null,n=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:i,href:o,title:t,text:n}:{type:"image",raw:i,href:o,title:t,text:Je(n)}}var nn=function(){function e(e){this.options=e||We}return e.prototype.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=i.length?e.slice(i.length):e}).join("\n")}(i,n[3]||"");return{type:"code",raw:i,lang:n[2]&&n[2].trim(),text:e}}},e.prototype.heading=function(e){var n=this.rules.block.heading.exec(e);if(n){var i=n[2].trim();return/#$/.test(i)&&(e=Xe(i,"#"),!this.options.pedantic&&e&&!/ $/.test(e)||(i=e.trim())),{type:"heading",raw:n[0],depth:n[1].length,text:i}}},e.prototype.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var n={type:"table",header:Qe(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(n.header.length===n.align.length){for(var i=n.align.length,o=0;o ?/gm,"");return{type:"blockquote",raw:n[0],text:e}}},e.prototype.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var n,i,o,t,a,r=e[0],c=e[2],u=1s[1].length:o[1].length>s[0].length||3/i.test(e[0])&&(n=!1),!i&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?i=!0:i&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(i=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:n,inRawBlock:i,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]}},e.prototype.link=function(e){var n=this.rules.inline.link.exec(e);if(n){e=n[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;var i=Xe(e.slice(0,-1),"\\");if((e.length-i.length)%2==0)return}else{var o=Ke(n[2],"()");-1$/.test(e)?i.slice(1):i.slice(1,-1):i)&&i.replace(this.rules.inline._escapes,"$1"),title:o&&o.replace(this.rules.inline._escapes,"$1")},n[0])}},e.prototype.reflink=function(e,n){if((i=this.rules.inline.reflink.exec(e))||(i=this.rules.inline.nolink.exec(e))){var e=(i[2]||i[1]).replace(/\s+/g," ");if((e=n[e.toLowerCase()])&&e.href)return en(i,e,i[0]);var i=i[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.strong.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="**"===o[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.strong.middle.exec(n.slice(0,o.index+3)))return{type:"strong",raw:e.slice(0,t[0].length),text:e.slice(2,t[0].length-2)}}},e.prototype.em=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.em.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="*"===o[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.em.middle.exec(n.slice(0,o.index+2)))return{type:"em",raw:e.slice(0,t[0].length),text:e.slice(1,t[0].length-1)}}},e.prototype.codespan=function(e){var n=this.rules.inline.code.exec(e);if(n){var i=n[2].replace(/\n/g," "),o=/[^ ]/.test(i),e=/^ /.test(i)&&/ $/.test(i);return o&&e&&(i=i.substring(1,i.length-1)),i=Je(i,!0),{type:"codespan",raw:n[0],text:i}}},e.prototype.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},e.prototype.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},e.prototype.autolink=function(e,n){e=this.rules.inline.autolink.exec(e);if(e){var i,n="@"===e[2]?"mailto:"+(i=Je(this.options.mangle?n(e[1]):e[1])):i=Je(e[1]);return{type:"link",raw:e[0],text:i,href:n,tokens:[{type:"text",raw:i,text:i}]}}},e.prototype.url=function(e,n){var i,o,t,a;if(i=this.rules.inline.url.exec(e)){if("@"===i[2])t="mailto:"+(o=Je(this.options.mangle?n(i[0]):i[0]));else{for(;a=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0],a!==i[0];);o=Je(i[0]),t="www."===i[1]?"http://"+o:o}return{type:"link",raw:i[0],text:o,href:t,tokens:[{type:"text",raw:o,text:o}]}}},e.prototype.inlineText=function(e,n,i){e=this.rules.inline.text.exec(e);if(e){i=n?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]:Je(this.options.smartypants?i(e[0]):e[0]);return{type:"text",raw:e[0],text:i}}},e}(),Ze=De,Ve=Ne,De=Ue,Ne={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:Ze,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ne.def=Ve(Ne.def).replace("label",Ne._label).replace("title",Ne._title).getRegex(),Ne.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ne.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,Ne.item=Ve(Ne.item,"gm").replace(/bull/g,Ne.bullet).getRegex(),Ne.listItemStart=Ve(/^( *)(bull)/).replace("bull",Ne.bullet).getRegex(),Ne.list=Ve(Ne.list).replace(/bull/g,Ne.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ne.def.source+")").getRegex(),Ne._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ne._comment=/|$)/,Ne.html=Ve(Ne.html,"i").replace("comment",Ne._comment).replace("tag",Ne._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ne.paragraph=Ve(Ne._paragraph).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.blockquote=Ve(Ne.blockquote).replace("paragraph",Ne.paragraph).getRegex(),Ne.normal=De({},Ne),Ne.gfm=De({},Ne.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Ne.gfm.nptable=Ve(Ne.gfm.nptable).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.gfm.table=Ve(Ne.gfm.table).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.pedantic=De({},Ne.normal,{html:Ve("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ne._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:Ve(Ne.normal._paragraph).replace("hr",Ne.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ne.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});Ze={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};Ze.punctuation=Ve(Ze.punctuation).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Ze._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Ze._comment=Ve(Ne._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ze.em.start=Ve(Ze.em.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.middle=Ve(Ze.em.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.em.endAst=Ve(Ze.em.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.endUnd=Ve(Ze.em.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.start=Ve(Ze.strong.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.middle=Ve(Ze.strong.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.strong.endAst=Ve(Ze.strong.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.endUnd=Ve(Ze.strong.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.blockSkip=Ve(Ze._blockSkip,"g").getRegex(),Ze.overlapSkip=Ve(Ze._overlapSkip,"g").getRegex(),Ze._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ze._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ze._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ze.autolink=Ve(Ze.autolink).replace("scheme",Ze._scheme).replace("email",Ze._email).getRegex(),Ze._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ze.tag=Ve(Ze.tag).replace("comment",Ze._comment).replace("attribute",Ze._attribute).getRegex(),Ze._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ze._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ze._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ze.link=Ve(Ze.link).replace("label",Ze._label).replace("href",Ze._href).replace("title",Ze._title).getRegex(),Ze.reflink=Ve(Ze.reflink).replace("label",Ze._label).getRegex(),Ze.reflinkSearch=Ve(Ze.reflinkSearch,"g").replace("reflink",Ze.reflink).replace("nolink",Ze.nolink).getRegex(),Ze.normal=De({},Ze),Ze.pedantic=De({},Ze.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:Ve(/^!?\[(label)\]\((.*?)\)/).replace("label",Ze._label).getRegex(),reflink:Ve(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ze._label).getRegex()}),Ze.gfm=De({},Ze.normal,{escape:Ve(Ze.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(i?e:gn(e,!0))+"\n":"
"+(i?e:gn(e,!0))+"
\n"},e.prototype.blockquote=function(e){return"
\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,i,o){return this.options.headerIds?"'+e+"\n":""+e+"\n"},e.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},e.prototype.list=function(e,n,i){var o=n?"ol":"ul";return"<"+o+(n&&1!==i?' start="'+i+'"':"")+">\n"+e+"\n"},e.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},e.prototype.checkbox=function(e){return" "},e.prototype.paragraph=function(e){return"

    "+e+"

    \n"},e.prototype.table=function(e,n){return"\n\n"+e+"\n"+(n=n&&""+n+"")+"
    \n"},e.prototype.tablerow=function(e){return"\n"+e+"\n"},e.prototype.tablecell=function(e,n){var i=n.header?"th":"td";return(n.align?"<"+i+' align="'+n.align+'">':"<"+i+">")+e+"\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;e='"},e.prototype.image=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;i=''+i+'":">"},e.prototype.text=function(e){return e},e}(),ln=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,i){return""+i},e.prototype.image=function(e,n,i){return""+i},e.prototype.br=function(){return""},e}(),vn=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var i=e,o=0;if(this.seen.hasOwnProperty(i))for(o=this.seen[e];i=e+"-"+ ++o,this.seen.hasOwnProperty(i););return n||(this.seen[e]=o,this.seen[i]=0),i},e.prototype.slug=function(e,n){void 0===n&&(n={});e=this.serialize(e);return this.getNextSafeSlug(e,n.dryrun)},e}(),hn=we.defaults,_n=Ie,mn=function(){function i(e){this.options=e||hn,this.options.renderer=this.options.renderer||new sn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ln,this.slugger=new vn}return i.parse=function(e,n){return new i(n).parse(e)},i.parseInline=function(e,n){return new i(n).parseInline(e)},i.prototype.parse=function(e,n){void 0===n&&(n=!0);for(var i,o,t,a,r,c,u,f,p,d,g,s,l,v,h,_="",m=e.length,b=0;bAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}}xn.options=xn.setOptions=function(e){return bn(xn.defaults,e),yn(xn.defaults),xn},xn.getDefaults=Me,xn.defaults=we,xn.use=function(a){var n,e=bn({},a);if(a.renderer){var i,r=xn.defaults.renderer||new sn;for(i in a.renderer)!function(o){var t=r[o];r[o]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.renderer[o].apply(r,e);return i=!1===i?t.apply(r,e):i}}(i);e.renderer=r}if(a.tokenizer){var t,c=xn.defaults.tokenizer||new nn;for(t in a.tokenizer)!function(){var o=c[t];c[t]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.tokenizer[t].apply(c,e);return i=!1===i?o.apply(c,e):i}}();e.tokenizer=c}a.walkTokens&&(n=xn.defaults.walkTokens,e.walkTokens=function(e){a.walkTokens(e),n&&n(e)}),xn.setOptions(e)},xn.walkTokens=function(e,n){for(var i=0,o=e;iAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}},xn.Parser=mn,xn.parser=mn.parse,xn.Renderer=sn,xn.TextRenderer=ln,xn.Lexer=fn,xn.lexer=fn.lex,xn.Tokenizer=nn,xn.Slugger=vn;var Sn=xn.parse=xn;function An(e,i){if(void 0===i&&(i='
      {inner}
    '),!e||!e.length)return"";var o="";return e.forEach(function(e){var n=e.title.replace(/(<([^>]+)>)/g,"");o+='
  • '+e.title+"
  • ",e.children&&(o+=An(e.children,i))}),i.replace("{inner}",o)}function $n(e,n){return'

    '+n.slice(5).trim()+"

    "}function zn(e,o){var t=[],a={};return e.forEach(function(e){var n=e.level||1,i=n-1;o?@[\]^`{|}~]/g;function Rn(e){return e.toLowerCase()}function Tn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Rn).replace(/<[^>]+>/g,"").replace(En,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=Fn[n],e=u.call(Fn,n)?e+1:0;return n=(Fn[n]=e)?n+"-"+e:n}Tn.clear=function(){Fn={}};var Cn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function jn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=Cn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return"&#x"+e+";"}).join("‍").concat("︎")+"":''+o+'':i;var i,o}).replace(/__colon__/g,":")}function Ln(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}function On(e){return(e=void 0===e?"":e).replace(/(<\/?a.*?>)/gi,"")}var qn,Pn=be(function(e){var u,f,p,d,n,g=function(u){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},R={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof T?new T(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=r.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof T)){var k,w=1;if(l){if(!(k=C(h,m,n,s))||k.index>=n.length)break;var y=k.index,x=k.index+k[0].length,S=m;for(S+=_.value.length;S<=y;)_=_.next,S+=_.value.length;if(S-=_.value.length,m=S,_.value instanceof T)continue;for(var A=_;A!==i.tail&&(Sr.reach&&(r.reach=E);b=_.prev;z&&(b=j(i,b,z),m+=z.length),L(i,b,w);$=new T(c,g?R.tokenize($,g):$,v,$);_=j(i,b,$),F&&j(i,_,F),1r.reach&&(r.reach=E.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=R.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=R.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:T};function T(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function C(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function a(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function j(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function L(e,n,i){for(var o=n.next,t=0;t"+t.content+""},!u.document)return u.addEventListener&&(R.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;u.postMessage(R.highlight(e,R.languages[i],i)),n&&u.close()},!1)),R;var o=R.util.currentScript();function t(){R.manual||R.highlightAll()}return o&&(R.filename=o.src,o.hasAttribute("data-manual")&&(R.manual=!0)),R.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),R}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=g),void 0!==me&&(me.Prism=g),g.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},g.languages.markup.tag.inside["attr-value"].inside.entity=g.languages.markup.entity,g.languages.markup.doctype.inside["internal-subset"].inside=g.languages.markup,g.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(g.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:g.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:g.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},g.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(g.languages.markup.tag,"addAttribute",{value:function(e,n){g.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:g.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),g.languages.html=g.languages.markup,g.languages.mathml=g.languages.markup,g.languages.svg=g.languages.markup,g.languages.xml=g.languages.extend("markup",{}),g.languages.ssml=g.languages.xml,g.languages.atom=g.languages.xml,g.languages.rss=g.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+n.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(g),g.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},g.languages.javascript=g.languages.extend("clike",{"class-name":[g.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),g.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,g.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:g.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:g.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:g.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:g.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:g.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),g.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:g.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),g.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),g.languages.markup&&(g.languages.markup.tag.addInlined("script","javascript"),g.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),g.languages.js=g.languages.javascript,void 0!==g&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),u={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',g.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),g.hooks.add("before-sanity-check",function(e){var t,n,i,o,a,r,c=e.element;c.matches(d)&&(e.code="",c.setAttribute(f,p),(t=c.appendChild(document.createElement("CODE"))).textContent="Loading…",i=c.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=u[n]||n),g.util.setLanguage(t,e),g.util.setLanguage(c,e),(n=g.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){c.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(c.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),c.hasAttribute("data-start")||c.setAttribute("data-start",String(i+1))),t.textContent=e,g.highlightElement(t)},a=function(e){c.setAttribute(f,"failed"),t.textContent=e},(r=new XMLHttpRequest).open("GET",i,!0),r.onreadystatechange=function(){4==r.readyState&&(r.status<400&&r.responseText?o(r.responseText):400<=r.status?a("✖ Error "+r.status+" while fetching file: "+r.statusText):a("✖ Error: File does not exist or is empty"))},r.send(null))}),n=!(g.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)g.highlightElement(n)}}),g.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),g.plugins.fileHighlight.highlight.apply(this,arguments)})});function Mn(e,n){return"___"+e.toUpperCase()+n+"___"}qn=Prism,Object.defineProperties(qn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,a){var r;o.language===t&&(r=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var n,i=r.length;-1!==o.code.indexOf(n=Mn(t,i));)++i;return r[i]=e,n}),o.grammar=qn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,g;f.language===p&&f.tokenStack&&(f.grammar=qn.languages[p],d=0,g=Object.keys(f.tokenStack),function e(n){for(var i=0;i=g.length);i++){var o,t,a,r,c,u=n[i];"string"==typeof u||u.content&&"string"==typeof u.content?(t=g[d],a=f.tokenStack[t],o="string"==typeof u?u:u.content,c=Mn(p,t),-1<(r=o.indexOf(c))&&(++d,t=o.substring(0,r),a=new qn.Token(p,qn.tokenize(a,f.grammar),"language-"+p,a),r=o.substring(r+c.length),c=[],t&&c.push.apply(c,e([t])),c.push(a),r&&c.push.apply(c,e([r])),"string"==typeof u?n.splice.apply(n,[i,1].concat(c)):u.content=c)):u.content&&e(u.content)}return n}(f.tokens))}}});function In(t,e){var a=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var r=o(e=t.markdown||{})?e(Sn,n):(Sn.setOptions(m(e,{renderer:m(n,e.renderer)})),Sn);this._marked=r,this.compile=function(i){var o=!0,e=c(function(e){o=!1;var n="";return i&&(n=f(i)?r(i):r.parser(i),n=t.noEmoji?n:jn(n,t.nativeEmoji),Tn.clear(),n)})(i),n=a.router.parse().file;return o?a.toc=a.cacheTOC[n]:a.cacheTOC[n]=[].concat(a.toc),e}}var Nn={},Hn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};In.prototype.compileEmbed=function(e,n){var i,o,t=Ln(n),a=t.str,t=t.config;if(n=a,t.include)return R(e)||(e=q(this.contentBase,C(this.router.getCurrentPath()),e)),t.type&&(o=Hn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Hn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},In.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore} --\x3e",""),e.title=On(o),e.ignoreSubHeading=!0),/{docsify-ignore}/g.test(o)&&(o=o.replace("{docsify-ignore}",""),e.title=On(o),e.ignoreSubHeading=!0),//g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),e.title=On(o),e.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(o)&&(o=o.replace("{docsify-ignore-all}",""),e.title=On(o),e.ignoreAllSubs=!0);i=Tn(t.id||o),t=a.toURL(a.getCurrentPath(),{id:i});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=Pn.languages[n=void 0===n?"markup":n]||Pn.languages.markup;return'
    '+Pn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
    "},t.link=(i=(n={renderer:e,router:a,linkTarget:n,linkRel:i,compilerClass:g}).renderer,c=n.router,u=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=Ln(n=void 0===n?"":n),a=t.str,t=t.config;return u=t.target||u,r="_blank"===u?f.config.externalLinkRel||"noopener":"",n=a,R(e)||f._matchNotCompileLink(e)||t.ignore?(R(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+u+'"'),o.push(0!==e.indexOf("mailto:")&&""!==r?' rel="'+r+'"':"")):(e===f.config.homepage&&(e="README"),e=c.toURL(e,null,c.getCurrentPath())),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?$n("tip",e):/^\?>/.test(e)?$n("warn",e):"

    "+e+"

    ";return e},t.image=(o=(i={renderer:e,contentBase:o,router:a}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],a=Ln(n),r=a.str,a=a.config;return n=r,a["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),a.size&&(n=(r=a.size.split("x"))[0],(r=r[1])?t.push('width="'+n+'" height="'+r+'"'):t.push('width="'+n+'"')),a.class&&t.push('class="'+a.class+'"'),a.id&&t.push('id="'+a.id+'"'),R(e)||(o=q(p,C(d.getCurrentPath()),e)),0":''+i+'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},In.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var a=0;a{inner}");this.cacheTree[o]=n}return t},In.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),a({token:t,embedToken:n}),++u>=c&&a({})}}(n);n.embed.url?X(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:c,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),r=r.slice(0,n).concat(i,r.slice(n+1)),p.push({start:n,length:i.length-1})):(Bn[t]=r.concat(),r.links=Bn[t].links=f,o(r))})}function Yn(e,n,i){var o,t,a,r;return n="function"==typeof i?i(n):"string"==typeof i?(a=[],r=0,(o=i).replace(V,function(n,e,i){a.push(o.substring(r,i-1)),r=i+=n.length+1,a.push(t&&t[n]||function(e){return("00"+("string"==typeof Y[n]?e[Y[n]]():Y[n](e))).slice(-n.length)})}),r!==o.length&&a.push(o.substring(r)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var a=0,r=k(".markdown-section > *").filter(n);ascript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var u,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!Un&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(Un=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(u=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,k(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||u.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return Un&&(n.data=function(){return Un}),[e,n]})));for(var g=0,s=p;g([^<]*?)

    $'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],S(o,"add","has-mask"),R(n[1])||(e=q(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):S(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'
    \x3c!--cover--\x3e
    ')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=q(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"","
    "+('')+'
    \x3c!--main--\x3e
    '),this._renderTo(u,f,!0)):this.rendered=!0,e.mergeNavbar&&s?p=b(".sidebar"):(c.classList.add("app-nav"),e.repo||c.classList.add("no-badge")),e.loadNavbar&&y(p,c),e.themeColor&&(v.head.appendChild(w("div","").firstElementChild),a=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=k("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?Q(e,a):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&X(e).then(function(e){e=w("style",e);_.appendChild(e),Q(e,a)}))}))),this._updateRender(),S(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var a=this.routes(),r=Object.keys(a),c=function(){return null};function u(){var e=r.shift();if(!e)return c(null);var n=A(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return u();var o=a[e];if("string"==typeof o)return c(o);if("function"!=typeof o)return u();n=o,e=Xn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?c(e):!1===e?c(null):u()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){c=e,u()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?D:H)(e);this.router=e,this.updateRender(),U=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),U.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),U=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,a){void 0===a&&(a=d);var r=this._hooks[e],c=this.config.catchPluginErrors,u=function(n){var e=r[n];if(n>=r.length)a(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,u(n+1)})}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}else try{var o=e(t);t=void 0===o?t:o,u(n+1)}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}}else u(n+1)};u(0)},n}(we))))))));function Kn(e,n,i){return Qn&&Qn.abort&&Qn.abort(),Qn=X(e,!0,i)}window.Docsify={util:Me,dom:n,get:X,slugify:Tn,version:"4.13.1"},window.DocsifyCompiler=In,window.marked=Sn,window.Prism=Pn,e(function(e){return new Jn})}(); diff --git a/docs/_cdns/fonts.css b/docs/_cdns/fonts.css deleted file mode 100644 index 02482a233..000000000 --- a/docs/_cdns/fonts.css +++ /dev/null @@ -1,24 +0,0 @@ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - src: url(./fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 300; - src: url(./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 400; - src: url(./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 600; - src: url(./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf) format('truetype'); -} diff --git a/docs/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf b/docs/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf deleted file mode 100644 index 82ca44afe..000000000 Binary files a/docs/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf and /dev/null differ diff --git a/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf b/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf deleted file mode 100644 index f16ada236..000000000 Binary files a/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf and /dev/null differ diff --git a/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf b/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf deleted file mode 100644 index 61acbb710..000000000 Binary files a/docs/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf and /dev/null differ diff --git a/docs/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf b/docs/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf deleted file mode 100644 index 133153f69..000000000 Binary files a/docs/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf and /dev/null differ diff --git a/docs/_cdns/search.min.js b/docs/_cdns/search.min.js deleted file mode 100644 index 63b5bc7e4..000000000 --- a/docs/_cdns/search.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=e.text||""):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n

    '+e.title+"

    \n

    "+e.content+"

    \n
    \n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||'

    '+c+"

    ",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-1nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}img.emoji{height:1.2em}img.emoji,span.emoji{vertical-align:middle}span.emoji{font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1.2em}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{position:relative;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100vh;width:100%;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;bottom:0;width:100%}section.cover .cover-main{flex:1;margin:0 16px;text-align:center;position:relative}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid #42b983;border-color:var(--theme-color,#42b983);box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0;content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto} \ No newline at end of file diff --git a/docs/_coverpage.md b/docs/_coverpage.md deleted file mode 100644 index 09bff41ae..000000000 --- a/docs/_coverpage.md +++ /dev/null @@ -1,8 +0,0 @@ -# Molgenis Armadillo 4 - -![logo](img/armadillo-logo.png) - -> Armadillo is a data portal that allows data stewards to share datasets on a server, and researchers to analyze these datasets and datasets shared on other servers using the DataSHIELD analysis tools. - -[GitHub](https://github.com/molgenis/molgenis-service-armadillo) -[Armadillo suite](/README.md#armadillo-suite) diff --git a/docs/_navbar.md b/docs/_navbar.md deleted file mode 100644 index 12b325e9c..000000000 --- a/docs/_navbar.md +++ /dev/null @@ -1,21 +0,0 @@ -* Menu - - * [Armadillo suite](/) - - * [Armadillo 3 UI](/ui.md#armadillo-user-interface "Armadillo 3 UI") - - * [DsMolgenisArmadillo](https://github.com/molgenis/molgenis-r-datashield/blob/master/README.md) - - * [MolgenisArmadillo](https://molgenis.github.io/molgenis-r-armadillo/) - - * [molgenis-r-auth](https://molgenis.github.io/molgenis-r-auth/) - - * [molgenis-r-client](https://github.com/molgenis/molgenis-r-client/blob/master/README.md) - - * [ds-upload](https://lifecycle-project.github.io/ds-upload/) - - * [ds-dictionaries](https://github.com/lifecycle-project/ds-dictionaries/blob/master/README.md) - - * [Migration from 2 to 3](/upgrade-2-3.md) - - * [Release test (R)](/release-test.md) diff --git a/docs/_sidebar.md b/docs/_sidebar.md deleted file mode 100644 index ccd401116..000000000 --- a/docs/_sidebar.md +++ /dev/null @@ -1,15 +0,0 @@ -- [Armadillo suite](/ "Armadillo suite") - - [Install](/ops/installing.md) - - [Configure](/ops/configuring.md) - - [UI](/ui.md#armadillo-user-interface "Armadillo 3 UI") - - [FAQ](/faq.md) - - [Migration from 3 to 4](/upgrade-3-4.md) - - [Migration from 2 to 3](/upgrade-2-3.md) - - [Release test (R)](/release-test.md) -- External sites - - [DsMolgenisArmadillo](https://github.com/molgenis/molgenis-r-datashield/blob/master/README.md) - - [MolgenisArmadillo](https://molgenis.github.io/molgenis-r-armadillo/) - - [molgenis-r-auth](https://molgenis.github.io/molgenis-r-auth/) - - [molgenis-r-client](https://github.com/molgenis/molgenis-r-client/blob/master/README.md) - - [ds-upload](https://lifecycle-project.github.io/ds-upload/) - - [ds-dictionaries](https://github.com/lifecycle-project/ds-dictionaries/blob/master/README.md) diff --git a/docs/adr/0001-use-adr-to-describe-architecture-decisions.md b/docs/adr/0001-use-adr-to-describe-architecture-decisions.md deleted file mode 100644 index 25c12b516..000000000 --- a/docs/adr/0001-use-adr-to-describe-architecture-decisions.md +++ /dev/null @@ -1,19 +0,0 @@ -# 1. Record architecture decisions - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need to record the architectural decisions made on this project. - -## Decision - -We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions. - -## Consequences - -See Michael Nygard's article, linked above. \ No newline at end of file diff --git a/docs/adr/0002-implement-authentication-using-openid.md b/docs/adr/0002-implement-authentication-using-openid.md deleted file mode 100644 index 4b8c6c330..000000000 --- a/docs/adr/0002-implement-authentication-using-openid.md +++ /dev/null @@ -1,27 +0,0 @@ -# 2. Implement authentication using openid - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need a way to help users authentication in our system - -## Decision - -We implement openid in our application so we use ID-providers to authenticate in our services. -We intent to use JWTs which are machine-readable bearer tokens, to integrate external services. - -## Consequences - -- Use an ID-provider to authenticate in the DataSHIELD service. -- We intend to use the role mechanism in the ID-provider to authorise. The roles that we can think of at this moment are: - - Local data manager - - Researcher - - Administrator - This list can be updated and / or amended over time. -- By using an ID-provider we intend to be able to attach other systems as well. -- By using an ID-provider we intend to be able to federate to other ID-providers. \ No newline at end of file diff --git a/docs/adr/0003-implement-method-security-using-datashield4j.md b/docs/adr/0003-implement-method-security-using-datashield4j.md deleted file mode 100644 index f14d11a97..000000000 --- a/docs/adr/0003-implement-method-security-using-datashield4j.md +++ /dev/null @@ -1,21 +0,0 @@ -# 3. Implement method security using datashield4j - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need some way of being sure that there is no malicious script execution on the DataSHIELD environment. - -## Decision - -We implement datashield4j to prevent users to execute method and parameters other than the DataSHIELD provided methods. -We looked at OCAP ([R native object capabilities](https://docs.google.com/document/d/1Yx10Xw8Uige3hK-6YwzM8RhrtqSogDTGAZlcF218U2U/edit)) as well but came to the conclusion -that the client that OCAP is using needs to be developed in JAVA. The datashield4j library is already available and tested which makes it easier to implement. - -## Consequences - -- Users can not execute malicious scripts on the R environment anymore. diff --git a/docs/adr/0004-implement-asynchronicity-in-requestflow.md b/docs/adr/0004-implement-asynchronicity-in-requestflow.md deleted file mode 100644 index 46f93a44f..000000000 --- a/docs/adr/0004-implement-asynchronicity-in-requestflow.md +++ /dev/null @@ -1,20 +0,0 @@ -# 4. Implement asynchronicity in the requestflow - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -Clients can have operations running on multiple DataSHIELD servers concurrently. - -## Decision - -We need to support asynchronous requests in the R client. We implemented it using completable futures. -We must keep the last execution result for each R session until it gets retrieved or until a new execution is started. - -## Consequences - -- Users can have more than one session on the same server. diff --git a/docs/adr/0005-deploy-on-both-vm-and-kubernetes.md b/docs/adr/0005-deploy-on-both-vm-and-kubernetes.md deleted file mode 100644 index 0a1dc6074..000000000 --- a/docs/adr/0005-deploy-on-both-vm-and-kubernetes.md +++ /dev/null @@ -1,21 +0,0 @@ -# 5. Deploy both on a virtual machine and Kubernetes - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -Because of the diversity in landscape we need to be able to deploy on different environments. - -## Decision - -We are going to create deployments for virtual machines based on CentOS (>=8) using Ansible and Vagrant. -Besides that we are going to create a chart which allows you to deploy on Kubernetes. - -## Consequences - -- Some of the VM environments are not serviced (Ubuntu, Suse, other operating systems). -- Some other container orchestrators are not implemented e.g. docker-swarm. diff --git a/docs/adr/0006-use-rdata-format-as-data-input.md b/docs/adr/0006-use-rdata-format-as-data-input.md deleted file mode 100644 index db37508cd..000000000 --- a/docs/adr/0006-use-rdata-format-as-data-input.md +++ /dev/null @@ -1,23 +0,0 @@ -# 6. Use RData file format as data input for the service - -Date: 2020-04-23 - -## Status - -Accepted - -## Context - -We want to make the MOLGENIS "Armadillo" service data provider agnostic. There are a couple of reasons why we are doing this -- the service is usable for other parties as well -- the service can still integrate with MOLGENIS. -- the release cycle of the service is data provider independent -- the service can be developed by other parties as well - -## Decision - -We implement an endpoint to upload and load RData files in the MOLGENIS "Armadillo" service to manage data for the use in DataSHIELD. - -## Consequences -- We need a client which is capable to deal with uploading RData files into the MOLGENIS "Armadillo" service -- MOLGENIS needs to have an RData download option and/or have an EMX to RData-converter to be able to communicate directly with the MOLGENIS "Armadillo" service diff --git a/docs/adr/0007-use-roles-to-authorise-users-to-use-data.md b/docs/adr/0007-use-roles-to-authorise-users-to-use-data.md deleted file mode 100644 index 31775ff51..000000000 --- a/docs/adr/0007-use-roles-to-authorise-users-to-use-data.md +++ /dev/null @@ -1,39 +0,0 @@ -# 7. Use roles to authorise users to use certain data - -Date: 2020-04-23 - -## Status - -Accepted - -## Context - -We need a way to authorise users in the MOLGENIS "Armadillo" service to use data. We want this to be as straightforward as possible. - -Other techniques are ACL's for fine grained permissions on resources and ID-based. - -**ACL's** -It is more time consuming to implement ACL's. With roles the you are less flexible, but we believe this is sufficient to use DataSHIELD. - -**ID-based** -The ID-based approach was suggested as a first solution to make the service secure. A big disadvantage is that anyone with the link can -share the data. That means that there has to be a lot of trust between the data manager and the researcher. In practices, this is not feasible. - -## Decision - -We will use roles as a basic principle to give users permission on the data they need to have access to. -Within the MOLGENIS "Armadillo" service there are at least these 2 types of roles: -- Researcher - *Implicit permissions* - - READ --> on data in the shared folder(s) to which they have access - - ADMIN --> on data in their own (user)folder -- Data manager - *Implicit permissions* - - ADMIN --> on all that lives -Each researcher role will have corresponding folder. The data manager is able to do anything anywhere. - -## Consequences -- In the file storage you need to be aware of the fact that you are using roles as an authorisation mechanism -- It is not possible to have fine grained permissions within a role - - diff --git a/docs/adr/0008-load-multiple-rdata-files.md b/docs/adr/0008-load-multiple-rdata-files.md deleted file mode 100644 index 26603b9ec..000000000 --- a/docs/adr/0008-load-multiple-rdata-files.md +++ /dev/null @@ -1,18 +0,0 @@ -# 8. As a researcher you are able to load multiple RData files - -Date: 2020-04-23 - -## Status - -Accepted - -## Context -We want to give researchers as much freedom as possible when they're selecting and loading data. - -## Decision - -The `/load-tables` endpoint supports loading multiple .RData files at once. These files can be in different folders. - -## Consequences -- Users with multiple roles can load data from multiple folders into their workspace. This will make - sharing workspaces between users difficult (because they may not have the same roles). \ No newline at end of file diff --git a/docs/adr/0009-use-bean-scope-for-profiles.md b/docs/adr/0009-use-bean-scope-for-profiles.md deleted file mode 100644 index 363b994d3..000000000 --- a/docs/adr/0009-use-bean-scope-for-profiles.md +++ /dev/null @@ -1,25 +0,0 @@ -# 9. Use Spring bean scope to distinguish between profiles - -Date: 2020-10-05 - -## Status - -Accepted - -## Context -We need to be able to switch between profiles. -But many singleton beans have profile information in them. - -## Decision - -Create a custom `@ProfileScope` to store the different versions of the profiles and inject proxies -to look up the correct instance. - -We already did this for the ArmadilloSession, a `@SessionScope` bean, to store the connection to -the R server which is different for each user session. - -## Consequences -- Profiles remain an aspect, are not interwoven with the logic -- Easier to test -- Once it works, it works -- The mechanism is harder to understand and debug diff --git a/docs/css/simple.css b/docs/css/simple.css new file mode 100644 index 000000000..394429767 --- /dev/null +++ b/docs/css/simple.css @@ -0,0 +1,250 @@ +/*Apply MOLGENIS Styleguide to docs theme*/ +/*Import fonts*/ +@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=IBM+Plex+Mono&family=IBM+Plex+Sans&display=swap'); + +/* Set colours for default theme (light) */ +:root { + --md-primary-fg-color: #017FFD; + --md-primary-fg-inverse-color: #FAFAFA; + --md-primary-fg-color--light: #FFFFFF; + --md-primary-fg-color--dark: #353535; + --md-accent-fg-color: #005EC4; + --md-accent-fg-color--light: #353535; + --md-accent-fg-inverse-color--light: #D4ECFF; + --md-accent-bg-color--light: #D4ECFF; + --md-primary-bg-color: #FAFAFA; + --md-primary-bg-inverse-color: #017FFD; +} + +/* Set colours for dark theme */ +[data-md-color-scheme="slate"] { + --md-primary-fg-color: #5DFFAC; + --md-accent-fg-color: #00BD55; + --md-accent-bg-color--light: #5C5C5C; + --md-accent-fg-inverse-color--light: #D1FAE5; + --md-accent-fg-color--light: #D1FAE5; + --md-primary-bg-color: #353535; + --md-primary-fg-inverse-color: #5DFFAC; + --md-primary-bg-inverse-color: #353535; +} +/* Container for main text*/ +body > div.md-container > main > div > div.md-content { + background-color: var(--md-primary-bg-color); +} + +/* Left menu colours*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar--primary{ + color: var(--md-primary-fg-inverse-color); + background-color: var(--md-primary-bg-inverse-color); +} + +/* Selected item left menu*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar > div > div > nav > ul > li.md-nav__item.md-nav__item--active > a { + border: transparent; + color: var(--md-primary-fg-inverse-color); + border-left-color: var(--md-accent-fg-color); + border-left-style: solid; + border-left-width: 5px; + font-weight: 700; +} + +/* Padding for selected items in menus */ +body > div.md-container > main > div > div.md-sidebar.md-sidebar > div > div > nav > ul > li.md-nav__item.md-nav__item--active > a > span, +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary > div > div > nav > ul > li > a.md-nav__link--active > span, +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary > div > div > nav > ul > li > nav > ul > li > a.md-nav__link--active > span{ + padding-left: 0.5em; +} + +/* Hover colour in menu*/ +.md-nav__link[href]:hover { + color: var(--md-accent-fg-color); +} +/* Active colour of secondary menu item*/ +.md-nav__item .md-nav__link--active, .md-nav__item .md-nav__link--active code { + color: var(--md-accent-fg-color--light); +} + +/* Label in menu*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar > div > div > nav > label { + background-color: inherit; + box-shadow: none; + font-family: "Bebas Neue", sans-serif; + font-weight: 400; + font-style: normal; + font-size: 1.7em; + line-height: normal; +} + +/* Text colour of title in left menu*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar--primary > div > div > nav > label { + color: var(--md-primary-fg-inverse-color); +} + +/* Text colour of title in right menu*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary > div > div > nav > label { + color: var(--md-primary-fg-inverse-color); +} +/* Styling right menu*/ +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary { + background-color: var(--md-primary-bg-inverse-color); + color: var(--md-primary-fg-inverse-color); +} + +/* Set active styling for right menu */ +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary > div > div > nav > ul > li > a.md-nav__link--active, +body > div.md-container > main > div > div.md-sidebar.md-sidebar--secondary > div > div > nav > ul > li > nav > ul > li > a.md-nav__link--active { + color: var(--md-accent-fg-inverse-color--light); + border-left-color: var(--md-accent-fg-inverse-color--light); + border-left-style: solid; + border-left-width: 5px; + font-weight: 700; +} + +/* Menu styling */ +.md-sidebar { + padding: 0.8rem; + height: 100%; +} + +/*Set font for all text*/ +body { + font-family: "IBM Plex Sans", sans-serif; + font-weight: 400; + font-size: 16px; + font-style: normal; +} +/*Set font and sizes for headings*/ +.md-typeset h1, h2, h3, h4 { + font-family: "Bebas Neue", sans-serif; + font-weight: 400; + font-style: normal; + color: var(--md-primary-fg-color); +} + +.md-typeset h1 { + font-size: 82px; + border-top-color: var(--md-primary-fg-color); + border-top-style: solid; + border-top-width: 5px; + padding-top: 0.5em; + margin-bottom: 0; +} + +.md-typeset h2 { + font-size: 64px; + margin: 0; +} + +.md-typeset h3 { + font-size: 32px; + margin: 0; +} + +.md-typeset h4 { + font-size: 24px; + margin: 0; +} + +.md-typeset h5 { + font-size: 16px; + font-weight: 700; + margin: 0; +} + +.md-typeset h6 { + font-size: 14px; + font-weight: 700; + margin: 0; +} + +/*Style blockquote as in styleguide*/ +.md-typeset blockquote { + color: var(--md-accent-fg-color--light); + font-size: 14px; + font-family: "IBM Plex Mono", sans-serif; + font-weight: 300; + font-style: italic; + padding: 0.1rem 1rem 0.1rem; + background-color: var(--md-accent-bg-color--light); +} +[dir=ltr] .md-typeset blockquote { + border-left-color: var(--md-accent-fg-color); +} + +/*Style code blocks*/ +.rst-content pre code { + font-size: 16px; +} +.rst-content code { + font-family: "IBM Plex Mono", sans-serif; +} + +.md-header__button.md-logo img { + height: 2.5em; +} + +.md-typeset a { + color: var(--md-primary-fg-color); +} + +.md-main__inner { + max-width: 100%; +} + +.md-grid { + max-width: 100%; + margin-left: 0; + margin-right: 0; +} + +/* Top navbar*/ +body > header.md-header { + background-color: var(--md-primary-fg-color--dark); + color: var(--md-primary-fg-color--light); + /* this is a negative margin because if we set the top one to be 0, the right navbar stops + working properly with active links*/ + margin-bottom: -1.5rem; +} + +.intro { + font-size: 18px; + font-family: "IBM Plex Mono", sans-serif; + background-color: var(--md-primary-bg-inverse-color)!important; + color: var(--md-primary-fg-inverse-color)!important; + font-weight: bold; + padding: 2rem 3rem 2rem 2rem; + margin-left: -1.2rem; + margin-right: -1.2rem; + margin-top: -1.2rem; +} + +.before-intro { + color: var(--md-primary-bg-inverse-color); + padding: 2.2rem; + margin-left: -1.2rem; + margin-right: -1.2rem; + margin-top: -2.5rem; + background-color: var(--md-primary-bg-inverse-color)!important; +} + +h1.intro { + font-weight: 300; + background-color: var(--md-primary-bg-inverse-color)!important; + color: var(--md-primary-fg-inverse-color)!important; + border-color: var(--md-primary-fg-inverse-color); + margin: -1.2rem; + padding-bottom: 1rem; +} + +.md-typeset img[align]:only-child.armadillo-intro-logo { + margin-top: 3rem; + max-width: 35%; +} + +.md-typeset .tabbed-labels>label { + font-size: 0.8rem; +} + +body > div.md-container > main > div > div.md-content:has(.intro) { + background-color: var(--md-primary-bg-inverse-color); +} \ No newline at end of file diff --git a/docs/DataSHIELD-datamanagement/data/nonrep.parquet b/docs/data/nonrep.parquet similarity index 100% rename from docs/DataSHIELD-datamanagement/data/nonrep.parquet rename to docs/data/nonrep.parquet diff --git a/docs/DataSHIELD-datamanagement/data/yearlyrep.parquet b/docs/data/yearlyrep.parquet similarity index 100% rename from docs/DataSHIELD-datamanagement/data/yearlyrep.parquet rename to docs/data/yearlyrep.parquet diff --git a/docs/developer_notes.md b/docs/developer_notes.md deleted file mode 100644 index 39bf3b13e..000000000 --- a/docs/developer_notes.md +++ /dev/null @@ -1,36 +0,0 @@ -# To develop the docs - -You can run following the [UI docs](../ui/README.md) section. Then there -is no need for installing docsify globally. - -## Install docsify - -Install docsify - -```bash -npm i docsify-cli -g -``` - -## Serve - -Then you can serve the dev in this directory using - -```bash -docsify serve ./docs -``` - -## View -``` -http://localhost:3000 -``` - -## Github pages - -We have this configured through github pages instead of custom actions: - -- https://github.com/molgenis/molgenis-service-armadillo/settings/pages - -You can check its status on: - -- https://github.com/molgenis/molgenis-service-armadillo/actions - diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 248b2e503..000000000 --- a/docs/faq.md +++ /dev/null @@ -1,101 +0,0 @@ -# Frequently asked questions - -## Docker gives a 'java.socket' error -You might need to enable Docker socket. On Docker desktop you can find that under 'settings' and 'advanced'. - -## Can I use docker compose to start profiles? -Instead of making Armadillo start/stop DataSHIELD profiles you can also use docker compose. -See commented section in docker-compose.yml file. - -## Can I pass environment or commandline variables instead of application.yml? -Yes, it is standard spring. - -## Can I run Armadillo with oauth2 config offline? -Yes, you can run in 'offline' profile -``` -./gradlew run -Dspring.profiles.active=offline -``` - -## How to run Armadillo version 2? - -For armadillo 2.x you can follow instructions at -* for testing we use docker compose at https://github.com/molgenis/molgenis-service-armadillo/tree/armadillo-service-2.2.3 -* for production we are using Ansible at https://galaxy.ansible.com/molgenis/armadillo` - -## How to run Armadillo as developer? -We develop Armadillo using IntelliJ. - -### To build Armadillo -Run following command in the github root: -```./gradlew build``` - -To execute in 'dev' run following command in the github root: -```./gradlew run``` - -### Setting up development tools -This repository uses `pre-commit` to manage commit hooks. An installation guide can be found -[here](https://pre-commit.com/index.html#1-install-pre-commit). To install the hooks, run `pre-commit install` from the root folder of this repository. Now -your code will be automatically formatted whenever you commit. - -### How to change data directory -Data is automatically stored in the `data` folder in this repository. You can choose another location -in `application.yml` by changing the `storage.root-dir` -setting. - -> **_Note_**: When you run Armadillo locally for the first time, the `lifecycle` project has not been -> added to the system metadata yet. To add it automatically, see [Application properties](#application-properties). -> Alternatively you can add it manually: -> - Go to the Swagger UI (`http://localhost:8080/swagger-ui/index.html`) -> - Go to the `PUT /access/projects` endpoint -> - Add the project `lifecycle` -> -> Now you're all set! - -### Working with resources in development mode -When developing locally, docker has trouble connecting to localhost. This problem becomes clear when working with -resources. Luckily there is a quick fix for the problem. Instead of defining a resource as for example -`http://localhost:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`, rewrite it to: -`http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`. Here's some example R code -for uploading resources: -```R -## Uploading resources -library(MolgenisArmadillo) -library(resourcer) - -token <- armadillo.get_token("http://localhost:8080/") - -resGSE1 <- resourcer::newResource( - name = "GSE66351_1", - secret = token, - url = "http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda", - format = "ExpressionSet" -) - -armadillo.login("http://localhost:8080/") -armadillo.upload_resource(project="omics", folder="ewas", resource = resGSE1, name = "GSE66351_1") -``` -And for using them: -```R -library(DSMolgenisArmadillo) -library(dsBaseClient) - -token <- armadillo.get_token("http://localhost:8080/") - -builder <- DSI::newDSLoginBuilder() -builder$append( - server = "local", - url = "http://localhost:8080/", - token = token, - driver = "ArmadilloDriver", - profile = "uniform", - resource = "omics/ewas/GSE66351_1" -) - -login_data <- builder$build() -conns <- DSI::datashield.login(logins = login_data, assign = TRUE) - -datashield.resources(conns = conns) -datashield.assign.resource(conns, resource="omics/ewas/GSE66351_1", symbol="eSet_0y_EUR") -ds.class('eSet_0y_EUR', datasources = conns) -datashield.assign.expr(conns, symbol = "methy_0y_EUR",expr = quote(as.resource.object(eSet_0y_EUR))) -``` diff --git a/docs/img/armadillo-logo-border.png b/docs/img/armadillo-logo-border.png new file mode 100644 index 000000000..f8375a674 Binary files /dev/null and b/docs/img/armadillo-logo-border.png differ diff --git a/docs/img/ds-complete-setup.png b/docs/img/ds-complete-setup.png new file mode 100644 index 000000000..9947692c4 Binary files /dev/null and b/docs/img/ds-complete-setup.png differ diff --git a/docs/img/ds-simple-setup.png b/docs/img/ds-simple-setup.png new file mode 100644 index 000000000..61ef40859 Binary files /dev/null and b/docs/img/ds-simple-setup.png differ diff --git a/docs/img/favicon.ico b/docs/img/favicon.ico new file mode 100644 index 000000000..93fef3a77 Binary files /dev/null and b/docs/img/favicon.ico differ diff --git a/docs/img/overview-datashield.png b/docs/img/overview-datashield.png deleted file mode 100644 index 9342fc0be..000000000 Binary files a/docs/img/overview-datashield.png and /dev/null differ diff --git a/docs/img/project-file-structure.png b/docs/img/project-file-structure.png new file mode 100644 index 000000000..8fe06a13a Binary files /dev/null and b/docs/img/project-file-structure.png differ diff --git a/docs/img/swagger.png b/docs/img/swagger.png new file mode 100644 index 000000000..494b1ca65 Binary files /dev/null and b/docs/img/swagger.png differ diff --git a/docs/img/ui-non-admin-message.png b/docs/img/ui-non-admin-message.png new file mode 100644 index 000000000..76a883cbb Binary files /dev/null and b/docs/img/ui-non-admin-message.png differ diff --git a/docs/img/ui-projects.png b/docs/img/ui-projects.png new file mode 100644 index 000000000..d3025a48d Binary files /dev/null and b/docs/img/ui-projects.png differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 47478e291..000000000 --- a/docs/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Armadillo suite documentation - - - - - - -
    - - - - - - - - diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..c0af7b73a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,13 @@ +. +{.before-intro} + +![Image title](/img/armadillo-logo-border.png){ align=right .armadillo-intro-logo} +

    MOLGENIS Armadillo

    +A simple, user-friendly, open source solution for implementing federated analysis.


    +Armadillo is a data portal that allows data stewards to share datasets on a server, +and researchers to analyse these data and those shared on other servers using +the DataSHIELD analysis tools.

    +{.intro} +> Why is it called Armadillo?
    +> - The word Armadillo is Spanish and means "little armored one". MOLGENIS Armadillo is a compact solution that implements +> the DataSHIELD infrastructure. \ No newline at end of file diff --git a/docs/ops/configuring.md b/docs/ops/configuring.md deleted file mode 100644 index 55890b1b1..000000000 --- a/docs/ops/configuring.md +++ /dev/null @@ -1,23 +0,0 @@ -# Using Armadillo -Armadillo has three main screens to manage projects, user access and DataSHIELD profiles: - -### Create data access projects -Data stewards can use the Armadillo web user interface or [MolgenisArmadillo R client](https://molgenis.github.io/molgenis-r-armadillo) -to create 'projects' and upload their data into those. Data tables need to be in parquet format that supports fast selections of the columns -(variables) you need for analysis. Other files can be configured as 'resources'. - -### Manage user access -Data stewards can use the permission screen to give email adresses access to the data. Everybody signs in via single sign on using an OIDC central -authentication server such as KeyCloack or Fusion auth that federates to authentication systems of -connected institutions, ideally using a federated AAI such as LifeScience AAI. - -### Configure DataSHIELD profiles -To analyse data, users must choose a datashield profile. Armadillo owners can use the web user interface to configure new profiles. Assuming you -installed docker you can also start/stop these images. Alternatively you can use docker-compose for that. We -recommend selecting one of the -[DataSHIELD standard profiles](https://www.datashield.org/help/standard-profiles-and-plaforms). - -### End users can use Armadillo as any other DataSHIELD server -A researcher connects from an [R client](https://molgenis.github.io/molgenis-r-datashield) to one or multiple Armadillo servers. The data is -loaded into an R session on the Armadillo server specifically created for the researcher. Analysis requests are sent to the R session on each Armadillo server. -There the analysis is performed and aggregated results are sent back to the client. diff --git a/docs/ops/install/install_apache.md b/docs/ops/install/install_apache.md deleted file mode 100644 index 11379e7d1..000000000 --- a/docs/ops/install/install_apache.md +++ /dev/null @@ -1,32 +0,0 @@ -# Apache - -It is possible to run Molgenis Armadillo using Apache, however we do not provide support with this configuration. - -## Encoding - -Apache requires some additional configuration to get the `/storage/projects/{project}/objects/{object}` to work. When this endpoint doesn't work: - -- tables cannot be assigned -- subsets cannot be created -- resources cannot be used. - -## Tell Armadillo about https - -We need to tell Armadillo server how to building URLs. - -## Changes to your site-enabled configuration - -Your configuration probably should look like this. - -```conf -ProxyPreserveHost On - -ProxyPass / http://localhost:8080/ nocanon - -AllowEncodedSlashes On - -RequestHeader set X-Forwarded-Proto https -RequestHeader set X-Forwarded-Port 443 -``` - -After setting this don't forget to restart Apache. diff --git a/docs/ops/install/install_docker.md b/docs/ops/install/install_docker.md deleted file mode 100644 index 6f3e476f9..000000000 --- a/docs/ops/install/install_docker.md +++ /dev/null @@ -1,10 +0,0 @@ -## Run Armadillo via docker compose -For testing without having to installing Java you can run using docker: - -1. Install [docker-compose](https://docs.docker.com/compose/install/) -2. Download this [docker-compose.yml](docker-compose.yml). -3. Execute ```docker-compose up``` -4. Once it says 'Started' go to http://localhost:8080 to see your Armadillo running. - -The command must run in the same directory as the downloaded docker file. We made docker available via 'docker.sock' so we can start/stop DataSHIELD profiles. Alternatively you must include the datashield profiles into this docker-compose. You can override all application.yaml settings via environment variables -(see commented code in docker-compose file). diff --git a/docs/ops/install/install_java.md b/docs/ops/install/install_java.md deleted file mode 100644 index 1c00246ea..000000000 --- a/docs/ops/install/install_java.md +++ /dev/null @@ -1,13 +0,0 @@ -## Run Armadillo using java commandline -Software developers often run Armadillo as java jar file: - -1. Install Java and Docker (for the DataSHIELD profiles) -2. Download Armadillo jar file from [releases](https://github.com/molgenis/molgenis-service-armadillo/releases), for example: -[molgenis-armadillo-3.3.0.jar](https://github.com/molgenis/molgenis-service-armadillo/releases/download/V3.3.0/) -3. Run armadillo using ```java -jar molgenis-armadillo-3.3.0.jar``` -4. Go to http://localhost:8080 to see your Armadillo running. - -Default Armadillo will start with only 'basic-auth' and user 'admin' with password 'admin'. You can enable 'oidc' for connecting more users. You can change -by providing and editing [application.yaml](application.template.yml) file -in your working directory and then run command above again. - diff --git a/docs/overrides/partials/copyright.html b/docs/overrides/partials/copyright.html new file mode 100644 index 000000000..440b79c14 --- /dev/null +++ b/docs/overrides/partials/copyright.html @@ -0,0 +1,13 @@ + diff --git a/docs/pages/about_us.md b/docs/pages/about_us.md new file mode 100644 index 000000000..9a2186f2e --- /dev/null +++ b/docs/pages/about_us.md @@ -0,0 +1,7 @@ +# About us +Armadillo is part of the [MOLGENIS community](https://molgenis.org/communities.html). We are an +[enthusiastic group](https://molgenis.org/team.html) of datamanagers, systemoperator and developers. We have a large +number of national and international partners and value research, data-sharing, collaboration and open source software. + +We are also involved in the [DataSHIELD community](https://wiki.datashield.org/). This community is very passionate when +it comes to federated analyses and sharing data securely. They are very welcoming to anyone willing to participate. \ No newline at end of file diff --git a/docs/pages/advanced_usage.md b/docs/pages/advanced_usage.md new file mode 100644 index 000000000..9b3e398a5 --- /dev/null +++ b/docs/pages/advanced_usage.md @@ -0,0 +1,6 @@ +# Advanced Usage +API usage + +monitoring + +Logs \ No newline at end of file diff --git a/docs/pages/basic_concepts.md b/docs/pages/basic_concepts.md new file mode 100644 index 000000000..94880d4bb --- /dev/null +++ b/docs/pages/basic_concepts.md @@ -0,0 +1,42 @@ +# Basic concepts +[DataSHIELD](https://datashield.org/) is an R-based software solution for federated analysis - the remote analysis of multiple data sources. +It allows for sophisticated analyses without the user being able to view or copy individual level data. Instead, only non-disclosive +summary statistics are returned. This makes it an effective solution for secure data science collaborations. + +In order to use DataSHIELD, additional software is required to store data and manage user interaction. There are currently two solutions for doing this: Armadillo and [Opal](https://opaldoc.obiba.org/), which can be used compatibly within the same network. Below is an example of a simple setup: + +### Example setup 1 +![Simple setup](../img/ds-simple-setup.png){ width="600" } +/// caption +A user writes analysis commands in R, using client-side DataSHIELD R packages. The commands are sent to the biobank +servers. For Armadillo, the communication between the client and the server is handled by the R packages [DSI](https://github.com/datashield/DSI) and [DSMolgenisArmadillo](https://molgenis.github.io/molgenis-r-datashield/), whilst the data storage and execution of commands on the server is handled by [ArmadilloService](https://molgenis.github.io/molgenis-service-armadillo/). The non-disclosive summary statistics are then returned to the user. +/// + +### Example setup 2 +An alternative setup involves the user first connecting to a Central Analysis Server (CAS), which is an online R studio environment: + +![ds-complete-setup.png](../img/ds-complete-setup.png) +/// caption +Once logged in to the CAS, users write their code as if they were running RStudio locally. The advantage of this setup is that Biobank servers can be configured so that they are blocked off from the rest of the internet by a firewall and can only be accessed from the CAS. This provices an additional layer of data protection. It also benefits users, as all required DataSHIELD R packages can be pre-installed thus removing the needs for users to set up their R environment. +/// + +### Resources +An additional optional feature of DataSHIELD is the ability to host files elsewhere (e.g. computer clusters) and link them to +the data of armadillo or opal servers. This is impletmented using the [resourcer](https://github.com/obiba/resourcer) package. External resources can +be used alongside data stored in armadillo itself, and resources may be hosted in different locations and formats. + +### DataSHIELD packages and their use +Finally, here is a brief summary of the core Armadillo and DataSHIELD packages described in this documentation. + +| **Name** | **Type of application** | **User type** | **Description** | Sources | +|---------------------:|------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| armadilloService | :material-language-java: Java | - | The software that facilitates hosting and analysing data using DataSHIELD. | [:simple-github:](https://github.com/molgenis/molgenis-service-armadillo) | +| DSI | :simple-r: R package | - | This package defines the API that is to be implemented by DataSHIELD compliant data repositories. | [:simple-github:](https://github.com/datashield/DSI) | +| DSMolgenisArmadillo | :simple-r: R package | Researchers | To communicate between the client-side packages and armadilloService to perform analysis. | [:simple-github:](https://github.com/molgenis/molgenis-R-datashield) [:simple-r:](https://cran.r-project.org/web/packages/DSMolgenisArmadillo/index.html) | +| MolgenisArmadillo | :simple-r: R package | Datamanager | To upload data and manage projects in Armadillo in R. | [:simple-github:](https://github.com/molgenis/molgenis-R-armadillo) [:simple-r:](https://cran.r-project.org/web/packages/MolgenisArmadillo/index.html) | +| User Interface| :simple-javascript: Javascript/
    :material-vuejs: VueJS | Datamanager | To manage users, data, configurations and view logs. | [:simple-github:](https://github.com/molgenis/molgenis-service-armadillo/tree/master/ui) | +| dsUpload | :simple-r: R package | Datamanager | To upload data according to a specific format into Armadillo/Opal using R. | [:simple-github:](https://github.com/lifecycle-project/ds-upload) | +| dsBaseClient | :simple-r: R package | Researchers | Core DataSHIELD client-side R package required for executing basic DataSHIELD commands. | [:simple-github:](https://github.com/datashield/dsBaseClient) | +| dsTidyverseClient | :simple-r: R package | Researchers | Client-side R package which implements efficient data-manipulation using selected Tidyverse functions. | [:simple-github:](https://github.com/molgenis/ds-tidyverse-client) [:simple-r:](https://cran.r-project.org/web/packages/dsTidyverseClient/index.html) | + + diff --git a/docs/pages/basic_usage/armadillo_ui.md b/docs/pages/basic_usage/armadillo_ui.md new file mode 100644 index 000000000..67e4265ae --- /dev/null +++ b/docs/pages/basic_usage/armadillo_ui.md @@ -0,0 +1,172 @@ +# Armadillo UI + +Relevant for: :material-file-table:{title="Data Managers"} + +Armadillo features a user interface, or UI for short. + +## Login + +![Login screen of Armadillo](../../img/ui/login.png) + +To login to the UI, select the **Institute account (oath2)** button and login using the institute or organisation login screen you will be redirected to. + +### Superuser + +You need to have admin or superuser permissions if you want to add projects, users or profiles. This means you need to be granted permission in order to be able to use the UI. If you don't have correct permissions, you will receive the +following error: + +![Error message in case you are not a superuser](../../img/ui/no-superuser.png) + +If you receive this error, contact someone in your institute that is already able to login without an error, or [contact MOLGENIS Support](../contact.md). + +To grant a user superuser permissions simply search for that user in the `Users` tab of the UI, and tick the _admin_ checkbox for that user: + +![Grant user superuser rights](../../img/ui/admin.png) + +## Projects + +Once you're logged in, you will be redirected to the `Projects` page. On this page you can add and edit projects. + +You can add users to projects and navigate to the "project-editor"-view and search through the projects using the search bar on the top right. + +![Armadillo projects page](../../img/ui/projects.png) + +### Editing projects + +To edit your project, click on the edit button in front of the project you want to edit: ![Armadillo edit project](../../img/ui/edit.png){width="15"}. + +The row will be opened in edit mode: + +![Edit a specific row on the projects page](../../img/ui/edit-project.png) + +The edit mode can be recognized by its blue background color and you have the option to add new users to your project by clicking on the + button ![Armadillo plus user](../../img/ui/plus.png){width="15"} of the users column. Then, you can either select an existing user from the dropdown, or add the email address of a new user. + +![Add an existing user to a project or enter the email address of the new user](../../img/ui/edit-projects-add-user.png) + +In case of adding a user in this screen, a warning will be shown to prevent email addresses with typographical errors from being added to your system. For example, if you have the address `j.doe@example.com` in your users table but attempt to add `j.die@example.com` a warning message will be displayed asking if you want to add a new user. + +![Warning message to remind you to save the project in order to finalize adding a new user](../../img/ui/add-user-warning.png) + +It is not possible to edit the name of your project; this is intentional in order to ensure tables, resources, users, and permissions are transferred to the new project name correctly. + +Click on the checkmark ![Armadillo project check](../../img/ui/check.png){width="25"} to save the edited row and the X ![Armadillo project cancel](../../img/ui/cancel.png){width="25"} to cancel. + +Be careful, if you do cancel your changes will be lost. + +### Adding projects + +To add a new project, click on the + button ![Armadillo project add](../../img/ui/add.png){width="25"} on top of the table. If you click this button, an empty row will be opened in edit mode. + +![Add a new project to Armadillo](../../img/ui/add-project.png) + +Click on the checkmark ![Armadillo project check](../../img/ui/check.png){width="25"} to save the edited row and the X ![Armadillo project cancel](../../img/ui/cancel.png){width="25"} to cancel. + +Be careful, if you do cancel your changes will be lost. + +## Project explorer + +If you click on the icon ![Armadillo project view](../../img/ui/view-project.png){width="40"} next to the project name, you will be directed to the `project editor`. In this screen you can upload and preview data in projects. To go back to your `projects` page, +press the back button ![Armadillo project back](../../img/ui/back-button.png){width="25"}. + +![Armadillo project editor](../../img/ui/project-editor.png) + +If you click on a folder, it will open. + +![Armadillo project folder](../../img/ui/view-project-folder.png) + +Here you can upload files to that folder, or click on the tables (files) to preview their contents. + +![Armadillo project file preview](../../img/ui/preview-file.png) + +To upload files, either drag a file from your file browser to the file upload area, or click the area and select the +file. + +![Armadillo upload a file](../../img/ui/upload-a-file.png) + +After selecting the file, click on _upload_ to upload it. This usually only takes a few seconds but will take longer in your uploading a large file. + +It is also possible to create new folders. To do so, click the "add folder" button ![Armadillo project add folder](../../img/ui/add-folder-button.png){width="30"} just below the project name. An input dialog will be presented: + +![Armadillo add a folder](../../img/ui/add-folder.png) + +Fill in the name you want to use and click on the checkmark button ![Armadillo project check](../../img/ui/check.png){width="25"}. Please keep in mind that the folder will only be saved if you put data into it. Select the new folder to select files to upload. + +### Resources + +All file types can be uploaded into Armadillo, however previews will only be available for `.parquet` files. Other files that can be uploaded are treated as _resources_. Resource filetypes usually are `.rda` files or `.Rdata` files. + +To be able to use these resources as a researcher, first an `.rds` file must be generated. How to create these files, is described [here](https://molgenis.github.io/molgenis-r-armadillo/articles/create_resources.html). + +The URL of your resources should consist of: + +```r +{your url}/storage/projects/{project name}/objects/{name of the folder}%2F{the resource file} +``` + +Here is an example, with some example parameters: + +```r +url = "https://armadillo3.demo.molgenis.net" +project = "omics" +folder = "ewas" +file = "gse66351_1.rda" +``` + +Which results in the following url: + +```r +https://armadillo3.demo.molgenis.net/storage/projects/omics/objects/ewas%2Fgse66351_1.rda +``` + +## Users + +The `Users` page works just as the `Projects` page. You can search users by entering (a part of) the email address or name of the user into the search box: + +![Search for a user in the Users page](../../img/ui/admin.png) + +### Editing users + +Users can be edited, **except** for their email addresses. This is because when user's email address changes, that user is possibly no longer working for the same institution and therefore might not be allowed to access the data anymore. + +![Editing a user on the Users page](../../img/ui/edit-user.png) + +In edit mode, the row will turn blue. Projects can be added by clicking on the + icon ![Armadillo user plus](../../img/ui/plus.png){width="15"}in the projects column. + +![Armadillo add user to project](../../img/ui/add-project-to-user.png){width="350"} + +You can add a new project by typing it and clicking the checkmark button ![Armadillo project check](../../img/ui/check.png){width="25"}. You will be prompted with a warning message, asking you to confirm if you want to add a new project. + +Alternatively, you can select an existing project by using the search box or scrolling through the presented list. + +### Adding users + +By clicking on the plus button ![Armadillo user add](../../img/ui/add.png){width="25"} on the top of the table, a new user can be added. The row with the new user will turn blue in edit mode. + +![Armadillo user add](../../img/ui/add-user.png){width="650"} + +Users can be added before they have logged in previously. These users can be added to projects, which will grant them permission to use the data from those projects upon their first login. Researchers should not be set as admin. + +## Analysis Profiles + +![Armadillo Profiles page](../../img/ui/profiles.png) + +Since the release of Armadillo 3.0.0, it is possible to create and manage analysis profiles in the user interface, rather than asking system administrators to manage these profiles. You can start ![Armadillo profile start](../../img/ui/start-profile.png){width="35"} and stop ![Armadillo profile start](../../img/ui/stop-profile.png){width="35"} profiles. + +When you start a profile for the first time, it will take a bit longer to load because the profile needs to be downloaded and installed before it can be started. + +![Stopping a profile](../../img/ui/loading-stop-profile.png) + +If you switch to another screen whilst either starting or stopping a profile, the profiles page will no longer show the loading information. It is however still loading, and when it's done, if you reload the page, you will see that your profile started or stopped successfully. + +As in the other screens, you can add profiles with the add-button ![Armadillo profile add](../../img/ui/add.png){width="25"}. + +![Add a profile](../../img/ui/add-profile.png) + +By default, some fields will be set. Please update them to install the correct profile. + +Possible images can be found on [dockerhub](https://hub.docker.com/search?q=datashield%2Farmadillo-rserver). We recommend selecting one of the +[DataSHIELD standard profiles](https://wiki.datashield.org/en/opmanag/standard-profiles-and-platforms). The image name of those profiles can be found on the dockerhub link above. + +Although the default `port` setting should find an available port, please keep in mind that the port has to be unique, otherwise you cannot start your profile and will receive an error message. + +R packages can be whitelisted by adding them to the `package whitelist` column so researchers can use them. 'Whitelisting' a package allows analysts to use it. If you want to whitelist a package, you need to make sure it is installed on the image you selected. Additionally, it is possible to blacklist certain R functions in the `blacklist function` column. This can be useful if certain functions are not allowed to be used on certain data or within certain cohorts. diff --git a/docs/pages/basic_usage/auth.md b/docs/pages/basic_usage/auth.md new file mode 100644 index 000000000..21a59cdd5 --- /dev/null +++ b/docs/pages/basic_usage/auth.md @@ -0,0 +1,5 @@ +# Authentication server + +Relevant for: :material-server:{title="System Operator"} + +Authentication server: [https://lifecycle-auth.molgenis.org/](https://lifecycle-auth.molgenis.org/) \ No newline at end of file diff --git a/docs/pages/basic_usage/central_analysis_server.md b/docs/pages/basic_usage/central_analysis_server.md new file mode 100644 index 000000000..f9b810187 --- /dev/null +++ b/docs/pages/basic_usage/central_analysis_server.md @@ -0,0 +1,7 @@ +# Central Analysis Server (CAS) + +Relevant for: :material-layers-search:{title="Researchers"} + +Most networks use the analysis environment instead of their locally installed R/Rstudio environment. The analysis environment is centralized. You will be using a web based RStudio which is available here: [https://lifecycle.analysis.molgenis.org/](https://lifecycle.analysis.molgenis.org/) + +The benefit of using a CAS server is a additional layer of security, only traffic from the CAS server is accepted by the Armadillo server. diff --git a/docs/pages/basic_usage/ds_molgenis_armadillo.md b/docs/pages/basic_usage/ds_molgenis_armadillo.md new file mode 100644 index 000000000..c52a90b7b --- /dev/null +++ b/docs/pages/basic_usage/ds_molgenis_armadillo.md @@ -0,0 +1,70 @@ +# DSMolgenisArmadillo + +Relevant for: :material-layers-search:{title="Researchers"} + +Use DSMolgenisArmadillo to analyse data shared in MOLGENIS Armadillo servers using DataSHIELD. DataSHIELD allows execution of a subset of analysis methods available in R. Methods such as: + +`ds.mean()` `ds.glm()` `ds.lmerSLMA()` + +For more detailed information please visit [DSMolgenisArmadillo](https://molgenis.github.io/molgenis-r-datashield/) and additionally check the documentation: [https://cran.datashield.org/](https://cran.datashield.org/). + +=== "Prerequisite" + + ???+ example + + ```r linenums="1" title="Prerequisite.r" + install.packages("DSI") + install.packages("DSMolgenisArmadillo") + install.packages("dsBaseClient", repos = c("http://cran.datashield.org", "https://cloud.r-project.org/"), dependencies = TRUE) + + library(dsBaseClient) + library(DSMolgenisArmadillo) + ``` +=== "Basic usage" + ???+ example + + ```r linenums="1" title="Analyse_data_subset_DSMolgenisArmadillo.r" + library(dsBaseClient) + library(DSMolgenisArmadillo) + + # specify server url + armadillo_url <- "https://armadillo.test.molgenis.org" + + # get token from central authentication server + token <- armadillo.get_token(armadillo_url) + + + # build the login dataframe + builder <- DSI::newDSLoginBuilder() + builder$append( + server = "armadillo", + url = armadillo_url, + token = token, + table = "workshop1/2_1-core-1_0/nonrep", + driver = "ArmadilloDriver" + ) + + # create loginframe + logindata <- builder$build() + + # login into server + conns <- datashield.login( + logins = logindata, + symbol = "core_nonrep", + variables = c("coh_country"), + assign = TRUE + ) + + # calculate the mean + ds.mean("core_nonrep$coh_country", datasources = conns) + + ds.histogram(x = "core_nonrep$coh_country", datasources = conns) + ``` + +!!! info + A researcher only has access to data if he/she has been granted access to this data (typically) by a data manager or administrator of a cohorts Armadillo server. + +!!! note + The example files used in the examples above can be found here: [nonrep.parquet](../../data/nonrep.parquet), [yearlyrep.parquet](../../data/yearlyrep.parquet) + + Load these `parquet` files in R and see which variables you can call. diff --git a/docs/pages/basic_usage/dslite.md b/docs/pages/basic_usage/dslite.md new file mode 100644 index 000000000..af4711613 --- /dev/null +++ b/docs/pages/basic_usage/dslite.md @@ -0,0 +1,10 @@ +# DSLite + +Relevant for: :fontawesome-solid-laptop-code:{title="Developers"} + +DataSHIELD Lite ([DSLite](https://github.com/datashield/DSLite)) is a serverless [DataSHIELD Interface (DSI)](https://datashield.github.io/DSI/) implementation which purpose is to mimic the behavior of a distant (virtualized or barebone) data repository server like Armadillo or Opal. The datasets that are being analyzed are fully accessible in the local environment. The DataSHIELD configuration (set of allowed aggregation and assignment functions) is discovered at runtime by inspecting the DataSHIELD server-side R packages installed locally. This configuration can also be amended or provided explicitly. + +DSLite can be used to: + +- speed up development and testing cycle when developing new DataSHIELD functions (both at server and client side): no need to deploy a data repository infrastructure. +- allow DataSHIELD analysis with combined datasets, some of them being accessible remotely in secure data repositories, others being privately accessible (in a governmental institution for instance). diff --git a/docs/pages/basic_usage/dsupload_dsdictionaries.md b/docs/pages/basic_usage/dsupload_dsdictionaries.md new file mode 100644 index 000000000..c4a072b3f --- /dev/null +++ b/docs/pages/basic_usage/dsupload_dsdictionaries.md @@ -0,0 +1,10 @@ +# DsUpload & DsDictionaries + +Relevant for: :material-file-table:{title="Data Managers"} + +DataSHIELD upload tools ([DsUpload](https://lifecycle-project.github.io/ds-upload/)) s a collections of tools used to upload data into DataSHIELD backends like Armadillo and Opal. It aids data managers in the initial stages of uploading data to DataSHIELD backends. + +DsUpload depends on [DsDictionaries](https://github.com/lifecycle-project/ds-dictionaries) which holds the [harmonised data dictionaries](https://github.com/lifecycle-project/ds-dictionaries/tree/master/dictionaries) of multiple projects like [Lifecycle](https://data-catalogue.molgeniscloud.org/catalogue/ssr-catalogue/LifeCycle) and [Athlete](https://data-catalogue.molgeniscloud.org/catalogue/ssr-catalogue/ATHLETE). + +!!! info + Visit MOLGENIS [European Health Research Data and Sample Catalogue](https://data-catalogue.molgeniscloud.org/) which is used by projects like Lifecycle and Athlete to harmonise their variables! diff --git a/docs/pages/basic_usage/index.md b/docs/pages/basic_usage/index.md new file mode 100644 index 000000000..e15dd166f --- /dev/null +++ b/docs/pages/basic_usage/index.md @@ -0,0 +1,22 @@ +# Basic Usage + +Depending on your [role](../quick_start.md#quick-start) you have different options to interact with MOLGENIS Armadillo. + +=== ":material-file-table: Data Manager" + + A [data manager](../quick_start.md#data-manager) will typically interact with Armadillo in multiple ways: + + - Using the [Armadillo User Interface (UI)](../basic_usage/armadillo_ui.md) + - Using a R client [MolgenisArmadillo](../basic_usage/molgenis_armadillo.md) + - Using [dsUpload](../basic_usage/dsupload_dsdictionaries.md) (R) to upload data with the help of predefined harmonised data dictionaries ([ds-dictionaries](../basic_usage/dsupload_dsdictionaries.md)). + +=== ":material-server: System Operator" + [System operator](../quick_start.md#system-operator): [Authentication server](../basic_usage/auth.md) + +=== ":material-layers-search: Researcher" + + A [researcher](../quick_start.md#researcher) will connect to a Armadillo server with a R client [DSMolgenisArmadillo](../basic_usage/ds_molgenis_armadillo.md), this client allows for execution of DataSHIELD functions. Depending on the way your cohort has been setup you might use the R client through the [Central Analysis server (CAS)](../basic_usage/central_analysis_server.md). In both cases the researcher has installed R and Rstudio to edit and run their analysis. + +=== ":fontawesome-solid-laptop-code: Developer" + + A [developer](../quick_start.md#developer) can use DataSHIELD Lite ([DSLite](../basic_usage/dslite.md)) as a serverless [DataSHIELD Interface (DSI)](https://datashield.github.io/DSI/) implementation which purpose is to mimic the behavior of a distant (virtualized or barebone) data repository server like Armadillo or Opal. diff --git a/docs/pages/basic_usage/molgenis_armadillo.md b/docs/pages/basic_usage/molgenis_armadillo.md new file mode 100644 index 000000000..da2a2a458 --- /dev/null +++ b/docs/pages/basic_usage/molgenis_armadillo.md @@ -0,0 +1,252 @@ +# MolgenisArmadillo + +Relevant for: :material-file-table:{title="Data Managers"} + +MolgenisArmadillo is a R library that can be used by data managers to share datasets on a MOLGENIS Armadillo server. Researchers can then analyse these datasets and datasets shared on other servers using DataSHIELD. Researchers will only be able to access aggregate information and cannot see individual rows. + +For more detailed information please visit [MolgenisArmadillo](https://molgenis.github.io/molgenis-r-armadillo/index.html). + +Below some basic operations to get you started using MolgenisArmadillo: + +=== "Prerequisite" + + ???+ example + + ```r linenums="1" title="Prerequisite.r" + # install the following packages + install.packages("MolgenisArmadillo") + install.packages("dplyr") + install.packages("arrow") + # After installation make sure you can load the libraries + library(MolgenisArmadillo) + library(dplyr) + library(arrow) + # verify the loaded libraries (see: other attached packages) + sessionInfo() + ``` +=== "Basic usage" + + ???+ example + + ```r linenums="1" title="MolgenisArmadillo.r" + ## Prerequisites + # + # - Run the code in Prerequisite.r + # + library(MolgenisArmadillo) + + ## + # To share your data using Armadillo, you first need to login + + ## Login + # + # In order to access the files as a data manager, you need to log in. + # The login method needs the URLs of the Armadillo server. + # It will open a browser window where you can identify yourself with the ID provider. + # + # If you are unsure how this login function (or any function) works ask R to provide documentation + ?armadillo.login + + armadillo.login( + armadillo = "https://armadillo.test.molgenis.org" + ) + #token <- armadillo.get_token("https://armadillo.test.molgenis.org") + + # armadillo.login will create a session and store the credentials in the environment. + + ## Structure + # To share data via Armadillo you can have a nested structure to save you data. + + # We distinguish: + # - projects + # - folders + # - tables + + ### Projects + # Projects are root-folders you can give persons permissions on. + # you can imagine that you will use a separate project for each study you need to support. + # This way you make sure people can not see each others variables. + + ### Folders + # Folder objects can be used to version the different tables you want to share in Armadillo. + # This is not mandatory and are free to use the folder level as you see fit. + # In our examples we will go into the versioning part a bit deeper. + + ### Tables + # Tables contain the data you want to share. + # This can be all the data on a certain subject, mostly used in consortia or a specific study you want to expose. + + ## Sharing data + # Assume you are in a consortia which has core-variables and outcome-variables. + # You want to share and version the whole dataset to all researchers which applied to access your data. + + # First we will create the project. In our case "ipec". + + cohort <- "workshop2" + armadillo.create_project(cohort) + + # Secondly we will load the table(s) we want to upload to Armadillo in the R-environment. + # We have test data which is in `arrow` format, the upload will take any object that has a table like structure to upload into the Armadillo. + # This can be SPSS, STATA, SAS or R-based data as well. + + library(arrow) + + # load the core data (make sure you are working in the correct directory) + nonrep <- arrow::read_parquet("data/nonrep.parquet") + yearlyrep <- arrow::read_parquet("data/yearlyrep.parquet") + + # explore your data (sanity check) + View(nonrep) + dim(nonrep) + names(nonrep) + table(nonrep$recruit_age) + + # The third step is determining the second level, which contains in this case the datamodel-version the type of variables and the data-version. + + # y_y-#variable-type#-x_x + + # y_y = datamodel version + # x_x = data version + + # upload the core variables + armadillo.upload_table(cohort, "2_1-core-1_0", nonrep) + armadillo.upload_table(cohort, "2_1-core-1_0", yearlyrep) + + ## Looking at the data + # There are helper functions to help you determine what is in the storage server. + # You can list projects and tables to what's in the storage. + + # list of projects + armadillo.list_projects() + + # listing tables per project + armadillo.list_tables(cohort) + + # You can download the data in the R-environment as well. + + # download table to local R environment + download_nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") + + # check the column names from the local environment + colnames(download_nonrep) + + # check if local data and uploaded data are equal (optional) + setequal(nonrep, download_nonrep) + + # Now you can also take a look at the files in the user interface of the MinIO file server + # open the MinIO server URL in a browser window (used in armadillo.login). + + # > !IMPORTANT: run this part after subsetting the data + + ## Deleting the data + # To delete the data you need to throw away the contents first. + + # throw away the core tables + armadillo.delete_table(cohort, "2_1-core-1_0", "nonrep") + armadillo.delete_table(cohort, "2_1-core-1_0", "yearlyrep") + + # Now you can delete the project. + armadillo.delete_project(cohort) + ``` +=== "Create subsets" + + ???+ example + + ```r linenums="1" title="Creating_data_subsets_Armadillo.r" + ## Creating data subsets on the Armadillo server + # When researchers request access to your data they may in many cases not be granted access to the whole data set, but only to a subset. + # Here you can see the different relevant steps you need to take to create these subsets. + # + # - Setting up the environment + # - Logging into the servers + # - Exploring the data + # - Making subsets of the data + # - Upload data subsets + # + # - Delete data subsets + + ## Setting up the environment + # - Run the code in 0_Prerequisite.r + + # load required libraries + library(MolgenisArmadillo) + library(dplyr) + + ## Logging in to the servers + # In order to access the files you need to log in using the URLs of the Armadillo server. + # A browser window will be opened where you can identify yourself with the ID provider. + + armadillo.login( + armadillo = "https://armadillo.test.molgenis.org" + ) + # A session will be created and the credentials are stored in the environment. + + ## Explore the data + # Let's assume you are in a consortium which has core-variables and outcome-variables. + # You want to share a subset of the whole data set with certain researchers that applied for access to your data. + + # List projects on the Armadillo server. + armadillo.list_projects() + + # Next create a study, here called 'subset1'. + # NOTE: change the name of the subset for your own practice run + + study <- "study1" + armadillo.create_project(study) + + # List the tables in a project + + # You want to share data from the cohort you just uploaded (MolgenisArmadillo.r). + # Change the cohort_name to your own cohort name here: + cohort = "workshop1" + + # List the available tables within this project. + armadillo.list_tables(cohort) + + # Subset the core variables + # Download the relevant core tables to the local environment + + nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") + yearlyrep <- armadillo.load_table(cohort, "2_1-core-1_0", "yearlyrep") + + # List their variables + + colnames(nonrep) + colnames(yearlyrep) + + # Subset the variables that were requested per table. + + subset_core_nonrep <- nonrep %>% select(child_id, asthma_m, preg_cig, preg_fever, preg_alc) + subset_core_yearlyrep <- yearlyrep %>% select(child_id, cohab_, smk_exp) + + ## Uploading the data subset + # Check the variables in the data subset before uploading + + colnames(subset_core_nonrep) + colnames(subset_core_yearlyrep) + + # Upload the data subsets + armadillo.upload_table(study, "2_1-core-1_0", subset_core_nonrep, "nonrep") + armadillo.upload_table(study, "2_1-core-1_0", subset_core_yearlyrep, "yearlyrep") + + # See if tables are uploaded + armadillo.list_tables(study) + + # Now you can also take a look at the files in the user interface of the Armadillo server. In this case: https://armadillo.test.molgenis.org/#/projects + + # > !IMPORTANT: run this part after subsetting the data + + ## Deleting the data + # To delete the data you need to throw away the contents first. + + # throw away the core tables + armadillo.delete_table(study, "2_1-core-1_0", "nonrep") + armadillo.delete_table(study, "2_1-core-1_0", "yearlyrep") + + # Now you can delete the project. + + armadillo.delete_project(study) + ``` + +!!! note + The example files used in the examples above can be found here: [nonrep.parquet](../../data/nonrep.parquet), [yearlyrep.parquet](../../data/yearlyrep.parquet) diff --git a/docs/pages/contact.md b/docs/pages/contact.md new file mode 100644 index 000000000..a0a7d9e40 --- /dev/null +++ b/docs/pages/contact.md @@ -0,0 +1,3 @@ +# Contact + +MOLGENIS Support (support@molgenis.org). \ No newline at end of file diff --git a/docs/pages/dev_guides.md b/docs/pages/dev_guides.md new file mode 100644 index 000000000..a12f56a59 --- /dev/null +++ b/docs/pages/dev_guides.md @@ -0,0 +1,176 @@ +# Developer Guidelines +The Armadillo and DataSHIELD community both are very welcoming to anyone who wants to contribute in any sort of form. +One way of doing that is by helping with the development of Armadillo. To help you get started, we've put together some +information to get you started and help you get familiarised with our code and way of working. + +=== ":fontawesome-solid-circle-info: General information" + +

    Commits and automatic releases

    + + Versionnumbers are updated according to [semantic versioning](https://semver.org/), + using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). + + It is especially important to correctly identify and commit user-facing changes. These being: bugfixes and new + features. The correct prefix to a commit will trigger an automatic release. Automatic releases are set to be + pre-releases. + + Each commit with `!` just before the colon `:` is a major update, indicating a breaking change. So use it wisely. + You can also add `BREAKING CHANGE:` in the long commit message format. + + - Use `feat!: ...` or `fix!: ...` for a major upgrade, indicating a breaking change. + - Use `feat: ...` for a minor upgrade, indicating a new feature. + - Use `fix: ...` for a patch update, indicating a bugfix. + +

    Releases

    + As mentioned above, the automatic releases in Armadillo are pre-releases. Normal releases are done manually whenever + deemed necessary. Of course a new release can be requested anytime. + +

    Testing and continuous integration

    + All our repositories contain unittests. Although we usually aim for a coverage of 80%, sometimes we make an + exception. Tests are ran as part of our continuous integration, on each pull request and each merge with the master. + +

    Pull request

    + We encourage everyone to contribute to MOLGENIS armadillo. We do however have a couple of requirements for a pull + requests, mostly because of our automatic processes. + + 1. Start the title of your PR with prefix using conventional commits (see general information tab) + 2. When you're fixing an issue, include "closing #issuenumber" or "fixes #issuenumber" in a commit in the PR + 3. Describe what your PR does shortly and how to test it. + +

    Armadillo API and Swagger

    + Armadillo can be controlled using its REST API. To see and test all available endpoints, you can visit Armadillo's + swaggerpage. Swagger is a very useful tool for exactly this purpose. The page can be found on + `/swagger-ui/index.html` of each Armadillo instance. If you want to see an example of our swagger page, you can + visit it on [our demo server](https://armadillo-demo.molgenis.net/swagger-ui/index.html). + + ![swagger.png](../img/swagger.png){ width="700" } + ///caption + Example of an endpoint tested our using our swagger page. + /// + +=== ":fontawesome-brands-java: Java" + + The serverside code of MOLGENIS Armadillo + ([molgenis-service-armadillo](https://github.com/molgenis/molgenis-service-armadillo)) is written in Java. In order + to run it locally you will need the following prerequisites: + + - Java 17 (unless running with Docker) + - Docker + +

    Project Structure

    + If you look at the java code in [our repository](https://github.com/molgenis/molgenis-service-armadillo/), you might + notice there are two parts to it: + + - [Armadillo](https://github.com/molgenis/molgenis-service-armadillo/tree/master/armadillo) + - [R](https://github.com/molgenis/molgenis-service-armadillo/tree/master/r) + + The Armadillo part is the main application that contains all basic funcitonality and APIs. It uses the R part to + communicate with R, using DataSHIELD logic. + +

    Tools

    +

    pre-commit

    + This repository uses `pre-commit` to manage commit hooks. An installation guide can be found + [here](https://pre-commit.com/index.html#1-install-pre-commit). To install the hooks, run `pre-commit install` from the root folder of this repository. Now + your code will be automatically formatted whenever you commit. The pre-commit hooks we've set, will reformat new + code upon commit, so that it matches our code style. + +

    Running

    + There are several ways to run armadillo. What you're planning on doing with armadillo determines the best way to go. + If you just want to test how it works, runnig via the jar or even using docker is very suitable. If you're planning + on writing code yourself, it's better to clone the project, build it yourself and run it, either using your IDE (we + use IntelliJ), or running the jar you built locally. + +

    Jar

    + + 1. Download the jar from our + [releases page](https://github.com/molgenis/molgenis-service-armadillo/releases). + 2. Copy paste the contents of + [application-template.yml](https://github.com/molgenis/molgenis-service-armadillo/blob/master/application.template.yml) + and paste it in a file called `application.yml`, in the same folder as the downloaded jar. + 3. To start the application, run `java -jar molgenis-armadillo-x.yy.zz.jar`. + 4. Go to `http://localhost:8080` to see the Armadillo UI. + +

    Docker

    + For testing without having to installing Java you can run using docker: + + 1. Install [docker-compose](https://docs.docker.com/compose/install/) + 2. Download this [docker-compose.yml](docker-compose.yml). + 3. Execute `docker-compose up` + 4. Once it says 'Started', go to http://localhost:8080 to see your Armadillo running. + + The command must run in the same directory as the downloaded docker file. + We made docker available via 'docker.sock' so we can start/stop DataSHIELD profiles. + Alternatively you must include the datashield profiles into this docker-compose. + You can override all application.yml settings via environment variables (see commented code in docker-compose file). + +

    IntelliJ

    + We develop Armadillo using IntelliJ. To do so: + + 1. Clone our project: + ```shell + git clone https://github.com/molgenis/molgenis-service-armadillo.git + ``` + 2. Open the project in Intellij. + 3. Make sure the right version of Java is set in `File> Project structure...` + 4. Build the project: select the gradle button (elephant symbol, usually on the right of your intellij screen), + select `clean` and then `build`. + 5. Go to the `ArmadilloServiceApplication` class (press shift-shift and search for it). + 6. Click on the play button in front of the main function. You might have to press "Run" again if a popup with the + run configuration appears. + +

    Testing

    + Most of our code is covered by unittests. Although we usually aim for a coverage of 80%, sometimes we make + exceptions because we value quality of tests over their quantity. We recognise that for some pieces of code it's + hard to write meaningful unittests (half of some functions would have to be mocked, usually causing tests to + lose their purpose). This is especially the case in our Java repository. Of course we don't want + these pieces untested, therefore we have + [a set of release test scripts](https://github.com/molgenis/molgenis-service-armadillo/tree/master/scripts/release). + This test is made to test basic functionality from Datamanager and Researcher perspective using the most commonly + used DataSHIELD packages. This test script is used before each release to ensure Armadillo's quality. The script + also runs partly (without OIDC) in our continuous integration test on each pull request, as well as the unittests. + +

    host.docker.internal error

    + This error is returned only in development environments, when working on an unsupported operating system and running + a profile that has the `resourcer` R package whitelisted. The only way to fix this error, is by temporarily altering + the Armadillo source code in the + [DockerService.java](https://github.com/molgenis/molgenis-service-armadillo/blob/master/armadillo/src/main/java/org/molgenis/armadillo/profile/DockerService.java) + Go to the `install_image` method to where `createContainerCmd` is called: + ```java + cmd.withExposedPorts(exposed) + ... + .exec() + ``` + Simply add `.withExtraHosts("host.docker.internal:host-gateway"))` before the `.exec()`. To ensure the code is + properly updated, we suggest rebuilding the code before restarting Armadillo. If you already had the profile + running, you will need to restart that as well. +=== ":material-vuejs: JavaScript/VueJS" + + The user interface (UI) of MOLGENIS Armadillo is written in JavaScript, using VueJS as framework. We use `yarn` to + compile the code and develop in VSCode. Our setup is as follows: + + 1. Open the armadillo repository, and build and run it in InteliJ (as described in Java Developer guide) + 2. Open the ui folder of the repository in VSCode. + 3. Install using: + ```shell + yarn install + ``` + 4. Run dev server using: + ``` + yarn dev + ``` + You can now login on http://localhost:8080 and then switch to http://localhost:8081 to directly see the changes + you're changing the UI code. + +=== ":simple-r: R" + + We maintain several R packages: + + - [MolgenisArmadillo](https://github.com/molgenis/molgenis-r-armadillo) + - [DSMolgenisArmadillo](https://github.com/molgenis/molgenis-r-datashield) + - [DSUpload](https://github.com/lifecycle-project/ds-upload) + - [DSTidyVerse](- [DSTidyverse](https://github.com/molgenis/ds-tidyverse)) + - [DSTidyverseClient](https://github.com/molgenis/ds-tidyverse-client) + + We aim to release all our packages (excluding DSUpload) to CRAN, to increase visibility and compatibility. This + means that documentation and vignettes should be updated in every pull request that changes or updates + functionality. \ No newline at end of file diff --git a/docs/pages/examples_usage.md b/docs/pages/examples_usage.md new file mode 100644 index 000000000..d297c8047 --- /dev/null +++ b/docs/pages/examples_usage.md @@ -0,0 +1,6 @@ +# Example of usage +User management + +Project management + +Profile management \ No newline at end of file diff --git a/docs/pages/faq.md b/docs/pages/faq.md new file mode 100644 index 000000000..52ecc6d61 --- /dev/null +++ b/docs/pages/faq.md @@ -0,0 +1,95 @@ +# Frequently Asked Questions + +??? question "Docker gives a `java.socket` error" + You might need to enable Docker socket. On Docker desktop you can find that under 'settings' and 'advanced'. + +??? question "Can I use docker compose to start profiles?" + Instead of making Armadillo start/stop DataSHIELD profiles you can also use docker compose. See commented section in docker-compose.yml file. + +??? question "Can I pass environment or commandline variables instead of application.yml?" + Yes, it is standard spring. + +??? question "Can I run Armadillo with oauth2 config offline?" + Yes, you can run in 'offline' profile + ```bash + ./gradlew run -Dspring.profiles.active=offline + ``` + +??? question "How to run Armadillo version 2?" + For armadillo 2.x you can follow [instructions](https://github.com/molgenis/molgenis-service-armadillo/tree/armadillo-service-2.2.3) for testing we use docker compose. For production environment we are using [Ansible](https://galaxy.ansible.com/molgenis/armadillo) + +??? question "How to run Armadillo as developer?" + We develop Armadillo using IntelliJ. + +??? question "How to build Armadillo" + Run following command in the github root: + ```./gradlew build``` + + To execute in 'dev' run following command in the github root: + ```./gradlew run``` + + **Setting up development tools** + + This repository uses `pre-commit` to manage commit hooks. An installation guide can be found [here](https://pre-commit.com/index.html#1-install-pre-commit). To install the hooks, run `pre-commit install` from the root folder of this repository. Now your code will be automatically formatted whenever you commit. + + **How to change data directory** + + Data is automatically stored in the `data` folder in this repository. You can choose another location in `application.yml` by changing the `storage.root-dir` setting. + + When you run Armadillo locally for the first time, the `lifecycle` project has not been added to the system metadata yet. To add it automatically, see Application properties. + Alternatively you can add it manually: + + - Go to the Swagger UI (`http://localhost:8080/swagger-ui/index.html`) + - Go to the `PUT /access/projects` endpoint + - Add the project `lifecycle` + + **Working with resources in development mode** + + When developing locally, docker has trouble connecting to localhost. This problem becomes clear when working with + resources. Luckily there is a quick fix for the problem. Instead of defining a resource as for example + `http://localhost:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`, rewrite it to: + `http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`. Here's some example R code + for uploading resources: + + ```R + ## Uploading resources + library(MolgenisArmadillo) + library(resourcer) + + token <- armadillo.get_token("http://localhost:8080/") + + resGSE1 <- resourcer::newResource( + name = "GSE66351_1", + secret = token, + url = "http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda", + format = "ExpressionSet" + ) + + armadillo.login("http://localhost:8080/") + armadillo.upload_resource(project="omics", folder="ewas", resource = resGSE1, name = "GSE66351_1") + ``` + And for using them: + ```R + library(DSMolgenisArmadillo) + library(dsBaseClient) + + token <- armadillo.get_token("http://localhost:8080/") + + builder <- DSI::newDSLoginBuilder() + builder$append( + server = "local", + url = "http://localhost:8080/", + token = token, + driver = "ArmadilloDriver", + profile = "uniform", + resource = "omics/ewas/GSE66351_1" + ) + + login_data <- builder$build() + conns <- DSI::datashield.login(logins = login_data, assign = TRUE) + + datashield.resources(conns = conns) + datashield.assign.resource(conns, resource="omics/ewas/GSE66351_1", symbol="eSet_0y_EUR") + ds.class('eSet_0y_EUR', datasources = conns) + datashield.assign.expr(conns, symbol = "methy_0y_EUR",expr = quote(as.resource.object(eSet_0y_EUR))) + ``` diff --git a/docs/ops/installing.md b/docs/pages/install_management/armadillo_install.md similarity index 64% rename from docs/ops/installing.md rename to docs/pages/install_management/armadillo_install.md index 3e649b567..d999ae7af 100644 --- a/docs/ops/installing.md +++ b/docs/pages/install_management/armadillo_install.md @@ -1,6 +1,7 @@ # Armadillo installation -> This guide assumes using Ubuntu Server LTS as the installation script expect the `systemd` service. See [Install alternatives](#install-alternatives) below. +!!! note + This guide assumes you are using Ubuntu Server LTS, the installation script expects the `systemd` service. See [Install alternatives](#install-alternatives) below. ## Requirements @@ -14,18 +15,18 @@ You need a server or virtual machine to deploy the Armadillo stack. The specific | 20.000-70.000 | 16 | 100 | 4 | | 70.000 > | 32 | 150 | 8 | -In case of using dsOmics this setup can be rather bigger. Please contact molgenis-operations@umcg.nl for the latest specifications. +In case of using dsOmics this setup can be rather bigger. Please [contact](../contact.md) us for the latest specifications. ## Software requirements -* Java 17 JRE or JDK +* Java 19 JRE or JDK * Docker -In addition to these, there are other optional components you may wish to install, such as setting up nginx as a reverse proxy. +In addition to these, there are other optional components you may wish to install, such as setting up nginx as a reverse proxy. ## Domain -An domain or an hostname ie `cohort.armadillo.domain.org` is required to run Armadillo. +A domain or an hostname ie `cohort.armadillo.domain.org` is required to run Armadillo. This domain is needed for the installation script. @@ -33,15 +34,15 @@ This domain is needed for the installation script. Before we start with the deployment of Armadillo you will need to register your domain that you are going to use with your Armadillo on the DataSHIELD authentication server. This allows you to delegate the authentication and user management. The authorization will still be under the control of the Data Manager (who gets access and who don't get access) within your armadillo installation. -To registrate you will need to send a mail to `molgenis-support@umcg.nl` with the [chosen domains](#domain) and the e-mail adres of the Data Manager who is granted admin permissions in Armadillo. Also add to the mail that you want to register for the the DataSHIELD authentication server and if you belong to a project like Lifecycle, Athlete or Longitools. +To register you will need to [contact](../contact.md) us with the [chosen domains](#domain) and the e-mail address of the Data Manager who is granted admin permissions in Armadillo. Also add to the mail that you want to register for the the DataSHIELD authentication server and if you belong to a project like Lifecycle, Athlete or Longitools. -When the Armadillo domain is registrerd you will get an mail back with data that need to be inserted in step 2. +When the Armadillo domain is processed you will get an email back with data that need to be inserted in step 2. The values needed are: -- OIDC service url i.e. https://lifecycle-auth.molgenis.org -- OIDC Client ID -- OIDC Client Secret + - OIDC service url i.e. https://lifecycle-auth.molgenis.org + - OIDC Client ID + - OIDC Client Secret ## Securing the connection @@ -55,7 +56,7 @@ setsebool -P httpd_can_network_connect on ## Installing Armadillo as service -We run Armadillo in production as a Linux service on Ubuntu, ensuring it gets restarted when the server is rebooted. You might be able to reproduce also on CentOS (using yum instead of apt). +We run Armadillo in production as a Linux service on Ubuntu, ensuring it gets restarted when the server is rebooted. You might be able to reproduce also on CentOS (using `yum` instead of `apt`). ### 1. Install necessary software @@ -65,17 +66,18 @@ apt install openjdk-19-jre-headless apt install docker.io ``` -Note: you might need 'sudo' +??? note + you might need to execute these commands as `sudo` ### 2. Run installation script -This step will install most recent [release](https://github.com/molgenis/molgenis-service-armadillo/releases) +This step will install the latest [release](https://github.com/molgenis/molgenis-service-armadillo/releases). After installation the Armadillo application is installed with the given configuration and its service. #### 2.1 Download the setup script -``` +```bash wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/scripts/install/armadillo-setup.sh ``` @@ -95,18 +97,17 @@ The installation script requires some arguments: | admin-password | Secure password for the admin user | | datadir | The location where the data is stored. This directory should be have enough diskspace en could be backuped (Default → /usr/share/armadillo/data)| | domain | The URL where armadillo is listening on. For example: cohort.armadillo.domain.org | -||| -|oidc | Enable OIDC, see [authentication](#Authentication) | +|oidc | Enable OIDC, see [authentication](#authentication) | |oidc_url | Given oidc URL | |oidc_clientid | Given client ID| |oidc_clientsecret | Given secret ID| -### 2.2 Run the install script +#### 2.2 Run the install script Adapt the following install command to suit your situation. Use `--help` to see all options. -> Note: https://lifecycle-auth.molgenis.org is MOLGENIS provided OIDC service but -you can also use your own, see FAQ below. +??? note + https://lifecycle-auth.molgenis.org is MOLGENIS provider of the OIDC service but you can also use your own. ```bash bash armadillo-setup.sh \ @@ -119,7 +120,7 @@ bash armadillo-setup.sh \ --oidc_clientsecret secret \ ``` -## File locations +### File locations The script creates using default values the follow files and directories: @@ -142,12 +143,15 @@ systemctl start armadillo After the installation is complete Armadillo is listening on port 8080. Test the setup by visiting `http://armadillo.cohort.study.com:8080` or type in the terminal `wget http://localhost:8080` to see a text response. -> Note: the Armadillo website is not secure yet so you need to setup a *front-end* proxy. +??? note + The Armadillo website is not secure yet so you need to setup a *front-end* proxy. -When having setup this *front-end* proxy you could get a bad gateway. This mostly means Armadillo is not ready yet as you already have tested armadillo as mentioned above. +When having setup this *front-end* proxy you could get a bad gateway (see below). This mostly means Armadillo is not ready yet as you already have tested armadillo as mentioned above. -> 502 Bad Gateway -> nginx/1.18.0 (Ubuntu) +```http +502 Bad Gateway +nginx/1.18.0 (Ubuntu) +``` Recheck with `wget http://localhost:8080` and check the log files. @@ -171,23 +175,55 @@ server { } ``` -> Note: the above is still not secure but not you can reach Armadillo from `http://armadillo.cohort.study.com`. +??? note + The above is still not secure but not you can reach Armadillo from `http://armadillo.cohort.study.com`. To secure the communication using https we have a [nginx example](https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/scripts/install/conf/armadillo-nginx.conf) ## Backups -A good start for backuping data is the /usr/share/armadillo and /etc/armadillo. If you gave another datadir as setup option you also should backup this directory. For disaster backups you should contact your IT department. +A good start for data backup is the `/usr/share/armadillo` and `/etc/armadillo`. If you gave another datadir as setup option you also should backup this directory. For disaster backups you should contact your IT department. ## Install alternatives -- On local machine using [java](./install/install_java.md) -- Armidillo running as a [Docker](./install/install_docker.md) container. -- [Apache](./install/install_apache.md) - -For questions on other linux release you can email molgenis-operations@umcg.nl +- On local machine using java +- Armidillo running as a Docker container. +- [Apache](#apache) ## What's next? * [For the server owner or data manager who need to put data on to the server](https://molgenis.github.io/molgenis-r-armadillo/) * [For the researcher who want to start analyzing the data on the server](https://molgenis.github.io/molgenis-r-datashield/) + +## Apache + +It is possible to run Molgenis Armadillo using Apache, however we do **not** provide support with this configuration. + +### Encoding + +Apache requires some additional configuration to get the `/storage/projects/{project}/objects/{object}` to work. When this endpoint doesn't work: + + - tables cannot be assigned + - subsets cannot be created + - resources cannot be used. + +### Tell Armadillo about https + +We need to tell Armadillo server how to building URLs. + +### Changes to your site-enabled configuration + +Your configuration probably should look like this. + +```conf +ProxyPreserveHost On + +ProxyPass / http://localhost:8080/ nocanon + +AllowEncodedSlashes On + +RequestHeader set X-Forwarded-Proto https +RequestHeader set X-Forwarded-Port 443 +``` + +After setting this don't forget to restart Apache. \ No newline at end of file diff --git a/docs/pages/install_management/armadillo_management.md b/docs/pages/install_management/armadillo_management.md new file mode 100644 index 000000000..1137a59f2 --- /dev/null +++ b/docs/pages/install_management/armadillo_management.md @@ -0,0 +1,19 @@ +# Armadillo management + +Armadillo has three main screens to manage projects, user access and DataSHIELD profiles: + +## Create data access projects + +Data managers can use the Armadillo web user interface or [MolgenisArmadillo R client](https://molgenis.github.io/molgenis-r-armadillo) to create 'projects' and upload their data into those. Data tables need to be in parquet format that supports fast selections of the columns (variables) you need for analysis. Other files can be configured as 'resources'. + +## Manage user access + +Data managers can use the permission screen to give email addresses access to the data. Everybody signs in via single sign on using an OIDC central authentication server such as KeyCloack or Fusion auth that federates to authentication systems of connected institutions, ideally using a federated AAI such as LifeScience AAI. + +## Configure DataSHIELD profiles + +To analyse data, users must choose a datashield profile. Armadillo owners can use the web user interface to configure new profiles. Assuming you installed docker you can also start/stop these images. Alternatively you can use docker-compose for that. We recommend selecting one of the DataSHIELD standard profiles. + +## End users can use Armadillo as any other DataSHIELD server + +A researcher connects from an [R client](https://molgenis.github.io/molgenis-r-datashield) to one or multiple Armadillo servers. The data is loaded into an R session on the Armadillo server specifically created for the researcher. Analysis requests are sent to the R session on each Armadillo server. There the analysis is performed and aggregated results are sent back to the client. \ No newline at end of file diff --git a/docs/upgrade-3-4.md b/docs/pages/install_management/armadillo_migrate_2_to_3.md similarity index 88% rename from docs/upgrade-3-4.md rename to docs/pages/install_management/armadillo_migrate_2_to_3.md index c4f477029..a6cf5b8e0 100644 --- a/docs/upgrade-3-4.md +++ b/docs/pages/install_management/armadillo_migrate_2_to_3.md @@ -1,6 +1,9 @@ -# Upgrading to Armadillo 4 (rock only) +# Migrate Armadillo 3 to Armadillo 4 -> Note: We assume Ubuntu with systemd is used. +Upgrade to Armadillo 4 (rock only) + +??? note + We assume Ubuntu with systemd is used. The upgrade from Armadillo v3.4 to 4.x is breaking as the profiles must be Rock profiles. @@ -16,10 +19,10 @@ Make a note of the version as you will use this below. Check if the new profiles are compatible with your needs, these profile names can be edited later on in the manual: -- `datashield/rock-base:latest` -- `datashield/rock-dolomite-xenon:latest` + - datashield/rock-base:latest + - datashield/rock-dolomite-xenon:latest -See also DataSHIELD [profiles](https://www.datashield.org/help/standard-profiles-and-plaforms) +See also DataSHIELD profiles ## 2. Check server space @@ -31,7 +34,8 @@ Make sure enough disk space is available for the Rock only images. # Check disk space df -H ``` -If you have 15 GB or more available, you can continue. If you have less available, check `docker image list` to see if you can cleanup some docker images (you only need the latest `datashield/armadillo-rserver` and `datashield/armadillo-rserver_caravan-xenon` for armadillo 3). + +If you have 15 GB or more available, you can continue. If you have less available, check `docker image list` to see if you can cleanup some docker images (you only need the latest `datashield/armadillo-rserver` and `datashield/armadillo-rserver_caravan-xenon` for armadillo 3). ### 2.2 Check docker images @@ -70,7 +74,7 @@ df -H ## 3. Download required files -Make a note of the version number ie. `v4.1.3` as you need to download some files from the terminal using the updatescript. +Make a note of the version number ie. `v4.1.3` as you need to download some files from the terminal using the update script. ### 3.1 Update script @@ -100,7 +104,8 @@ chmod u+x armadillo-check-update.sh You can run the following script to download the new Armadillo version. -> The output could help us to help you fix problems. +??? note + The output could help us to help you fix problems. ```bash # Change the version number v4.x.y @@ -117,17 +122,18 @@ ls -ltr /usr/share/armadillo/application/ ## 4. Config the new version ### 4.1 application.yml -NOTE: To compare the latest template to your own configuration, see the troubleshooting section below. The safest way to update armadillo is by fetching the template and filling it in with your configuration using the information in the troubleshooting section. You can try the following first: + +To compare the latest template to your own configuration, see the troubleshooting section below. The safest way to update armadillo is by fetching the template and filling it in with your configuration using the information in the troubleshooting section. You can try the following first: Edit the application.yml: + ```bash nano /etc/armadillo/application.yml ``` Below the line `docker-management-enabled: true`, ensure to insert the line `docker-run-in-container: false`. Typically, you'll find these configurations at the beginning of the file. - -### 4.2 Make backup of system config +## 4.2 Make backup of system config ```bash # Still in the correct directory? (`/root/v4.x.y`) @@ -182,29 +188,27 @@ systemctl status armadillo ## 6. Log on to the UI -Go to your armadillo website. Is the version in the left top corner updated? This means the update was successful. We're -almost finished. +Go to your armadillo website. Is the version in the left top corner updated? This means the update was successful. ## 7. Update profiles + Login into the website and go to the profiles tab. Here two profiles should be listed: `default` and `xenon`. Any other profiles can be removed. -1. Edit the default profile. -2. Change the "image" to `datashield/rock-base:latest` and save. +1. Edit the default profile. +2. Change the "image" to `datashield/rock-base:latest` and save. 3. Start the default profile. 4. Edit the "xenon" profile. 5. Change the "image" to `datashield/rock-dolomite-xenon:latest` and save. -6. Start the xenon profile. +6. Start the xenon profile. Everything should now be working correctly. You can try and login to your server via the central analysis server, using -the `DSMolgenisArmadillo` (2.0.5 or up) package to test. +the `DSMolgenisArmadillo` (2.0.5 or up) package to test. -Enjoy =) -Team Armadillo +## Troubleshooting +### Logs -## Troubleshooting -#### Logs Reviewing the log files can provide valuable insights into any issues or activities within the application. If you encounter any errors or unexpected behavior, examining the log files can often help diagnose the problem. Check log files location @@ -242,8 +246,8 @@ or tail -f /var/log/armadillo/* ``` +### Compare application.yml -#### Compare application.yml Although we try to be very complete in this manual, if you run into issues, it might be because a setting was changed in the application.yml. You can check if application settings has any new entries by first downloading the application template (for reference). @@ -267,7 +271,7 @@ Your output should look like output below. - the `>` means we have a setting (probably added or options) - the `|` means both have different values which happens with OICD/oauth settings for sure. -``` +```bash armadillo: armadillo: # set this false if you DON'T want Armadillo to create/edit # set this false if you DON'T want Armadillo to create/edit docker-management-enabled: true docker-management-enabled: true diff --git a/docs/upgrade-2-3.md b/docs/pages/install_management/armadillo_migrate_3_to_4.md similarity index 70% rename from docs/upgrade-2-3.md rename to docs/pages/install_management/armadillo_migrate_3_to_4.md index 0b7042502..aabef5e3d 100644 --- a/docs/upgrade-2-3.md +++ b/docs/pages/install_management/armadillo_migrate_3_to_4.md @@ -1,19 +1,18 @@ -### Migrate Armadillo 2 to Armadillo 3 +# Migrate Armadillo 2 to Armadillo 3 Migrating from Armadillo 2 to Armadillo 3 can be done in two variants, a full migration including [projects, users and data](#migrate-projects-users-and-data) or [just projects and their users](#migrate-projects-and-their-users). Both options require Python (version 3.8) and additional python libraries, described in [Getting started](#getting-started). -### Getting started +## Getting started To start the migration, python 3.8 is advised together with a number of utilitarian python libraries. Other python versions might work, but performance has only been tested with python 3.8. -#### Install with Python virtual environment +### Install with Python virtual environment For more info see [python virtual environment](https://docs.python.org/3/library/venv.html). -The following code does not require superuser rights. The code does assume you are already -in the [scripts/upgrade](https://github.com/molgenis/molgenis-service-armadillo/tree/master/scripts/upgrade) directory. +The following code does not require superuser rights. The code does assume you are already in the [scripts/upgrade](https://github.com/molgenis/molgenis-service-armadillo/tree/master/scripts/upgrade) directory. ```bash python3 -m venv venv @@ -21,34 +20,44 @@ source ./venv/bin/activate pip install -r requirements.txt ``` -If the installation of one (or more libraries) fails, try to install the libraries one by one. +??? tip + If the installation of one (or more libraries) fails, try to install the libraries one by one. -### Migrate Projects, users and data +## Migrate Projects, users and data -#### 1. Check if there's enough space left on the server -``` +### 1. Check if there's enough space left on the server + +```bash df -h ``` + Compare to: -``` + +```bash du -h /var/lib/minio ``` -Available space should be at least twice the size of the MinIO folder. -#### 2. Backup Armadillo 2 settings -``` +??? warning + Available space should be at least twice the size of the MinIO folder. + +### 2. Backup Armadillo 2 settings + +```bash mkdir armadillo2-backup rsync -avr /usr/share/armadillo armadillo2-backup cp /etc/armadillo/application.yml armadillo2-backup/application-armadillo2.yml ``` -N.B.change /usr/share to path matching your local config. -#### 3. Install helper software +???+ note + Change `/usr/share` to the path matching your local config. + +### 3. Install helper software FIXME: Skip these steps as you have already done installing the python code in a virtual env. Login to your server as root, using ssh. -``` + +```bash apt update #apt install pip @@ -56,36 +65,51 @@ apt update #pip install fusionauth-client #pip install simple_term_menu ``` + If you get a purple message asking to update, accept and install everything. + Restart of server is recommended after this. -N.B. Note that the commands in this manual are for Ubuntu, on other linux systems, -the `apt` command needs to be replaced with the correct one. +??? note + The commands in this manual are for Ubuntu, on other linux systems, the `apt` command needs to be replaced with the correct one. + +### 4. Stop all docker images for Armadillo 2 -#### 4. Stop all docker images for Armadillo 2 List all docker images -```docker ps -a``` -Stop and remove all Armadillo 2 related images (except for MinIO), e.g. +```bash +docker ps -a ``` + +Stop and remove all Armadillo 2 related images (except for MinIO), e.g. + +```bash docker rm armadillo_auth_1 armadillo_console_1 armadillo_rserver-default_1 armadillo_rserver-mediation_1 armadillo_rserver-exposome_1 armadillo_rserver-omics_1 armadillo_armadillo_1 -f ``` -Check with `docker ps -a` if there are still containers running, if so remove these (except for the MinIO) in the same way as the others. -#### 5. Install armadillo -``` +Check with `docker ps -a` if there are still containers running, if so remove these (**except for the MinIO**) in the same way as the others. + +???+ warning + Make sure you do **not** remove the docker instance of MinIO before you migrated the data! + +### 5. Install armadillo + +```bash apt update apt install openjdk-19-jre-headless apt install docker.io ``` + The docker.io step might fail because containerd already exists, if that's the case, remove containerd and try again: -``` + +```bash apt remove containerd.io apt install docker.io ``` Get armadillo: -``` + +```bash wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/scripts/install/armadillo-setup.sh bash armadillo-setup.sh \ --admin-user admin \ @@ -97,62 +121,77 @@ bash armadillo-setup.sh \ --oidc_clientsecret secret \ --cleanup \ ``` + Don't forget to set a proper admin password (use a generator), domain, clientid and clientsecret. The client id and secret can be found on the lifecycle auth server in the configuration for your server. If you don't have permissions to receive this, you can ask the support team to get it for you. Open armadillo in the browser and try to login using basicauth to check if the server is running properly. If it is not running at all, try: -``` + +```bash systemctl start armadillo ``` -#### 6. Export data from Armadillo 2 into armadillo 3 -Look up the user/password in the application.yml of the old armadillo. They are called MinIO access key and minio -secret key. -``` +### 6. Export data from Armadillo 2 into armadillo 3 + +Look up the user/password in the application.yml of the old armadillo. They are called MinIO access key and minio secret key. + +```bash cat /root/armadillo2-backup/application-armadillo2.yml ``` + Do the following step in a separate screen. On ubuntu use: -``` + +```bash screen ``` + Navigate to the armadillo folder: -``` + +```bash cd /usr/share/armadillo ``` + This step will copy Armadillo 2 data from minio into the folder matching of an Armadillo 3 data folder: -``` + +```bash mkdir data wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/scripts/upgrade/migrate-minio.py python3 migrate-minio.py --minio http://localhost:9000 --target /usr/share/armadillo/data ``` -This might take a couple of minutes. You can detach the screen using `ctrl+a` followed by `d` and reattach it using -`screen -r`. +This might take a couple of minutes. You can detach the screen using ++ctrl+a++ followed by ++d++ and reattach it using `screen -r`. + +### 7. Run Armadillo 3 using exported data -#### 7. Run Armadillo 3 using exported data Make sure to move the exported data into the new 'data' folder. Optionally you might need to fix user permissions, e.g.: -``` + +```bash chown armadillo:armadillo -R data ``` + Check if armadillo is running by going to the URL of your server in the browser, login and navigate to the projects tab. -#### 8. Optionally, acquire a permission set from MOLGENIS team -If you previously ran a central authorisation server with MOLGENIS team, they can provide you with procedure to load -pre-existing permissions. They will use: -``` +### 8. Optionally, acquire a permission set from MOLGENIS team + +If you previously ran a central authorisation server with MOLGENIS team, they can provide you with procedure to load pre-existing permissions. They will use: + +```bash wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/master/scripts/upgrade/migrate-auth.py python3 migrate-auth.py --fusion-auth https://lifecycle-auth.molgenis.org --armadillo https://thearmadillourl.net ``` -Now check if all users and data are properly migrated. -NOTE: if the script fails with a timeout, try pinging the armadillo url and lifecycle auth url to see if they're reachable from the server. In case they are not, you could choose to export the users using the `export-users.py` script locally and then manually enter them into the system. +Now check if all users and data are properly migrated. + +??? note + If the script fails with a timeout, try pinging the armadillo url and lifecycle auth url to see if they're reachable from the server. In case they are not, you could choose to export the users using the `export-users.py` script locally and then manually enter them into the system. -#### 9. Cleanup ngnix config +### 9. Cleanup ngnix config Change `/etc/nginx/sites-available/armadillo.conf` to: -``` + +```bash server { listen 80; server_name urlofyourserver.org @@ -167,27 +206,33 @@ server { } } ``` -Note that the `https://` is missing in the server_name part. -NOTE: if port 443 and the SSL certificates are in the old config, you mind have to keep that part, so you should not comment that out. Keep the listen and certificate lines, comment out the rest and paste the config above below the existing config. -Remove the console, auth and storage file from: `/etc/nginx/sites-enabled/` and `/etc/nginx/sites-available/. +??? note + Note that the `https://` is missing in the server_name part. +??? note + If port 443 and the SSL certificates are in the old config, you mind have to keep that part, so you should not comment that out. Keep the listen and certificate lines, comment out the rest and paste the config above below the existing config. -``` +Remove the console, auth and storage file from: `/etc/nginx/sites-enabled/` and `/etc/nginx/sites-available/`. + +```bash systemctl restart nginx ``` -#### 10. Fix application.yml +### 10. Fix application.yml + Make sure the following is added: -``` + +```bash server: forward-headers-strategy: framework ``` -#### 11. Fix URLs in the lifecycle FusionAuth -Add the following to the config of your server: -`https://yourserver.com/login/oauth2/code/molgenis` +### 11. Fix URLs in the lifecycle FusionAuth + +Add the following to the config of your server: `https://yourserver.com/login/oauth2/code/molgenis` + +### 12. Set up profiles -#### 12. Set up profiles Login to armadillo in the browser. Navigate to the "Profiles" tab. Add a new profile with the following properties: Name: `xenon` @@ -196,21 +241,31 @@ Package whitelist: `dsBase`, `resourcer`, `dsMediation`, `dsMTLBase`, `dsSurviva Assign a random 9-number seed and create and start the container. -#### 13. Remove old MinIO data +### 13. Remove old MinIO data + First remove the MinIO docker container. First check the name of the container using `docker ps -a`, then: -``` + +```bash docker rm containername -f ``` + After that remove the data: -```rm -Rf /var/lib/minio/ ``` -### Migrate Projects and their users +```bash +rm -Rf /var/lib/minio/ +``` -Migration of just the projects and their users (with their corresponding rights) can be done -by using [export-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/export-users.py) and [import-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/import-users.py). -**This options does not migrate the data!** +???+ warning + Be sure you have migrated your data successfully or created a backup prior to deleting your minio data folder! -#### 1. Export Projects and users from Armadillo 2 +## Migrate Projects and their users + +Migration of just the projects and their users (with their corresponding rights) can be done by using [export-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/export-users.py) and [import-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/import-users.py). + +???+ warning + This options does not migrate the data! + +### 1. Export Projects and users from Armadillo 2 To export users from an Armadillo 2 server, one must use the [export-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/export-users.py) script. `export-users.py` can be used by using the following arguments: @@ -224,12 +279,13 @@ Empty projects (without users) will also be exported as an empty TSV (containing Also note that some projects might change in name, as Armadillo 3 is stricter with naming projects. Example: + ```bash pipenv shell python3 export-users.py -f https://armadillo2-server.org -o ./armadillo_2_exports ``` -#### 2. Import Projects and users TSVs into Armadillo 3 +### 2. Import Projects and users TSVs into Armadillo 3 To import users into an Armadillo 3 server, one must use the [import-users.py](https://github.com/molgenis/molgenis-service-armadillo/blob/master/scripts/upgrade/import-users.py) script. `import-users` can be used by using the following arguments: @@ -241,6 +297,7 @@ To import users into an Armadillo 3 server, one must use the [import-users.py](h Empty TSVs from [step 1](#1-export-projects-and-users-from-armadillo-2) will be imported as empty projects with no users. Example: + ```bash pipenv shell python3 import-users.py -s https://armadillo3-server.org -d ./armadillo_2_exports/2023-11-09 diff --git a/docs/minor-release-update.md b/docs/pages/install_management/armadillo_minor_release_update.md similarity index 66% rename from docs/minor-release-update.md rename to docs/pages/install_management/armadillo_minor_release_update.md index c04420b0f..f1324af94 100644 --- a/docs/minor-release-update.md +++ b/docs/pages/install_management/armadillo_minor_release_update.md @@ -1,12 +1,15 @@ -# Minor Version Upgrade Manual: Procedures for y.z Releases +# Armadillo minor release update -> Note: This manual is intended for minor version updates within the latest major release. For example, you can use it to update from version 4.1 to 4.7.1. For upgrading to a new major version, please refer to the specific manuals dedicated to major version upgrades. +## Minor Version Upgrade Manual: Procedures for y.z Releases -## Check latest version +???+ note + This manual is intended for minor version updates within the latest major release. For example, you can use it to update from version 4.1 to 4.7.1. For upgrading to a new major version, please refer to the specific manuals dedicated to major version upgrades. -For the latest 4.y.z release check https://github.com/molgenis/molgenis-service-armadillo/releases/latest. This will redirect to a v4.y.z page. +### Check latest version -# Updating Armadillo +For the latest 4.y.z release check [https://github.com/molgenis/molgenis-service-armadillo/releases/latest](https://github.com/molgenis/molgenis-service-armadillo/releases/latest). This will redirect to a v4.y.z page. + +## Updating Armadillo ### 1. Stop docker containers @@ -24,11 +27,11 @@ docker container list docker container stop ``` -## 2. Download required files +### 2. Download required files Make a note of the version number ie. `v4.7.1` as you need to download some files from the terminal using the update script. -### 2.1 Update script +#### 2.1 Update script You need to be root user. @@ -48,16 +51,18 @@ pwd wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/v4.y.z/scripts/install/armadillo-check-update.sh ``` -Make the script runnable +Make the script executable: + ```bash chmod u+x armadillo-check-update.sh ``` -### 2.2 Run update script +#### 2.2 Run update script You can run the following script to download the new Armadillo version. -> The output could help us to help you fix problems. +??? tip + The output could help us to help you fix problems. ```bash # Change the version number v4.y.z @@ -71,7 +76,7 @@ Once the script has completed, you can verify that the Armadillo JAR file has be ls -ltr /usr/share/armadillo/application/ ``` -### 3. Make backup of system config +#### 3. Make backup of system config ```bash # Still in the correct directory? (`/root/v4.y.z`) @@ -91,17 +96,17 @@ ls system/ # access.json profiles.json ``` -## 4. Restart application using new version +### 4. Restart application using new version Armadillo has not yet been updated, follow the following steps to do so: -### 4.1 Stop Armadillo +#### 4.1 Stop Armadillo ```bash systemctl stop armadillo ``` -### 4.2 Link new version +#### 4.2 Link new version ```bash # List application files @@ -117,24 +122,19 @@ ln -s /usr/share/armadillo/application/armadillo-4.y.z.jar /usr/share/armadillo/ ls -l /usr/share/armadillo/application/ ``` -### 4.3 Restart Armadillo +#### 4.3 Restart Armadillo ```bash systemctl start armadillo systemctl status armadillo ``` -## 5. Log on to the UI +### 5. Log on to the UI -Go to your armadillo website. Is the version in the left top corner updated? This means the update was successful. We're -almost finished. +Go to your armadillo website. Is the version in the left top corner updated? This means the update was successful. -## 6. Start profiles +### 6. Start profiles Login into the website and go to the profiles tab. Now you can start all the profiles again. -Everything should now be working correctly. You can try and login to your server via the central analysis server, using -the `DSMolgenisArmadillo` (2.0.5 or up) package to test. - -Enjoy =) -Team Armadillo +Everything should now be working correctly. You can try and login to your server via the central analysis server, using the `DSMolgenisArmadillo` (2.0.5 or up) package to test. diff --git a/docs/pages/install_management/index.md b/docs/pages/install_management/index.md new file mode 100644 index 000000000..c1054dfef --- /dev/null +++ b/docs/pages/install_management/index.md @@ -0,0 +1,8 @@ +# Installation, Setup and Management + +=== ":material-server: System Operator" + - [Armadillo Installation](../install_management/armadillo_install.md) + - [Armadillo Management](../install_management/armadillo_management.md) + - [Armadillo minor release update](../install_management/armadillo_minor_release_update.md) + - [Migrate Armadillo 2 to Armadillo 3](../install_management/armadillo_migrate_2_to_3.md) + - [Migrate Armadillo 3 to Armadillo 4](../install_management/armadillo_migrate_3_to_4.md) \ No newline at end of file diff --git a/docs/pages/license.md b/docs/pages/license.md new file mode 100644 index 000000000..9b367c333 --- /dev/null +++ b/docs/pages/license.md @@ -0,0 +1,3 @@ +# License +GNU Lesser General Public License v3.0. Find the complete document +[here](https://github.com/molgenis/molgenis-service-armadillo/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/pages/quick_start.md b/docs/pages/quick_start.md new file mode 100644 index 000000000..d54c4f76c --- /dev/null +++ b/docs/pages/quick_start.md @@ -0,0 +1,128 @@ +# Quick Start +MOLGENIS Armadillo facilitates federated analysis using DataSHIELD. +To learn more about DataSHIELD, visit their website or our Basic Concepts page. + +First we need to determine what kind of user you are: + +1. :material-file-table: [Data Manager](#data-manager) + +2. :material-server: [System Operator](#system-operator) + +3. :material-layers-search: [Researcher](#researcher) + +4. :fontawesome-solid-laptop-code: [Developer](#developer) + +## Data manager +Data management can be done in different ways: the Armadillo User Interface, the MolgenisArmadillo R client, or using DsUpload. + +### User interface +In the armadillo user interface, data managers can login and manage users, projects and profiles. They can also see the logs (e.g. to understand errors that may have occurred or to monitor use). To get to know more about the UI, visit the [usage examples page](examples_usage.md). + +![ui-projects.png](../img/ui-projects.png){ width="1000" } + +Please note that the user interface is only accessible by users with admin rights (i.e. usually data managers and not researchers) only. Users without admin permissions will get the following message: + +![Warning message for non admin users](../img/ui-non-admin-message.png){ width="700" } +/// caption +You are logged in but you don't have permission to access the Armadillo user interface. +/// + +### Armadillo R client +Data can also be managed using the MolgenisArmadillo R client. The following code block is an example of how to create a project +and upload data. + +```R +# Install and load the package +remotes::install.packages('MolgenisArmadillo') +library('MolgenisArmadillo') + +# Login +armadillo.login("https://armadillo-url-example.org") + +# Load the iris dataset to upload as test +library(datasets) + +# Create a project called "project" +armadillo.create_project("project") + +# Upload the data in a folder called "folder" +armadillo.upload_table("project", "folder", iris) +``` + +Data is organised in projects. These projects can be compared to folders on the filesystem of your computer. +Users can be granted access to specific projects. Within those projects, data are organised in folders. + +![Project file structure](../img/project-file-structure.png){ width="500" } +/// caption +A typical project structure looks like this. +/// + + +### DsUpload +[dsUpload](https://lifecycle-project.github.io/ds-upload/) is an R package that aids data managers in the data uploading +process. Data uploaded using this package has to fit the +[dsDictionaries](ttps://github.com/lifecycle-project/ds-dictionaries/blob/master/README.md) format. + +## System Operator +System Operators are the people who install the software (Molgenis Armadillo) on the server. Installing is a quick and straightforward process, for full details please view the [Install Guide](../pages/install_management/index.md). + +## Researcher +As a reseracher you will need to arrange access to each Armadillo (or Opal) server that holds the data you want to analyse. +This guide shows you how to login, assuming you have been granted access to the server and the data. + +Research is conducted using R. To begin with you need install and load the following [packages](basic_concepts.md#datashield-packages-and-their-use): + +```R +install.packages(c("DSI", "DSMolgenisArmadillo", "dsBaseClient")) +library(DSI) +library(DSMolgenisArmadillo) +library(dsBaseClient) +``` +With these libraries, you can now login to Armadillo: +```R +url <- "https://armadillo-demo.molgenis.net/" +token <- armadillo.get_token(url) +builder <- DSI::newDSLoginBuilder() + +builder$append( + server = "armadillo", + url = url, + token = token, + driver = "ArmadilloDriver", + profile = "xenon") + +logindata <- builder$build() +conns <- DSI::datashield.login(logins = logindata) +``` +To use the data, you first need to 'assign' it. This means to create a temporary copy on the server within your RSsesion. You can use the following code: +```R +datashield.assign.table(conns, "mtcars", "project/data/cars") +``` +To see the data assigned in your workspace use: +```R +ds.ls() +``` + +If all of this succeeds, your access to Armadillo is setup correctly. + +For detailed information on how to use DataSHIELD as a researcher, please visit the [DataSHIELD Wiki](https://wiki.datashield.org/en/opmanag/private/researcher/intro) + +For more extensive documentation Armadillo, please visit our documentation for [`DSMolgenisArmadillo`](https://molgenis.github.io/molgenis-r-datashield/). + +## Developer +We encourage fellow developers to help us with new features or bugfixes in Armadillo, or help us with our R +packages. To run Armadillo locally, first clone the git repository: +```shell +git clone https://github.com/molgenis/molgenis-service-armadillo.git +``` +You first need to configure armadillo in the `application.yml`. To do that, you first will need to create this file. +The easiest way to do that is by copying the +[application.template.yml](https://github.com/molgenis/molgenis-service-armadillo/blob/master/application.template.yml) +and name it `application.yml`. + +If you are using IntelliJ, open the `ArmadilloServiceApplication` class (press shift twice and type the class name to go +there), and press the play button on the main function. You might have to first set the Java version (java 17). Armadillo +will initialise up without oAuth configured, so you can login using the username `admin` and the password specified in +`application.yml` (default: `admin`). + +For more information, see our [Developer guides](dev_guides.md) and [License](license.md). \ No newline at end of file diff --git a/docs/pages/troubleshooting.md b/docs/pages/troubleshooting.md new file mode 100644 index 000000000..64a80c87a --- /dev/null +++ b/docs/pages/troubleshooting.md @@ -0,0 +1 @@ +# Troubleshooting \ No newline at end of file diff --git a/docs/release-test.md b/docs/release-test.md deleted file mode 100644 index cc53aa616..000000000 --- a/docs/release-test.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release testing -## Prerequisites -- Test server with release candidate available -- OIDC user with admin permissions on testserver -- The following libraries installed in R: - - cli - - getPass - - arrow - - httr - - jsonlite - - future - - MolgenisArmadillo (2.0.0 >) - - DSI - - dsBaseClient - - DSMolgenisArmadillo (2.0.0 >) - - resourcer (1.4.0 >) - -*For full testing* -- Admin password by hand - -*Minimal* -- OIDC user with admin/superuser permissions on testserver -- Someone with admin permissions available to take and regrant admin permissions to OIDC (super)user - -With these minimal prerequisites met, tests for basic auth will be skipped and permissions will have to be set by hand -when asked by script. - -OR -- A basic admin password - -With these minimal prerequisites, the script will then skip the resources testing (is not possible with basic auth) and -testing as regular user - -## Running the tests -1. Open your commandline -2. `cd` to the `scripts/release` folder of this repository -3. To run tests, type: `Rscript release-test.R` -4. Follow the directions in the script - -If the script fails somewhere during the process, make sure you give your OIDC account back their admin permissions and -throw away the projects: cohort1, cohort2 and omics. diff --git a/docs/ui.md b/docs/ui.md deleted file mode 100644 index cd54a778f..000000000 --- a/docs/ui.md +++ /dev/null @@ -1,218 +0,0 @@ -# Armadillo User Interface - -A key addition from Armadillo version 3 onwards is the addition of a user interface, or UI for short. This UI replaces the MinIO file storage and permission management page, -as well as adding several new features that will be extended upon in future releases. - -## Table of contents - -1. [Login](#login) - 1. [Superuser](#superuser) -2. [Projects](#projects) - 1. [Editing projects](#edit-projects) - 2. [Adding projects](#add-projects) -3. [Project explorer](#project-explorer) - 1. [Resources](#resources) -4. [Users](#users) - 1. [Editing users](#edit-users) - 2. [Adding users](#add-users) -5. [Profiles](#profiles) - -## Login - -![Login screen of Armadillo 3](img/ui/login.png) - -To login to the UI, select the **Institute account (oath2)** button and login using the institute or organisation login -screen you will be redirected to. - -### Superuser - -You need to have admin or superuser permissions if you want to add projects, users or profiles. This means you need to -be granted permission in order to be able to use the UI. If you don't have correct permissions, you will receive the -following error: - -![Error message in case you are not a superuser](img/ui/no-superuser.png) - -If you receive this error, contact someone in your institute that is already able to login without an error, or if you -don't have anyone available, send an email to MOLGENIS Support (molgenis-support@umcg.nl). - -To grant a user superuser permissions simply search for that user in the `Users` tab of the UI, and tick the -_admin_ checkbox for that user: - -![Grant user superuser rights](img/ui/admin.png) - -## Projects - -Once you're logged in, you will be redirected to the `Projects` page. On this page you can add and edit projects. - -You can add users to projects and navigate to the "project-editor"-view and search through the projects using -the search bar on the top right. - -![Armadillo 3 projects page](img/ui/projects.png) - -### Editing projects - -To edit your project, click on the edit button in front of the project you want to edit: -. - -The row will be opened in edit mode: - -![Edit a specific row on the projects page](img/ui/edit-project.png) - -The edit mode can be recognized by its blue background color and you have the option to add new users to your project -by clicking on the + button of the users column. Then, you can either -select an existing user from the dropdown, or add the email address of a new user. - -![Add an existing user to a project or enter the email address of the new user](img/ui/edit-projects-add-user.png) - -In case of adding a user in this screen, a warning will be shown to prevent email addresses with typographical errors from being added to -your system. For example, if you have the address `j.doe@example.com` in your users table but attempt to add `j.die@example.com` a warning message will be displayed asking if you want to add a new user. - -![Warning message to remind you to save the project in order to finalize adding a new user](img/ui/add-user-warning.png) - -It is not possible to edit the name of your project; this is intentional in order to ensure tables, resources, -users, and permissions are transferred to the new project name correctly. - -Click on the checkmark to save the edited row and the X - to cancel. Be careful, if you do cancel your changes will be lost. - -### Adding projects - -To add a new project, click on the + button on top of the table. If -you click this button, an empty row will be opened in edit mode. - -![Add a new project to Armadillo](img/ui/add-project.png) - -Click on the checkmark to save the edited row and the X - to cancel. Be careful, if you do cancel your changes will be lost. - -## Project explorer - -If you click on the icon next to the project name, you will be directed to -the `project editor`. In this screen you can upload and preview data in projects. To go back to your `projects` page, -press the back button . - -![Armadillo project editor](img/ui/project-editor.png) - -If you click on a folder, it will open. - -![Armadillo project folder](img/ui/view-project-folder.png) - -Here you can upload files to that folder, or click on the tables (files) to preview their contents. - -![Armadillo project file preview](img/ui/preview-file.png) - -To upload files, either drag a file from your file browser to the file upload area, or click the area and select the -file. - -![Armadillo upload a file](img/ui/upload-a-file.png) - -After selecting the file, click on _upload_ to upload it. This usually only takes a few seconds but will take longer in your uploading a large file. - -It is also possible to create new folders. To do so, click the "add folder" button - just below the project name. An input dialog will be -presented: - -![Armadillo add a folder](img/ui/add-folder.png) - -Fill in the name you want to use and click on the checkmark button -. Please keep in mind that the folder will only be saved if you put data into it. Select the new folder to select files to upload. - -### Resources - -All file types can be uploaded into Armadillo, however previews will only be available for `.parquet` files. Other files that can be uploaded are treated as _resources_. Resource filetypes usually are `.rda` files or `.Rdata` files. - -To be able to use these resources as a researcher, first an `.rds` file must be generated. How to create these files, is -described [here](https://molgenis.github.io/molgenis-r-armadillo/articles/create_resources.html). - -The URL of your resources should consist of: - -```r -{your url}/storage/projects/{project name}/objects/{name of the folder}%2F{the resource file} -``` - -Here is an example, with some example parameters: - -```r -url = "https://armadillo3.demo.molgenis.net" -project = "omics" -folder = "ewas" -file = "gse66351_1.rda" -``` - -Which results in the following url: - -```r -https://armadillo3.demo.molgenis.net/storage/projects/omics/objects/ewas%2Fgse66351_1.rda -``` - -## Users - -The `Users` page works just as the `Projects` page. You can search users by entering (a part of) the email address or name -of the user into the search box: - -![Search for a user in the Users page](img/ui/admin.png) - -### Editing users - -Users can be edited, **except** for their email addresses. This is because when user's email address changes, that user is possibly no longer -working for the same institution and therefore might not be allowed to access the data anymore. - -![Editing a user on the Users page](img/ui/edit-user.png) - -In edit mode, the row will turn blue. Projects can be added by clicking on the + icon - in the projects column. - - - -You can add a new project by typing it and clicking the checkmark button -. You will be prompted with a warning message, asking you to confirm if you want to -add a new project. - -Alternatively, you can select an existing project by using the search box or scrolling through the presented list. - -### Adding users - -By clicking on the plus button on the top of the table, a new user can be -added. The row with the new user will turn blue in edit mode. - - - -Users can be added before they have logged in previously. These users can be added to projects, which will grant them -permission to use the data from those projects upon their first login. Researchers should not be set as admin. - -## Analysis Profiles - -![Armadillo Profiles page](img/ui/profiles.png) - -Since the release of Armadillo 3.0.0, it is possible to create and manage analysis profiles in the user interface, rather than -asking system administrators to manage these profiles. You can start -and stop profiles. - -When you start a profile for the first time, it will take a bit longer to load because the profile needs to be -downloaded and installed before it can be started. - -![Stopping a profile](img/ui/loading-stop-profile.png) - -If you switch to another screen whilst either starting or stopping a profile, the profiles page will no longer show the -loading information. It is however still loading, and when it's done, if you reload the page, you will see that -your profile started or stopped successfully. - -As in the other screens, you can add profiles with the add-button -. - -![Add a profile](img/ui/add-profile.png) - -By default, some fields will be set. Please update them to install the correct profile. - -Possible images can be found on [dockerhub](https://hub.docker.com/search?q=datashield%2Farmadillo-rserver). We -recommend selecting one of the -[DataSHIELD standard profiles](https://wiki.datashield.org/en/opmanag/standard-profiles-and-platforms). The image name of those -profiles can be found on the dockerhub link above. - -Although the default `port` setting should find an available port, please keep in mind that the port has to be unique, -otherwise you cannot start your profile and will receive an error message. - -R packages can be whitelisted by adding them to the `package whitelist` column so researchers can use them. 'Whitelisting' a package allows analysts to use it. If you want -to whitelist a package, you need to make sure it is installed on the image you selected. Additionally, it is possible to -blacklist certain R functions in the `blacklist function` column. This can be useful if certain functions -are not allowed to be used on certain data or within certain cohorts. diff --git a/docs/v3.4.0/.nojekyll b/docs/v3.4.0/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/0_Prerequisite.r b/docs/v3.4.0/DataSHIELD-datamanagement/0_Prerequisite.r deleted file mode 100644 index fc5d85efd..000000000 --- a/docs/v3.4.0/DataSHIELD-datamanagement/0_Prerequisite.r +++ /dev/null @@ -1,19 +0,0 @@ -# install the following packages -install.packages("MolgenisArmadillo") -install.packages("dplyr") -install.packages("arrow") -# After installation make sure you can load the libraries -library(MolgenisArmadillo) -library(dplyr) -library(arrow) -# verify the loaded libraries (see: other attached packages) -sessionInfo() - -# optional, install the following packages to be able to run a simple analyses -# 3_Analyse_data_subset_DSMolgenisArmadillo.r -install.packages("DSI") -install.packages("DSMolgenisArmadillo") -install.packages("dsBaseClient", repos = c("http://cran.datashield.org", "https://cloud.r-project.org/"), dependencies = TRUE) - -library(dsBaseClient) -library(DSMolgenisArmadillo) diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/1_MolgenisArmadillo.r b/docs/v3.4.0/DataSHIELD-datamanagement/1_MolgenisArmadillo.r deleted file mode 100644 index 78f25bbff..000000000 --- a/docs/v3.4.0/DataSHIELD-datamanagement/1_MolgenisArmadillo.r +++ /dev/null @@ -1,119 +0,0 @@ -## Prerequisites -# -# - Run the code in 0_Prerequisite.r -# -library(MolgenisArmadillo) - -## -# To share your data using Armadillo, you first need to login - -## Login -# -# In order to access the files as a data manager, you need to log in. -# The login method needs the URLs of the Armadillo server and the MinIO file server. -# It will open a browser window where you can identify yourself with the ID provider. -# -# If you are unsure how this login function (or any function) works ask R to provide documentation -?armadillo.login - -armadillo.login( - armadillo = "https://armadillo.test.molgenis.org", - minio = "https://armadillo-minio.test.molgenis.org" -) -#token <- armadillo.get_token("https://armadillo.test.molgenis.org") - -# armadillo.login will create a session and store the credentials in the environment. - -## Structure -# To share data via Armadillo you can have a nested structure to save you data. - -# We distinguish: -# - projects -# - folders -# - tables - -### Projects -# Projects are root-folders you can give persons permissions on. -# you can imagine that you will use a separate project for each study you need to support. -# This way you make sure people can not see each others variables. - -### Folders -# Folder objects can be used to version the different tables you want to share in Armadillo. -# This is not mandatory and are free to use the folder level as you see fit. -# In our examples we will go into the versioning part a bit deeper. - -### Tables -# Tables contain the data you want to share. -# This can be all the data on a certain subject, mostly used in consortia or a specific study you want to expose. - -## Sharing data -# Assume you are in a consortia which has core-variables and outcome-variables. -# You want to share and version the whole dataset to all researchers which applied to access your data. - -# First we will create the project. In our case "ipec". - -cohort <- "workshop2" -armadillo.create_project(cohort) - -# Secondly we will load the table(s) we want to upload to Armadillo in the R-environment. -# We have test data which is in `arrow` format, the upload will take any object that has a table like structure to upload into the Armadillo. -# This can be SPSS, STATA, SAS or R-based data as well. - -library(arrow) - -# load the core data (make sure you are working in the correct directory) -nonrep <- arrow::read_parquet("data/nonrep.parquet") -yearlyrep <- arrow::read_parquet("data/yearlyrep.parquet") - -# explore your data (sanity check) -View(nonrep) -dim(nonrep) -names(nonrep) -table(nonrep$recruit_age) - -# The third step is determining the second level, which contains in this case the datamodel-version the type of variables and the data-version. - -# y_y-#variable-type#-x_x - -# y_y = datamodel version -# x_x = data version - -# upload the core variables -armadillo.upload_table(cohort, "2_1-core-1_0", nonrep) -armadillo.upload_table(cohort, "2_1-core-1_0", yearlyrep) - -## Looking at the data -# There are helper functions to help you determine what is in the storage server. -# You can list projects and tables to what's in the storage. - -# list of projects -armadillo.list_projects() - -# listing tables per project -armadillo.list_tables(cohort) - -# You can download the data in the R-environment as well. - -# download table to local R environment -download_nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") - -# check the column names from the local environment -colnames(download_nonrep) - -# check if local data and uploaded data are equal (optional) -setequal(nonrep, download_nonrep) - -# Now you can also take a look at the files in the user interface of the MinIO file server -# open the MinIO server URL in a browser window (used in armadillo.login). - -# > !IMPORTANT: run this part after subsetting the data - -## Deleting the data -# To delete the data you need to throw away the contents first. - -# throw away the core tables -armadillo.delete_table(cohort, "2_1-core-1_0", "nonrep") -armadillo.delete_table(cohort, "2_1-core-1_0", "yearlyrep") - -# Now you can delete the project. -armadillo.delete_project(cohort) diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r b/docs/v3.4.0/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r deleted file mode 100644 index 0deea0e6b..000000000 --- a/docs/v3.4.0/DataSHIELD-datamanagement/2_Creating_data_subsets_Armadillo.r +++ /dev/null @@ -1,96 +0,0 @@ -## Creating data subsets on the Armadillo MiNIO file server -# When researchers request access to your data they may in many cases not be granted access to the whole data set, -# but only to a subset. On the MinIO file server access is regulated on the project level, -# so you will need to create a new project using a subset of the data. -# Here you can see the different relevant steps you need to take to create these subsets. -# -# - Setting up the environment -# - Logging into the servers -# - Exploring the data -# - Making subsets of the data -# - Upload data subsets -# -# - Delete data subsets - -## Setting up the environment -# - Run the code in 0_Prerequisite.r - -# load required libraries -library(MolgenisArmadillo) -library(dplyr) - -## Logging in to the servers -# In order to access the files on the MinIO fileserver you need to log in using the URLs of the Armadillo server and the MinIO fileserver. -# A browser window will be opened where you can identify yourself with the ID provider. - -armadillo.login( - armadillo = "https://armadillo.test.molgenis.org", - minio = "https://armadillo-minio.test.molgenis.org" -) -# A session will be created and the credentials are stored in the environment. - -## Explore the data -# Let's assume you are in a consortium which has core-variables and outcome-variables. -# You want to share a subset of the whole data set with certain researchers that applied for access to your data. - -# List projects on the MinIO file server -armadillo.list_projects() - -# Next create a study, here called 'subset1'. -# NOTE: change the name of the subset for your own practice run - -study <- "study1" -armadillo.create_project(study) - -# List the tables in a project - -# You want to share data from the cohort you just uploaded (1_MolgenisArmadillo.r). -# Change the cohort_name to your own cohort name here: -cohort = "workshop1" - -# List the available tables within this project. -armadillo.list_tables(cohort) - -# Subset the core variables -# Download the relevant core tables to the local environment - -nonrep <- armadillo.load_table(cohort, "2_1-core-1_0", "nonrep") -yearlyrep <- armadillo.load_table(cohort, "2_1-core-1_0", "yearlyrep") - -# List their variables - -colnames(nonrep) -colnames(yearlyrep) - -# Subset the variables that were requested per table. - -subset_core_nonrep <- nonrep %>% select(child_id, asthma_m, preg_cig, preg_fever, preg_alc) -subset_core_yearlyrep <- yearlyrep %>% select(child_id, cohab_, smk_exp) - -## Uploading the data subset -# Check the variables in the data subset before uploading - -colnames(subset_core_nonrep) -colnames(subset_core_yearlyrep) - -# Upload the data subsets -armadillo.upload_table(study, "2_1-core-1_0", subset_core_nonrep, "nonrep") -armadillo.upload_table(study, "2_1-core-1_0", subset_core_yearlyrep, "yearlyrep") - -# See if tables are uploaded -armadillo.list_tables(study) - -# Now you can also take a look at the files in the user interface of the MinIO file server. In this case: https://armadillo-minio.test.molgenis.org - -# > !IMPORTANT: run this part after subsetting the data - -## Deleting the data -# To delete the data you need to throw away the contents first. - -# throw away the core tables -armadillo.delete_table(study, "2_1-core-1_0", "nonrep") -armadillo.delete_table(study, "2_1-core-1_0", "yearlyrep") - -# Now you can delete the project. - -armadillo.delete_project(study) diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r b/docs/v3.4.0/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r deleted file mode 100644 index f29be2a6d..000000000 --- a/docs/v3.4.0/DataSHIELD-datamanagement/3_Analyse_data_subset_DSMolgenisArmadillo.r +++ /dev/null @@ -1,39 +0,0 @@ -install.packages("DSI") -install.packages("DSMolgenisArmadillo") -install.packages("dsBaseClient", repos = c("http://cran.datashield.org", "https://cloud.r-project.org/"), dependencies = TRUE) - -library(dsBaseClient) -library(DSMolgenisArmadillo) - -# specify server url -armadillo_url <- "https://armadillo.test.molgenis.org" - -# get token from central authentication server -token <- armadillo.get_token(armadillo_url) - - -# build the login dataframe -builder <- DSI::newDSLoginBuilder() -builder$append( - server = "armadillo", - url = armadillo_url, - token = token, - table = "workshop1/2_1-core-1_0/nonrep", - driver = "ArmadilloDriver" -) - -# create loginframe -logindata <- builder$build() - -# login into server -conns <- datashield.login( - logins = logindata, - symbol = "core_nonrep", - variables = c("coh_country"), - assign = TRUE -) - -# calculate the mean -ds.mean("core_nonrep$coh_country", datasources = conns) - -ds.histogram(x = "core_nonrep$coh_country", datasources = conns) diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/data/nonrep.parquet b/docs/v3.4.0/DataSHIELD-datamanagement/data/nonrep.parquet deleted file mode 100644 index 7a99ff0d4..000000000 Binary files a/docs/v3.4.0/DataSHIELD-datamanagement/data/nonrep.parquet and /dev/null differ diff --git a/docs/v3.4.0/DataSHIELD-datamanagement/data/yearlyrep.parquet b/docs/v3.4.0/DataSHIELD-datamanagement/data/yearlyrep.parquet deleted file mode 100644 index 7049168ed..000000000 Binary files a/docs/v3.4.0/DataSHIELD-datamanagement/data/yearlyrep.parquet and /dev/null differ diff --git a/docs/v3.4.0/README.md b/docs/v3.4.0/README.md deleted file mode 100644 index 3352bcfb2..000000000 --- a/docs/v3.4.0/README.md +++ /dev/null @@ -1,322 +0,0 @@ -# Armadillo suite - -# Overview - -Use MOLGENIS/Armadillo to make data available for privacy protecting federated analysis using [DataSHIELD](https://datashield.org) protocol. Armadillo -service provides the following features: -* **manage data projects**. Projects can either hold tabular data in the efficient 'parquet' format or any other file use DataSHIELD - 'resources' framework. -* **grant users access permission**. We use a central OIDC service like KeyCloak or FusionAuth in combination with a trused identity provider like - Life Sciences AAI to authenticate users. -* **configure DataSHIELD analysis profiles**. [DataSHIELD analysis profiles](https://www.datashield.org/help/standard-profiles-and-plaforms) are - Docker images that contain a collection of multiple [DataSHIELD analysis packages](https://www.datashield.org/help/community-packages). - -![DataSHIELD overview](./img/overview-datashield.png) - -# Armadillo installation -Armadillo requires Java to run, Docker to access the DataSHIELD profiles, and OIDC for authentication (not needed for local tests). Below instructions how to run Armadillo directly from Java, as a Docker container, as a service on Ubuntu or from source code. -Note that for production you should add a https proxy for essential security. And you might need to enable 'Docker socket' on your docker service. - -### Run Armadillo using java commandline -Software developers often run Armadillo as java jar file: - -1. Install Java and Docker (for the DataSHIELD profiles) -2. Download Armadillo jar file from [releases](https://github.com/molgenis/molgenis-service-armadillo/releases), for example: -[molgenis-armadillo-3.3.0.jar](https://github.com/molgenis/molgenis-service-armadillo/releases/download/V3.3.0/) -3. Run armadillo using ```java -jar molgenis-armadillo-3.3.0.jar``` -4. Go to http://localhost:8080 to see your Armadillo running. - -Default Armadillo will start with only 'basic-auth' and user 'admin' with password 'admin'. You can enable 'oidc' for connecting more users. You can change -by providing and editing [application.yaml](application.template.yml) file -in your working directory and then run command above again. - -### Run Armadillo via docker compose -For testing without having to installing Java you can run using docker: - -1. Install [docker-compose](https://docs.docker.com/compose/install/) -2. Download this [docker-compose.yml](docker-compose.yml). -3. Execute ```docker-compose up``` -4. Once it says 'Started' go to http://localhost:8080 to see your Armadillo running. - -The command must run in same directory as downloaded docker file. We made docker available via 'docker.sock' so we can start/stop DataSHIELD profiles. Alternatively you must include the datashield profiles into this docker-compose. You can override all application.yaml settings via environment variables -(see commented code in docker-compose file). - -### Run Armadillo as service on Ubuntu -We run Armadillo in production as a Linux service on Ubuntu, ensuring it gets restarted when the server is rebooted. You might be able to reproduce also on -CentOS (using yum instead of apt). - -#### 1. Install necessary software -``` -apt update -apt install openjdk-19-jre-headless -apt install docker.io -``` -Note: you might need 'sudo' - -#### 2. Run installation script - -> Download [./scripts/armadillo-setup.sh](./scripts/armadillo-setup.sh) or use it's URL to wget it from the server. - -Run wget with URL from above -``` -wget URL -``` -This step will install most recent [release](https://github.com/molgenis/molgenis-service-armadillo/releases): -``` -bash armadillo-setup.sh \ - --admin-user admin \ - --admin-password xxxxx - --domain my.server.com \ - --oidc \ - --oidc_url https://lifecycle-auth.molgenis.org \ - --oidc_clientid clientid \ - --oidc_clientsecret secret \ - --cleanup -``` -Note: adapt install command to suit your situation. Use --help to see the options. https://lifecycle-auth.molgenis.org is MOLGENIS provided OIDC service but -you can also use your own, see FAQ below. - -### Running Armadillo from source code - -You can run from source code as follows: -1. Install Java and Docker -2. Checkout the source using ```git clone https://github.com/molgenis/molgenis-service-armadillo.git``` -3. Optionally copy ```application.template.yml``` to ```application.yml``` to change settings (will be .gitignored) -4. Compile and execute the code using ```./gradlew run``` - -Note: contact MOLGENIS team if you want to contribute and need a testing OIDC config that you can run against localhost. - -# Using Armadillo -Armadillo has three main screens to manage projects, user access and DataSHIELD profiles: - -### Create data access projects -Data stewards can use the Armadillo web user interface or [MolgenisArmadillo R client](https://molgenis.github.io/molgenis-r-armadillo) -to create 'projects' and upload their data into those. Data tables need to be in parquet format that supports fast selections of the columns -(variables) you need for analysis. Other files can be configured as 'resources'. - -### Manage user access -Data stewards can use the permission screen to give email adresses access to the data. Everybody signs in via single sign on using an OIDC central -authentication server such as KeyCloack or Fusion auth that federates to authentication systems of -connected institutions, ideally using a federated AAI such as LifeScience AAI. - -### Configure DataSHIELD profiles -To analyse data, users must choose a datashield profile. Armadillo owners can use the web user interface to configure new profiles. Assuming you -installed docker you can also start/stop these images. Alternatively you can use docker-compose for that. - -There are DataSHIELD packages for [standard statistical analysis](https://github.com/datashield/dsBaseClient) -, [exposome studies](https://github.com/isglobal-brge/dsExposomeClient) -, [survival studies](https://github.com/neelsoumya/dsSurvivalClient) -, [microbiome studies](https://github.com/StuartWheater/dsMicrobiomeClient) -and [analysis tools for studies that are using large genetic datasets](https://github.com/isglobal-brge/dsomicsclient) -. These packages can all be installed in the Armadillo suite. - -### End users can use Armadillo as any other DataSHIELD server -A researcher connects from an [R client](https://molgenis.github.io/molgenis-r-datashield) to one or multiple Armadillo servers. The data is -loaded into an R session on the Armadillo server specifically created for the researcher. Analysis requests are sent to the R session on each Armadillo server. -There the analysis is performed and aggregated results are sent back to the client. - -# Developing Armadillo - -We use gradle to build: -* run using ```./gradlew run``` -* run tests using ```./gradlew test``` - -We use intellij to develop -* To run or debug in intellij, right click on armadillo/src/main/java/org.molgenis.armdadillo/ArmadilloServiceAppliction and choose 'Run/Debug Armadillo...' -* To run using oidc, create a copy of [application.yml](application.template.yml) in root of your project - -We have a swagger-ui to quickly see and test available web services at http://localhost:8080/swagger-ui/ - -# Developing DataSHIELD packages in Armadillo -As package developer will want to push your new packages into a DataSHIELD profile - -* You can start Armadillo with defaults as described above; then use admin/admin as authentication -* to see what profile are available and has been selected: -``` -curl -u admin:admin http://localhost:8080/profiles -``` -* to change selected profile 'my-profile': -``` -curl -X POST http://localhost:8080/select-profile \ - -H 'Content-Type: application/json' \ - -d 'default' -``` -* to install-packages in DataSHIELD current using admin user: -``` -curl -u admin:admin -v \ --H 'Content-Type: multipart/form-data' \ --F "file=@dsBase_6.3.0.tar.gz" \ --X POST http://localhost:8080/install-package -``` -* to update whitelist of your current profile: -``` -curl -u admin:admin -X POST http://localhost:8080/whitelist/dsBase -``` -* to get whitelist of current profile: -``` -curl -u admin:admin http://localhost:8080/whitelist -``` - -# Frequently asked questions - -### Docker gives a 'java.socket' error -You might need to enable Docker socket. On Docker desktop you can find that under 'settings' and 'advanced'. - -### Can I use docker compose to start profiles? -Instead of making Armadillo start/stop DataSHIELD profiles you can also use docker compose. -See commented section in docker-compose.yml file. - -### Can I pass environment or commandline variables instead of application.yml? - -Yes, it is standard spring. - -### Can I run Armadillo with oauth2 config offline? -Yes, you can run in 'offline' profile -``` -./gradlew run -Dspring.profiles.active=offline -``` - -### How to import data from Armadillo 2? -To export data from and Armadillo 2 server take the following steps: - -#### 1. Install helper software -``` -apt update -apt install pip -pip install minio -pip install fusionauth-client -pip install simple_term_menu -``` - -#### 2. Backup Armadillo 2 settings - -``` -mkdir armadillo2-backup -rsync -avr /usr/share/armadillo armadillo2-backup -cp /etc/armadillo/application.yml armadillo2-backup/application-armadillo2.yml -``` -N.B.change /usr/share to path matching your local config. - -##### 3. Export data from Armadillo 2 -This step will copy Armadillo 2 data from minio into the folder matching of an Armadillo 3 data folder: - -``` -mkdir data -wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/v3.4.0/scripts/migrate-minio.py -python3 migrate-minio.py --minio http://localhost:9000 --target data -``` - -N.B.: when aiming running armadillo as a service this folder should be /usr/share/armadillo/data - -#### 4. Stop all docker images for Armadillo 2 -List all docker images -```docker ps -a``` - -Stop and remove all Armadillo 2 related images, e.g. -``` -docker rm armadillo_auth_1 armadillo_console_1 armadillo_rserver-default_1 armadillo_rserver-mediation_1 armadillo_rserver-exposome_1 armadillo_rserver-omics_1 armadillo_armadillo_1 -f -``` - -#### 5. Remove old minio data -```rm -Rf /var/lib/minio/ ``` - -#### 6. Run Armadillo 3 using exported data -Make sure to move the exported data into the new 'data' folder. Optionally you might need to fix user permissions, e.g.: -``` -chown armadillo:armadillo -R data -``` - -#### 7. Optionally, acquire a permission set from MOLGENIS team -If you previously ran central authorisation server with MOLGENIS team they can provide you with procedure to load pre-existing permissions. -They will use: -``` -wget https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/v3.4.0/scripts/migrate-auth.py -python3 migrate-auth.py --fusion-auth https://lifecycle-auth.molgenis.org --armadillo http://localhost:8080 -``` - -### How to run previous armadillo 2? - -For armadillo 2.x you can follow instructions at -* for testing we use docker compose at https://github.com/molgenis/molgenis-service-armadillo/tree/armadillo-service-2.2.3 -* for production we are using Ansible at https://galaxy.ansible.com/molgenis/armadillo` - -### How to run Armadillo as developer? - -We develop Armadillo using IntelliJ. - -#### To build Armadillo -To build run following command in the github root: -```./gradlew build``` - -To execute in 'dev' run following command in the github root: -```./gradlew run``` - -#### Setting up development tools - -This repository uses `pre-commit` to manage commit hooks. An installation guide can be found -[here](https://pre-commit.com/index.html#1-install-pre-commit). To install the hooks, run `pre-commit install` once in the root folder of this repository. Now -your code will be automatically formatted whenever you commit. - -#### How to change data directory - -Data is automatically stored in the `data` folder in this repository. You can choose another location -in `application.yml` by changing the `storage.root-dir` -setting. - -> **_Note_**: When you run Armadillo locally for the first time, the `lifecycle` project has not been -> added to the system metadata yet. To add it automatically, see [Application properties](#application-properties). -> Or you can add it manually: -> - Go to the Swagger UI (`http://localhost:8080/swagger-ui/index.html`) -> - Go to the `PUT /access/projects` endpoint -> - Add the project `lifecycle` -> -> Now you're all set! - -#### Working with resources in development mode -When developing locally, docker has trouble connecting to localhost. This problem becomes clear when working with -resources. Luckily there's a quick fix for the problem. Instead of defining a resource as for example -`http://localhost:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`, rewrite it to: -`http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda`. Here's some example R code -for uploading resources: -```R -## Uploading resources -library(MolgenisArmadillo) -library(resourcer) - -token <- armadillo.get_token("http://localhost:8080/") - -resGSE1 <- resourcer::newResource( - name = "GSE66351_1", - secret = token, - url = "http://host.docker.internal:8080/storage/projects/omics/objects/test%2Fgse66351_1.rda", - format = "ExpressionSet" -) - -armadillo.login("http://localhost:8080/") -armadillo.upload_resource(project="omics", folder="ewas", resource = resGSE1, name = "GSE66351_1") -``` -And for using them: -```R -library(DSMolgenisArmadillo) -library(dsBaseClient) - -token <- armadillo.get_token("http://localhost:8080/") - -builder <- DSI::newDSLoginBuilder() -builder$append( - server = "local", - url = "http://localhost:8080/", - token = token, - driver = "ArmadilloDriver", - profile = "uniform", - resource = "omics/ewas/GSE66351_1" -) - -login_data <- builder$build() -conns <- DSI::datashield.login(logins = login_data, assign = TRUE) - -datashield.resources(conns = conns) -datashield.assign.resource(conns, resource="omics/ewas/GSE66351_1", symbol="eSet_0y_EUR") -ds.class('eSet_0y_EUR', datasources = conns) -datashield.assign.expr(conns, symbol = "methy_0y_EUR",expr = quote(as.resource.object(eSet_0y_EUR))) -``` diff --git a/docs/v3.4.0/_cdns/docsify-sidebar-collapse.min.js b/docs/v3.4.0/_cdns/docsify-sidebar-collapse.min.js deleted file mode 100644 index 2b067c7e2..000000000 --- a/docs/v3.4.0/_cdns/docsify-sidebar-collapse.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){("object"!=typeof exports||"undefined"==typeof module)&&"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";function e(e,n){var t,a=(n=void 0===n?{}:n).insertAt;e&&"undefined"!=typeof document&&(t=document.head||document.getElementsByTagName("head")[0],(n=document.createElement("style")).type="text/css","top"===a&&t.firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e)))}var t;function a(e){e&&null!=t&&(e=e.getBoundingClientRect().top,document.querySelector(".sidebar").scrollBy(0,e-t))}function n(){requestAnimationFrame(function(){var e=document.querySelector(".app-sub-sidebar > .active");if(e)for(e.parentNode.parentNode.querySelectorAll(".app-sub-sidebar").forEach(function(e){return e.classList.remove("open")});e.parentNode.classList.contains("app-sub-sidebar")&&!e.parentNode.classList.contains("open");)e.parentNode.classList.add("open"),e=e.parentNode})}function o(e){t=e.target.getBoundingClientRect().top;var n=d(e.target,"LI",2);n&&(n.classList.contains("open")?(n.classList.remove("open"),setTimeout(function(){n.classList.add("collapse")},0)):(function(e){if(e)for(e.classList.remove("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.remove("open"),e=e.parentNode}(s()),i(n),setTimeout(function(){n.classList.remove("collapse")},0)),a(n))}function s(){var e=document.querySelector(".sidebar-nav .active");return e||(e=d(document.querySelector('.sidebar-nav a[href="'.concat(decodeURIComponent(location.hash).replace(/ /gi,"%20"),'"]')),"LI",2))&&e.classList.add("active"),e}function i(e){if(e)for(e.classList.add("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.add("open"),e=e.parentNode}function d(e,n,t){if(e&&e.tagName===n)return e;for(var a=0;e;){if(t<++a)return;if(e.parentNode.tagName===n)return e.parentNode;e=e.parentNode}}e(".sidebar-nav > ul > li ul {\n display: none;\n}\n\n.app-sub-sidebar {\n display: none;\n}\n\n.app-sub-sidebar.open {\n display: block;\n}\n\n.sidebar-nav .open > ul:not(.app-sub-sidebar),\n.sidebar-nav .active:not(.collapse) > ul {\n display: block;\n}\n\n/* 抖动 */\n.sidebar-nav li.open:not(.collapse) > ul {\n display: block;\n}\n\n.active + ul.app-sub-sidebar {\n display: block;\n}\n"),document.addEventListener("scroll",n);e("@media screen and (max-width: 768px) {\n /* 移动端适配 */\n .markdown-section {\n max-width: none;\n padding: 16px;\n }\n /* 改变原来按钮热区大小 */\n .sidebar-toggle {\n padding: 0 0 10px 10px;\n }\n /* my pin */\n .sidebar-pin {\n appearance: none;\n outline: none;\n position: fixed;\n bottom: 0;\n border: none;\n width: 40px;\n height: 40px;\n background: transparent;\n }\n}\n");var r,c="DOCSIFY_SIDEBAR_PIN_FLAG";function l(){var e="true"===(e=localStorage.getItem(c));localStorage.setItem(c,!e),e?(document.querySelector(".sidebar").style.transform="translateX(0)",document.querySelector(".content").style.transform="translateX(0)"):(document.querySelector(".sidebar").style.transform="translateX(300px)",document.querySelector(".content").style.transform="translateX(300px)")}768 ul"),1),a(t),n(e)}),e.ready(function(){document.querySelector(".sidebar-nav").addEventListener("click",o)})})}); \ No newline at end of file diff --git a/docs/v3.4.0/_cdns/docsify@4.js b/docs/v3.4.0/_cdns/docsify@4.js deleted file mode 100644 index 80be8c5fd..000000000 --- a/docs/v3.4.0/_cdns/docsify@4.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function c(i){var o=Object.create(null);return function(e){var n=f(e)?e:JSON.stringify(e);return o[n]||(o[n]=i(e))}}var a=c(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),u=Object.prototype.hasOwnProperty,m=Object.assign||function(e){for(var n=arguments,i=1;i=e||n.classList.contains("hidden")?S(h,"add","sticky"):S(h,"remove","sticky"))}function ee(e,n,o,i){var t=[];null!=(n=l(n))&&(t=k(n,"a"));var a,r=decodeURI(e.toURL(e.getCurrentPath()));return t.sort(function(e,n){return n.href.length-e.href.length}).forEach(function(e){var n=decodeURI(e.getAttribute("href")),i=o?e.parentNode:e;e.title=e.title||e.innerText,0!==r.indexOf(n)||a?S(i,"remove","active"):(a=e,S(i,"add","active"))}),i&&(v.title=a?a.title||a.innerText+" - "+J:J),a}function ne(e,n){for(var i=0;ithis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,n,i,o){return(e/=o/2)<1?i/2*e*e+n:-i/2*(--e*(e-2)-1)+n}}]),re);function re(){var e=0c){n=n||p;break}n=p}!n||(r=fe[ve(e,n.getAttribute("data-id"))])&&r!==a&&(a&&a.classList.remove("active"),r.classList.add("active"),a=r,!pe&&h.classList.contains("sticky")&&(e=i.clientHeight,r=a.offsetTop+a.clientHeight+40,a=a.offsetTop>=t.scrollTop&&r<=t.scrollTop+e,i.scrollTop=a?t.scrollTop:+r"']/),xe=/[&<>"']/g,Se=/[<>"']|&(?!#?\w+;)/,Ae=/[<>"']|&(?!#?\w+;)/g,$e={"&":"&","<":"<",">":">",'"':""","'":"'"};var ze=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function Fe(e){return e.replace(ze,function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""})}var Ee=/(^|[^\[])\^/g;var Re=/[^\w:]/g,Te=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var Ce={},je=/^[^:]+:\/*[^/]*$/,Le=/^([^:]+:)[\s\S]*$/,Oe=/^([^:]+:\/*[^/]*)[\s\S]*$/;function qe(e,n){Ce[" "+e]||(je.test(e)?Ce[" "+e]=e+"/":Ce[" "+e]=Pe(e,"/",!0));var i=-1===(e=Ce[" "+e]).indexOf(":");return"//"===n.substring(0,2)?i?n:e.replace(Le,"$1")+n:"/"===n.charAt(0)?i?n:e.replace(Oe,"$1")+n:e+n}function Pe(e,n,i){var o=e.length;if(0===o)return"";for(var t=0;tn)i.splice(n);else for(;i.length>=1,e+=e;return i+e},We=we.defaults,Xe=Be,Qe=Ze,Je=Me,Ke=Ve;function en(e,n,i){var o=n.href,t=n.title?Je(n.title):null,n=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:i,href:o,title:t,text:n}:{type:"image",raw:i,href:o,title:t,text:Je(n)}}var nn=function(){function e(e){this.options=e||We}return e.prototype.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=i.length?e.slice(i.length):e}).join("\n")}(i,n[3]||"");return{type:"code",raw:i,lang:n[2]&&n[2].trim(),text:e}}},e.prototype.heading=function(e){var n=this.rules.block.heading.exec(e);if(n){var i=n[2].trim();return/#$/.test(i)&&(e=Xe(i,"#"),!this.options.pedantic&&e&&!/ $/.test(e)||(i=e.trim())),{type:"heading",raw:n[0],depth:n[1].length,text:i}}},e.prototype.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var n={type:"table",header:Qe(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(n.header.length===n.align.length){for(var i=n.align.length,o=0;o ?/gm,"");return{type:"blockquote",raw:n[0],text:e}}},e.prototype.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var n,i,o,t,a,r=e[0],c=e[2],u=1s[1].length:o[1].length>s[0].length||3/i.test(e[0])&&(n=!1),!i&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?i=!0:i&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(i=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:n,inRawBlock:i,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]}},e.prototype.link=function(e){var n=this.rules.inline.link.exec(e);if(n){e=n[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;var i=Xe(e.slice(0,-1),"\\");if((e.length-i.length)%2==0)return}else{var o=Ke(n[2],"()");-1$/.test(e)?i.slice(1):i.slice(1,-1):i)&&i.replace(this.rules.inline._escapes,"$1"),title:o&&o.replace(this.rules.inline._escapes,"$1")},n[0])}},e.prototype.reflink=function(e,n){if((i=this.rules.inline.reflink.exec(e))||(i=this.rules.inline.nolink.exec(e))){var e=(i[2]||i[1]).replace(/\s+/g," ");if((e=n[e.toLowerCase()])&&e.href)return en(i,e,i[0]);var i=i[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.strong.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="**"===o[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.strong.middle.exec(n.slice(0,o.index+3)))return{type:"strong",raw:e.slice(0,t[0].length),text:e.slice(2,t[0].length-2)}}},e.prototype.em=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.em.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="*"===o[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.em.middle.exec(n.slice(0,o.index+2)))return{type:"em",raw:e.slice(0,t[0].length),text:e.slice(1,t[0].length-1)}}},e.prototype.codespan=function(e){var n=this.rules.inline.code.exec(e);if(n){var i=n[2].replace(/\n/g," "),o=/[^ ]/.test(i),e=/^ /.test(i)&&/ $/.test(i);return o&&e&&(i=i.substring(1,i.length-1)),i=Je(i,!0),{type:"codespan",raw:n[0],text:i}}},e.prototype.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},e.prototype.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},e.prototype.autolink=function(e,n){e=this.rules.inline.autolink.exec(e);if(e){var i,n="@"===e[2]?"mailto:"+(i=Je(this.options.mangle?n(e[1]):e[1])):i=Je(e[1]);return{type:"link",raw:e[0],text:i,href:n,tokens:[{type:"text",raw:i,text:i}]}}},e.prototype.url=function(e,n){var i,o,t,a;if(i=this.rules.inline.url.exec(e)){if("@"===i[2])t="mailto:"+(o=Je(this.options.mangle?n(i[0]):i[0]));else{for(;a=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0],a!==i[0];);o=Je(i[0]),t="www."===i[1]?"http://"+o:o}return{type:"link",raw:i[0],text:o,href:t,tokens:[{type:"text",raw:o,text:o}]}}},e.prototype.inlineText=function(e,n,i){e=this.rules.inline.text.exec(e);if(e){i=n?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]:Je(this.options.smartypants?i(e[0]):e[0]);return{type:"text",raw:e[0],text:i}}},e}(),Ze=De,Ve=Ne,De=Ue,Ne={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:Ze,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ne.def=Ve(Ne.def).replace("label",Ne._label).replace("title",Ne._title).getRegex(),Ne.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ne.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,Ne.item=Ve(Ne.item,"gm").replace(/bull/g,Ne.bullet).getRegex(),Ne.listItemStart=Ve(/^( *)(bull)/).replace("bull",Ne.bullet).getRegex(),Ne.list=Ve(Ne.list).replace(/bull/g,Ne.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ne.def.source+")").getRegex(),Ne._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ne._comment=/|$)/,Ne.html=Ve(Ne.html,"i").replace("comment",Ne._comment).replace("tag",Ne._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ne.paragraph=Ve(Ne._paragraph).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.blockquote=Ve(Ne.blockquote).replace("paragraph",Ne.paragraph).getRegex(),Ne.normal=De({},Ne),Ne.gfm=De({},Ne.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Ne.gfm.nptable=Ve(Ne.gfm.nptable).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.gfm.table=Ve(Ne.gfm.table).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.pedantic=De({},Ne.normal,{html:Ve("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ne._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:Ve(Ne.normal._paragraph).replace("hr",Ne.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ne.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});Ze={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};Ze.punctuation=Ve(Ze.punctuation).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Ze._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Ze._comment=Ve(Ne._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ze.em.start=Ve(Ze.em.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.middle=Ve(Ze.em.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.em.endAst=Ve(Ze.em.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.endUnd=Ve(Ze.em.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.start=Ve(Ze.strong.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.middle=Ve(Ze.strong.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.strong.endAst=Ve(Ze.strong.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.endUnd=Ve(Ze.strong.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.blockSkip=Ve(Ze._blockSkip,"g").getRegex(),Ze.overlapSkip=Ve(Ze._overlapSkip,"g").getRegex(),Ze._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ze._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ze._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ze.autolink=Ve(Ze.autolink).replace("scheme",Ze._scheme).replace("email",Ze._email).getRegex(),Ze._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ze.tag=Ve(Ze.tag).replace("comment",Ze._comment).replace("attribute",Ze._attribute).getRegex(),Ze._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ze._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ze._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ze.link=Ve(Ze.link).replace("label",Ze._label).replace("href",Ze._href).replace("title",Ze._title).getRegex(),Ze.reflink=Ve(Ze.reflink).replace("label",Ze._label).getRegex(),Ze.reflinkSearch=Ve(Ze.reflinkSearch,"g").replace("reflink",Ze.reflink).replace("nolink",Ze.nolink).getRegex(),Ze.normal=De({},Ze),Ze.pedantic=De({},Ze.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:Ve(/^!?\[(label)\]\((.*?)\)/).replace("label",Ze._label).getRegex(),reflink:Ve(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ze._label).getRegex()}),Ze.gfm=De({},Ze.normal,{escape:Ve(Ze.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(i?e:gn(e,!0))+"\n":"
    "+(i?e:gn(e,!0))+"
    \n"},e.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,i,o){return this.options.headerIds?"'+e+"\n":""+e+"\n"},e.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},e.prototype.list=function(e,n,i){var o=n?"ol":"ul";return"<"+o+(n&&1!==i?' start="'+i+'"':"")+">\n"+e+"\n"},e.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},e.prototype.checkbox=function(e){return" "},e.prototype.paragraph=function(e){return"

    "+e+"

    \n"},e.prototype.table=function(e,n){return"\n\n"+e+"\n"+(n=n&&""+n+"")+"
    \n"},e.prototype.tablerow=function(e){return"\n"+e+"\n"},e.prototype.tablecell=function(e,n){var i=n.header?"th":"td";return(n.align?"<"+i+' align="'+n.align+'">':"<"+i+">")+e+"\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;e='"},e.prototype.image=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;i=''+i+'":">"},e.prototype.text=function(e){return e},e}(),ln=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,i){return""+i},e.prototype.image=function(e,n,i){return""+i},e.prototype.br=function(){return""},e}(),vn=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var i=e,o=0;if(this.seen.hasOwnProperty(i))for(o=this.seen[e];i=e+"-"+ ++o,this.seen.hasOwnProperty(i););return n||(this.seen[e]=o,this.seen[i]=0),i},e.prototype.slug=function(e,n){void 0===n&&(n={});e=this.serialize(e);return this.getNextSafeSlug(e,n.dryrun)},e}(),hn=we.defaults,_n=Ie,mn=function(){function i(e){this.options=e||hn,this.options.renderer=this.options.renderer||new sn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ln,this.slugger=new vn}return i.parse=function(e,n){return new i(n).parse(e)},i.parseInline=function(e,n){return new i(n).parseInline(e)},i.prototype.parse=function(e,n){void 0===n&&(n=!0);for(var i,o,t,a,r,c,u,f,p,d,g,s,l,v,h,_="",m=e.length,b=0;bAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}}xn.options=xn.setOptions=function(e){return bn(xn.defaults,e),yn(xn.defaults),xn},xn.getDefaults=Me,xn.defaults=we,xn.use=function(a){var n,e=bn({},a);if(a.renderer){var i,r=xn.defaults.renderer||new sn;for(i in a.renderer)!function(o){var t=r[o];r[o]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.renderer[o].apply(r,e);return i=!1===i?t.apply(r,e):i}}(i);e.renderer=r}if(a.tokenizer){var t,c=xn.defaults.tokenizer||new nn;for(t in a.tokenizer)!function(){var o=c[t];c[t]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.tokenizer[t].apply(c,e);return i=!1===i?o.apply(c,e):i}}();e.tokenizer=c}a.walkTokens&&(n=xn.defaults.walkTokens,e.walkTokens=function(e){a.walkTokens(e),n&&n(e)}),xn.setOptions(e)},xn.walkTokens=function(e,n){for(var i=0,o=e;iAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}},xn.Parser=mn,xn.parser=mn.parse,xn.Renderer=sn,xn.TextRenderer=ln,xn.Lexer=fn,xn.lexer=fn.lex,xn.Tokenizer=nn,xn.Slugger=vn;var Sn=xn.parse=xn;function An(e,i){if(void 0===i&&(i='
      {inner}
    '),!e||!e.length)return"";var o="";return e.forEach(function(e){var n=e.title.replace(/(<([^>]+)>)/g,"");o+='
  • '+e.title+"
  • ",e.children&&(o+=An(e.children,i))}),i.replace("{inner}",o)}function $n(e,n){return'

    '+n.slice(5).trim()+"

    "}function zn(e,o){var t=[],a={};return e.forEach(function(e){var n=e.level||1,i=n-1;o?@[\]^`{|}~]/g;function Rn(e){return e.toLowerCase()}function Tn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Rn).replace(/<[^>]+>/g,"").replace(En,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=Fn[n],e=u.call(Fn,n)?e+1:0;return n=(Fn[n]=e)?n+"-"+e:n}Tn.clear=function(){Fn={}};var Cn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function jn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=Cn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return"&#x"+e+";"}).join("‍").concat("︎")+"":''+o+'':i;var i,o}).replace(/__colon__/g,":")}function Ln(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}function On(e){return(e=void 0===e?"":e).replace(/(<\/?a.*?>)/gi,"")}var qn,Pn=be(function(e){var u,f,p,d,n,g=function(u){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},R={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof T?new T(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=r.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof T)){var k,w=1;if(l){if(!(k=C(h,m,n,s))||k.index>=n.length)break;var y=k.index,x=k.index+k[0].length,S=m;for(S+=_.value.length;S<=y;)_=_.next,S+=_.value.length;if(S-=_.value.length,m=S,_.value instanceof T)continue;for(var A=_;A!==i.tail&&(Sr.reach&&(r.reach=E);b=_.prev;z&&(b=j(i,b,z),m+=z.length),L(i,b,w);$=new T(c,g?R.tokenize($,g):$,v,$);_=j(i,b,$),F&&j(i,_,F),1r.reach&&(r.reach=E.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=R.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=R.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:T};function T(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function C(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function a(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function j(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function L(e,n,i){for(var o=n.next,t=0;t"+t.content+""},!u.document)return u.addEventListener&&(R.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;u.postMessage(R.highlight(e,R.languages[i],i)),n&&u.close()},!1)),R;var o=R.util.currentScript();function t(){R.manual||R.highlightAll()}return o&&(R.filename=o.src,o.hasAttribute("data-manual")&&(R.manual=!0)),R.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),R}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=g),void 0!==me&&(me.Prism=g),g.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},g.languages.markup.tag.inside["attr-value"].inside.entity=g.languages.markup.entity,g.languages.markup.doctype.inside["internal-subset"].inside=g.languages.markup,g.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(g.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:g.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:g.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},g.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(g.languages.markup.tag,"addAttribute",{value:function(e,n){g.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:g.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),g.languages.html=g.languages.markup,g.languages.mathml=g.languages.markup,g.languages.svg=g.languages.markup,g.languages.xml=g.languages.extend("markup",{}),g.languages.ssml=g.languages.xml,g.languages.atom=g.languages.xml,g.languages.rss=g.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+n.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(g),g.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},g.languages.javascript=g.languages.extend("clike",{"class-name":[g.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),g.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,g.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:g.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:g.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:g.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:g.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:g.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),g.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:g.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),g.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),g.languages.markup&&(g.languages.markup.tag.addInlined("script","javascript"),g.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),g.languages.js=g.languages.javascript,void 0!==g&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),u={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',g.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),g.hooks.add("before-sanity-check",function(e){var t,n,i,o,a,r,c=e.element;c.matches(d)&&(e.code="",c.setAttribute(f,p),(t=c.appendChild(document.createElement("CODE"))).textContent="Loading…",i=c.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=u[n]||n),g.util.setLanguage(t,e),g.util.setLanguage(c,e),(n=g.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){c.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(c.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),c.hasAttribute("data-start")||c.setAttribute("data-start",String(i+1))),t.textContent=e,g.highlightElement(t)},a=function(e){c.setAttribute(f,"failed"),t.textContent=e},(r=new XMLHttpRequest).open("GET",i,!0),r.onreadystatechange=function(){4==r.readyState&&(r.status<400&&r.responseText?o(r.responseText):400<=r.status?a("✖ Error "+r.status+" while fetching file: "+r.statusText):a("✖ Error: File does not exist or is empty"))},r.send(null))}),n=!(g.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)g.highlightElement(n)}}),g.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),g.plugins.fileHighlight.highlight.apply(this,arguments)})});function Mn(e,n){return"___"+e.toUpperCase()+n+"___"}qn=Prism,Object.defineProperties(qn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,a){var r;o.language===t&&(r=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var n,i=r.length;-1!==o.code.indexOf(n=Mn(t,i));)++i;return r[i]=e,n}),o.grammar=qn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,g;f.language===p&&f.tokenStack&&(f.grammar=qn.languages[p],d=0,g=Object.keys(f.tokenStack),function e(n){for(var i=0;i=g.length);i++){var o,t,a,r,c,u=n[i];"string"==typeof u||u.content&&"string"==typeof u.content?(t=g[d],a=f.tokenStack[t],o="string"==typeof u?u:u.content,c=Mn(p,t),-1<(r=o.indexOf(c))&&(++d,t=o.substring(0,r),a=new qn.Token(p,qn.tokenize(a,f.grammar),"language-"+p,a),r=o.substring(r+c.length),c=[],t&&c.push.apply(c,e([t])),c.push(a),r&&c.push.apply(c,e([r])),"string"==typeof u?n.splice.apply(n,[i,1].concat(c)):u.content=c)):u.content&&e(u.content)}return n}(f.tokens))}}});function In(t,e){var a=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var r=o(e=t.markdown||{})?e(Sn,n):(Sn.setOptions(m(e,{renderer:m(n,e.renderer)})),Sn);this._marked=r,this.compile=function(i){var o=!0,e=c(function(e){o=!1;var n="";return i&&(n=f(i)?r(i):r.parser(i),n=t.noEmoji?n:jn(n,t.nativeEmoji),Tn.clear(),n)})(i),n=a.router.parse().file;return o?a.toc=a.cacheTOC[n]:a.cacheTOC[n]=[].concat(a.toc),e}}var Nn={},Hn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};In.prototype.compileEmbed=function(e,n){var i,o,t=Ln(n),a=t.str,t=t.config;if(n=a,t.include)return R(e)||(e=q(this.contentBase,C(this.router.getCurrentPath()),e)),t.type&&(o=Hn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Hn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},In.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore} --\x3e",""),e.title=On(o),e.ignoreSubHeading=!0),/{docsify-ignore}/g.test(o)&&(o=o.replace("{docsify-ignore}",""),e.title=On(o),e.ignoreSubHeading=!0),//g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),e.title=On(o),e.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(o)&&(o=o.replace("{docsify-ignore-all}",""),e.title=On(o),e.ignoreAllSubs=!0);i=Tn(t.id||o),t=a.toURL(a.getCurrentPath(),{id:i});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=Pn.languages[n=void 0===n?"markup":n]||Pn.languages.markup;return'
    '+Pn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
    "},t.link=(i=(n={renderer:e,router:a,linkTarget:n,linkRel:i,compilerClass:g}).renderer,c=n.router,u=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=Ln(n=void 0===n?"":n),a=t.str,t=t.config;return u=t.target||u,r="_blank"===u?f.config.externalLinkRel||"noopener":"",n=a,R(e)||f._matchNotCompileLink(e)||t.ignore?(R(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+u+'"'),o.push(0!==e.indexOf("mailto:")&&""!==r?' rel="'+r+'"':"")):(e===f.config.homepage&&(e="README"),e=c.toURL(e,null,c.getCurrentPath())),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?$n("tip",e):/^\?>/.test(e)?$n("warn",e):"

    "+e+"

    ";return e},t.image=(o=(i={renderer:e,contentBase:o,router:a}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],a=Ln(n),r=a.str,a=a.config;return n=r,a["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),a.size&&(n=(r=a.size.split("x"))[0],(r=r[1])?t.push('width="'+n+'" height="'+r+'"'):t.push('width="'+n+'"')),a.class&&t.push('class="'+a.class+'"'),a.id&&t.push('id="'+a.id+'"'),R(e)||(o=q(p,C(d.getCurrentPath()),e)),0":''+i+'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},In.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var a=0;a{inner}");this.cacheTree[o]=n}return t},In.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),a({token:t,embedToken:n}),++u>=c&&a({})}}(n);n.embed.url?X(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:c,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),r=r.slice(0,n).concat(i,r.slice(n+1)),p.push({start:n,length:i.length-1})):(Bn[t]=r.concat(),r.links=Bn[t].links=f,o(r))})}function Yn(e,n,i){var o,t,a,r;return n="function"==typeof i?i(n):"string"==typeof i?(a=[],r=0,(o=i).replace(V,function(n,e,i){a.push(o.substring(r,i-1)),r=i+=n.length+1,a.push(t&&t[n]||function(e){return("00"+("string"==typeof Y[n]?e[Y[n]]():Y[n](e))).slice(-n.length)})}),r!==o.length&&a.push(o.substring(r)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var a=0,r=k(".markdown-section > *").filter(n);ascript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var u,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!Un&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(Un=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(u=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,k(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||u.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return Un&&(n.data=function(){return Un}),[e,n]})));for(var g=0,s=p;g([^<]*?)

    $'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],S(o,"add","has-mask"),R(n[1])||(e=q(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):S(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'
    \x3c!--cover--\x3e
    ')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=q(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"","
    "+('')+'
    \x3c!--main--\x3e
    '),this._renderTo(u,f,!0)):this.rendered=!0,e.mergeNavbar&&s?p=b(".sidebar"):(c.classList.add("app-nav"),e.repo||c.classList.add("no-badge")),e.loadNavbar&&y(p,c),e.themeColor&&(v.head.appendChild(w("div","").firstElementChild),a=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=k("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?Q(e,a):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&X(e).then(function(e){e=w("style",e);_.appendChild(e),Q(e,a)}))}))),this._updateRender(),S(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var a=this.routes(),r=Object.keys(a),c=function(){return null};function u(){var e=r.shift();if(!e)return c(null);var n=A(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return u();var o=a[e];if("string"==typeof o)return c(o);if("function"!=typeof o)return u();n=o,e=Xn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?c(e):!1===e?c(null):u()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){c=e,u()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?D:H)(e);this.router=e,this.updateRender(),U=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),U.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),U=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,a){void 0===a&&(a=d);var r=this._hooks[e],c=this.config.catchPluginErrors,u=function(n){var e=r[n];if(n>=r.length)a(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,u(n+1)})}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}else try{var o=e(t);t=void 0===o?t:o,u(n+1)}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}}else u(n+1)};u(0)},n}(we))))))));function Kn(e,n,i){return Qn&&Qn.abort&&Qn.abort(),Qn=X(e,!0,i)}window.Docsify={util:Me,dom:n,get:X,slugify:Tn,version:"4.13.1"},window.DocsifyCompiler=In,window.marked=Sn,window.Prism=Pn,e(function(e){return new Jn})}(); diff --git a/docs/v3.4.0/_cdns/fonts.css b/docs/v3.4.0/_cdns/fonts.css deleted file mode 100644 index 02482a233..000000000 --- a/docs/v3.4.0/_cdns/fonts.css +++ /dev/null @@ -1,24 +0,0 @@ -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - src: url(./fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 300; - src: url(./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 400; - src: url(./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format('truetype'); -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 600; - src: url(./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf) format('truetype'); -} diff --git a/docs/v3.4.0/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf b/docs/v3.4.0/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf deleted file mode 100644 index 82ca44afe..000000000 Binary files a/docs/v3.4.0/_cdns/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf and /dev/null differ diff --git a/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf b/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf deleted file mode 100644 index f16ada236..000000000 Binary files a/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf and /dev/null differ diff --git a/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf b/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf deleted file mode 100644 index 61acbb710..000000000 Binary files a/docs/v3.4.0/_cdns/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf and /dev/null differ diff --git a/docs/v3.4.0/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf b/docs/v3.4.0/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf deleted file mode 100644 index 133153f69..000000000 Binary files a/docs/v3.4.0/_cdns/fonts/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf and /dev/null differ diff --git a/docs/v3.4.0/_cdns/search.min.js b/docs/v3.4.0/_cdns/search.min.js deleted file mode 100644 index 63b5bc7e4..000000000 --- a/docs/v3.4.0/_cdns/search.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=e.text||""):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n

    '+e.title+"

    \n

    "+e.content+"

    \n
    \n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||'

    '+c+"

    ",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-1nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}img.emoji{height:1.2em}img.emoji,span.emoji{vertical-align:middle}span.emoji{font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1.2em}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{position:relative;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100vh;width:100%;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;bottom:0;width:100%}section.cover .cover-main{flex:1;margin:0 16px;text-align:center;position:relative}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid #42b983;border-color:var(--theme-color,#42b983);box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0;content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto} \ No newline at end of file diff --git a/docs/v3.4.0/_coverpage.md b/docs/v3.4.0/_coverpage.md deleted file mode 100644 index 9984e4dcf..000000000 --- a/docs/v3.4.0/_coverpage.md +++ /dev/null @@ -1,8 +0,0 @@ -# Molgenis Armadillo 3 - -![logo](img/armadillo-logo.png) - -> Armadillo is a data portal that allows data stewards to share datasets on a server, and researchers to analyse these datasets using the DataSHIELD analysis tools. - -[GitHub](https://github.com/molgenis/molgenis-service-armadillo) -[Armadillo suite](/README.md#armadillo-suite) diff --git a/docs/v3.4.0/_navbar.md b/docs/v3.4.0/_navbar.md deleted file mode 100644 index 09fbae6ac..000000000 --- a/docs/v3.4.0/_navbar.md +++ /dev/null @@ -1,19 +0,0 @@ -* Menu - - * [Armadillo suite](/) - - * [Armadillo 3 UI](/ui.md#armadillo-user-interface "Armadillo 3 UI") - - * [DsMolgenisArmadillo](https://github.com/molgenis/molgenis-r-datashield/blob/master/README.md) - - * [MolgenisArmadillo](https://molgenis.github.io/molgenis-r-armadillo/) - - * [molgenis-r-auth](https://molgenis.github.io/molgenis-r-auth/) - - * [molgenis-r-client](https://github.com/molgenis/molgenis-r-client/blob/master/README.md) - - * [ds-upload](https://lifecycle-project.github.io/ds-upload/) - - * [ds-dictionaries](https://github.com/lifecycle-project/ds-dictionaries/blob/master/README.md) - - * [Armadillo 2 to 3 migration guide](/migration-guide-2-to-3.md#instructions-for-migrating-a-2x-armadillo-to-3x "Armadillo 2 to 3 migration guide") \ No newline at end of file diff --git a/docs/v3.4.0/_sidebar.md b/docs/v3.4.0/_sidebar.md deleted file mode 100644 index 0a5ab0aba..000000000 --- a/docs/v3.4.0/_sidebar.md +++ /dev/null @@ -1,17 +0,0 @@ -- [Armadillo suite](/ "Armadillo suite") - -- [Armadillo 3 UI](/ui.md#armadillo-user-interface "Armadillo 3 UI") - -- [DsMolgenisArmadillo](https://github.com/molgenis/molgenis-r-datashield/blob/master/README.md) - -- [MolgenisArmadillo](https://molgenis.github.io/molgenis-r-armadillo/) - -- [molgenis-r-auth](https://molgenis.github.io/molgenis-r-auth/) - -- [molgenis-r-client](https://github.com/molgenis/molgenis-r-client/blob/master/README.md) - -- [ds-upload](https://lifecycle-project.github.io/ds-upload/) - -- [ds-dictionaries](https://github.com/lifecycle-project/ds-dictionaries/blob/master/README.md) - -- [Armadillo 2 to 3 migration guide](/migration-guide-2-to-3.md#instructions-for-migrating-a-2x-armadillo-to-3x "Armadillo 2 to 3 migration guide") \ No newline at end of file diff --git a/docs/v3.4.0/adr/0001-use-adr-to-describe-architecture-decisions.md b/docs/v3.4.0/adr/0001-use-adr-to-describe-architecture-decisions.md deleted file mode 100644 index 25c12b516..000000000 --- a/docs/v3.4.0/adr/0001-use-adr-to-describe-architecture-decisions.md +++ /dev/null @@ -1,19 +0,0 @@ -# 1. Record architecture decisions - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need to record the architectural decisions made on this project. - -## Decision - -We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions. - -## Consequences - -See Michael Nygard's article, linked above. \ No newline at end of file diff --git a/docs/v3.4.0/adr/0002-implement-authentication-using-openid.md b/docs/v3.4.0/adr/0002-implement-authentication-using-openid.md deleted file mode 100644 index 4b8c6c330..000000000 --- a/docs/v3.4.0/adr/0002-implement-authentication-using-openid.md +++ /dev/null @@ -1,27 +0,0 @@ -# 2. Implement authentication using openid - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need a way to help users authentication in our system - -## Decision - -We implement openid in our application so we use ID-providers to authenticate in our services. -We intent to use JWTs which are machine-readable bearer tokens, to integrate external services. - -## Consequences - -- Use an ID-provider to authenticate in the DataSHIELD service. -- We intend to use the role mechanism in the ID-provider to authorise. The roles that we can think of at this moment are: - - Local data manager - - Researcher - - Administrator - This list can be updated and / or amended over time. -- By using an ID-provider we intend to be able to attach other systems as well. -- By using an ID-provider we intend to be able to federate to other ID-providers. \ No newline at end of file diff --git a/docs/v3.4.0/adr/0003-implement-method-security-using-datashield4j.md b/docs/v3.4.0/adr/0003-implement-method-security-using-datashield4j.md deleted file mode 100644 index f14d11a97..000000000 --- a/docs/v3.4.0/adr/0003-implement-method-security-using-datashield4j.md +++ /dev/null @@ -1,21 +0,0 @@ -# 3. Implement method security using datashield4j - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -We need some way of being sure that there is no malicious script execution on the DataSHIELD environment. - -## Decision - -We implement datashield4j to prevent users to execute method and parameters other than the DataSHIELD provided methods. -We looked at OCAP ([R native object capabilities](https://docs.google.com/document/d/1Yx10Xw8Uige3hK-6YwzM8RhrtqSogDTGAZlcF218U2U/edit)) as well but came to the conclusion -that the client that OCAP is using needs to be developed in JAVA. The datashield4j library is already available and tested which makes it easier to implement. - -## Consequences - -- Users can not execute malicious scripts on the R environment anymore. diff --git a/docs/v3.4.0/adr/0004-implement-asynchronicity-in-requestflow.md b/docs/v3.4.0/adr/0004-implement-asynchronicity-in-requestflow.md deleted file mode 100644 index 46f93a44f..000000000 --- a/docs/v3.4.0/adr/0004-implement-asynchronicity-in-requestflow.md +++ /dev/null @@ -1,20 +0,0 @@ -# 4. Implement asynchronicity in the requestflow - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -Clients can have operations running on multiple DataSHIELD servers concurrently. - -## Decision - -We need to support asynchronous requests in the R client. We implemented it using completable futures. -We must keep the last execution result for each R session until it gets retrieved or until a new execution is started. - -## Consequences - -- Users can have more than one session on the same server. diff --git a/docs/v3.4.0/adr/0005-deploy-on-both-vm-and-kubernetes.md b/docs/v3.4.0/adr/0005-deploy-on-both-vm-and-kubernetes.md deleted file mode 100644 index 0a1dc6074..000000000 --- a/docs/v3.4.0/adr/0005-deploy-on-both-vm-and-kubernetes.md +++ /dev/null @@ -1,21 +0,0 @@ -# 5. Deploy both on a virtual machine and Kubernetes - -Date: 2020-04-01 - -## Status - -Accepted - -## Context - -Because of the diversity in landscape we need to be able to deploy on different environments. - -## Decision - -We are going to create deployments for virtual machines based on CentOS (>=8) using Ansible and Vagrant. -Besides that we are going to create a chart which allows you to deploy on Kubernetes. - -## Consequences - -- Some of the VM environments are not serviced (Ubuntu, Suse, other operating systems). -- Some other container orchestrators are not implemented e.g. docker-swarm. diff --git a/docs/v3.4.0/adr/0006-use-rdata-format-as-data-input.md b/docs/v3.4.0/adr/0006-use-rdata-format-as-data-input.md deleted file mode 100644 index db37508cd..000000000 --- a/docs/v3.4.0/adr/0006-use-rdata-format-as-data-input.md +++ /dev/null @@ -1,23 +0,0 @@ -# 6. Use RData file format as data input for the service - -Date: 2020-04-23 - -## Status - -Accepted - -## Context - -We want to make the MOLGENIS "Armadillo" service data provider agnostic. There are a couple of reasons why we are doing this -- the service is usable for other parties as well -- the service can still integrate with MOLGENIS. -- the release cycle of the service is data provider independent -- the service can be developed by other parties as well - -## Decision - -We implement an endpoint to upload and load RData files in the MOLGENIS "Armadillo" service to manage data for the use in DataSHIELD. - -## Consequences -- We need a client which is capable to deal with uploading RData files into the MOLGENIS "Armadillo" service -- MOLGENIS needs to have an RData download option and/or have an EMX to RData-converter to be able to communicate directly with the MOLGENIS "Armadillo" service diff --git a/docs/v3.4.0/adr/0007-use-roles-to-authorise-users-to-use-data.md b/docs/v3.4.0/adr/0007-use-roles-to-authorise-users-to-use-data.md deleted file mode 100644 index 31775ff51..000000000 --- a/docs/v3.4.0/adr/0007-use-roles-to-authorise-users-to-use-data.md +++ /dev/null @@ -1,39 +0,0 @@ -# 7. Use roles to authorise users to use certain data - -Date: 2020-04-23 - -## Status - -Accepted - -## Context - -We need a way to authorise users in the MOLGENIS "Armadillo" service to use data. We want this to be as straightforward as possible. - -Other techniques are ACL's for fine grained permissions on resources and ID-based. - -**ACL's** -It is more time consuming to implement ACL's. With roles the you are less flexible, but we believe this is sufficient to use DataSHIELD. - -**ID-based** -The ID-based approach was suggested as a first solution to make the service secure. A big disadvantage is that anyone with the link can -share the data. That means that there has to be a lot of trust between the data manager and the researcher. In practices, this is not feasible. - -## Decision - -We will use roles as a basic principle to give users permission on the data they need to have access to. -Within the MOLGENIS "Armadillo" service there are at least these 2 types of roles: -- Researcher - *Implicit permissions* - - READ --> on data in the shared folder(s) to which they have access - - ADMIN --> on data in their own (user)folder -- Data manager - *Implicit permissions* - - ADMIN --> on all that lives -Each researcher role will have corresponding folder. The data manager is able to do anything anywhere. - -## Consequences -- In the file storage you need to be aware of the fact that you are using roles as an authorisation mechanism -- It is not possible to have fine grained permissions within a role - - diff --git a/docs/v3.4.0/adr/0008-load-multiple-rdata-files.md b/docs/v3.4.0/adr/0008-load-multiple-rdata-files.md deleted file mode 100644 index 26603b9ec..000000000 --- a/docs/v3.4.0/adr/0008-load-multiple-rdata-files.md +++ /dev/null @@ -1,18 +0,0 @@ -# 8. As a researcher you are able to load multiple RData files - -Date: 2020-04-23 - -## Status - -Accepted - -## Context -We want to give researchers as much freedom as possible when they're selecting and loading data. - -## Decision - -The `/load-tables` endpoint supports loading multiple .RData files at once. These files can be in different folders. - -## Consequences -- Users with multiple roles can load data from multiple folders into their workspace. This will make - sharing workspaces between users difficult (because they may not have the same roles). \ No newline at end of file diff --git a/docs/v3.4.0/adr/0009-use-bean-scope-for-profiles.md b/docs/v3.4.0/adr/0009-use-bean-scope-for-profiles.md deleted file mode 100644 index 363b994d3..000000000 --- a/docs/v3.4.0/adr/0009-use-bean-scope-for-profiles.md +++ /dev/null @@ -1,25 +0,0 @@ -# 9. Use Spring bean scope to distinguish between profiles - -Date: 2020-10-05 - -## Status - -Accepted - -## Context -We need to be able to switch between profiles. -But many singleton beans have profile information in them. - -## Decision - -Create a custom `@ProfileScope` to store the different versions of the profiles and inject proxies -to look up the correct instance. - -We already did this for the ArmadilloSession, a `@SessionScope` bean, to store the connection to -the R server which is different for each user session. - -## Consequences -- Profiles remain an aspect, are not interwoven with the logic -- Easier to test -- Once it works, it works -- The mechanism is harder to understand and debug diff --git a/docs/v3.4.0/developer_notes.md b/docs/v3.4.0/developer_notes.md deleted file mode 100644 index e2ab123cd..000000000 --- a/docs/v3.4.0/developer_notes.md +++ /dev/null @@ -1,19 +0,0 @@ -# To develop the docs - -First time, install docsify - -``` -npm i docsify-cli -g -``` - -Then you can serve the dev in this directory using - -``` -docsify serve ./docs -``` - -And view the result on - -``` -http://localhost:3000 -``` \ No newline at end of file diff --git a/docs/v3.4.0/img/armadillo-logo.png b/docs/v3.4.0/img/armadillo-logo.png deleted file mode 100644 index 9caf819e3..000000000 Binary files a/docs/v3.4.0/img/armadillo-logo.png and /dev/null differ diff --git a/docs/v3.4.0/img/overview-datashield.png b/docs/v3.4.0/img/overview-datashield.png deleted file mode 100644 index 9342fc0be..000000000 Binary files a/docs/v3.4.0/img/overview-datashield.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-folder-button.png b/docs/v3.4.0/img/ui/add-folder-button.png deleted file mode 100644 index 882522b26..000000000 Binary files a/docs/v3.4.0/img/ui/add-folder-button.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-folder.png b/docs/v3.4.0/img/ui/add-folder.png deleted file mode 100644 index 49ee00a60..000000000 Binary files a/docs/v3.4.0/img/ui/add-folder.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-profile.png b/docs/v3.4.0/img/ui/add-profile.png deleted file mode 100644 index 1384d8f09..000000000 Binary files a/docs/v3.4.0/img/ui/add-profile.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-project-to-user.png b/docs/v3.4.0/img/ui/add-project-to-user.png deleted file mode 100644 index ad46bf7a0..000000000 Binary files a/docs/v3.4.0/img/ui/add-project-to-user.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-project.png b/docs/v3.4.0/img/ui/add-project.png deleted file mode 100644 index 468af95b7..000000000 Binary files a/docs/v3.4.0/img/ui/add-project.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-user-warning.png b/docs/v3.4.0/img/ui/add-user-warning.png deleted file mode 100644 index 1f0444d77..000000000 Binary files a/docs/v3.4.0/img/ui/add-user-warning.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add-user.png b/docs/v3.4.0/img/ui/add-user.png deleted file mode 100644 index efb5ada3d..000000000 Binary files a/docs/v3.4.0/img/ui/add-user.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/add.png b/docs/v3.4.0/img/ui/add.png deleted file mode 100644 index ea1d75a92..000000000 Binary files a/docs/v3.4.0/img/ui/add.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/admin.png b/docs/v3.4.0/img/ui/admin.png deleted file mode 100644 index 313272168..000000000 Binary files a/docs/v3.4.0/img/ui/admin.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/back-button.png b/docs/v3.4.0/img/ui/back-button.png deleted file mode 100644 index f1dbda9b1..000000000 Binary files a/docs/v3.4.0/img/ui/back-button.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/cancel.png b/docs/v3.4.0/img/ui/cancel.png deleted file mode 100644 index 85a51480d..000000000 Binary files a/docs/v3.4.0/img/ui/cancel.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/check.png b/docs/v3.4.0/img/ui/check.png deleted file mode 100644 index a213026c2..000000000 Binary files a/docs/v3.4.0/img/ui/check.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/delete.png b/docs/v3.4.0/img/ui/delete.png deleted file mode 100644 index 1e89f5de3..000000000 Binary files a/docs/v3.4.0/img/ui/delete.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/edit-project.png b/docs/v3.4.0/img/ui/edit-project.png deleted file mode 100644 index a0faa450f..000000000 Binary files a/docs/v3.4.0/img/ui/edit-project.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/edit-projects-add-user.png b/docs/v3.4.0/img/ui/edit-projects-add-user.png deleted file mode 100644 index eea7160f8..000000000 Binary files a/docs/v3.4.0/img/ui/edit-projects-add-user.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/edit-user.png b/docs/v3.4.0/img/ui/edit-user.png deleted file mode 100644 index 44d701f5a..000000000 Binary files a/docs/v3.4.0/img/ui/edit-user.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/edit.png b/docs/v3.4.0/img/ui/edit.png deleted file mode 100644 index d60e96d04..000000000 Binary files a/docs/v3.4.0/img/ui/edit.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/loading-stop-profile.png b/docs/v3.4.0/img/ui/loading-stop-profile.png deleted file mode 100644 index d3e69e6aa..000000000 Binary files a/docs/v3.4.0/img/ui/loading-stop-profile.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/login.png b/docs/v3.4.0/img/ui/login.png deleted file mode 100644 index 6863bd3cc..000000000 Binary files a/docs/v3.4.0/img/ui/login.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/no-superuser.png b/docs/v3.4.0/img/ui/no-superuser.png deleted file mode 100644 index 2f67df15f..000000000 Binary files a/docs/v3.4.0/img/ui/no-superuser.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/plus.png b/docs/v3.4.0/img/ui/plus.png deleted file mode 100644 index e067fe099..000000000 Binary files a/docs/v3.4.0/img/ui/plus.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/preview-file.png b/docs/v3.4.0/img/ui/preview-file.png deleted file mode 100644 index 898b1d3b3..000000000 Binary files a/docs/v3.4.0/img/ui/preview-file.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/profiles.png b/docs/v3.4.0/img/ui/profiles.png deleted file mode 100644 index 1d54d7c02..000000000 Binary files a/docs/v3.4.0/img/ui/profiles.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/project-editor.png b/docs/v3.4.0/img/ui/project-editor.png deleted file mode 100644 index 7c4f732c8..000000000 Binary files a/docs/v3.4.0/img/ui/project-editor.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/projects.png b/docs/v3.4.0/img/ui/projects.png deleted file mode 100644 index ce52b3f9e..000000000 Binary files a/docs/v3.4.0/img/ui/projects.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/start-profile.png b/docs/v3.4.0/img/ui/start-profile.png deleted file mode 100644 index c12b26e79..000000000 Binary files a/docs/v3.4.0/img/ui/start-profile.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/stop-profile.png b/docs/v3.4.0/img/ui/stop-profile.png deleted file mode 100644 index e92de4d16..000000000 Binary files a/docs/v3.4.0/img/ui/stop-profile.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/upload-a-file.png b/docs/v3.4.0/img/ui/upload-a-file.png deleted file mode 100644 index 8aa77b0f2..000000000 Binary files a/docs/v3.4.0/img/ui/upload-a-file.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/upload-file.png b/docs/v3.4.0/img/ui/upload-file.png deleted file mode 100644 index c168a46a9..000000000 Binary files a/docs/v3.4.0/img/ui/upload-file.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/uploaded-file.png b/docs/v3.4.0/img/ui/uploaded-file.png deleted file mode 100644 index 014630aeb..000000000 Binary files a/docs/v3.4.0/img/ui/uploaded-file.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/view-project-folder.png b/docs/v3.4.0/img/ui/view-project-folder.png deleted file mode 100644 index 826c4de77..000000000 Binary files a/docs/v3.4.0/img/ui/view-project-folder.png and /dev/null differ diff --git a/docs/v3.4.0/img/ui/view-project.png b/docs/v3.4.0/img/ui/view-project.png deleted file mode 100644 index 7da110084..000000000 Binary files a/docs/v3.4.0/img/ui/view-project.png and /dev/null differ diff --git a/docs/v3.4.0/index.html b/docs/v3.4.0/index.html deleted file mode 100644 index 47478e291..000000000 --- a/docs/v3.4.0/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Armadillo suite documentation - - - - - - -
    - - - - - - - - diff --git a/docs/v3.4.0/migration-guide-2-to-3.md b/docs/v3.4.0/migration-guide-2-to-3.md deleted file mode 100644 index 5dcda5386..000000000 --- a/docs/v3.4.0/migration-guide-2-to-3.md +++ /dev/null @@ -1,137 +0,0 @@ -# Instructions for migrating a 2.x Armadillo to 3.x -The release of Armadillo 3.0 introduces some breaking changes. These instructions will guide you -through the steps needed to migrate your old Armadillo service to the new version. - -## Configuration -This section is about changes in the `application.yml` configuration file. For a full -example you can look at [the application.yml in the code](/armadillo/src/main/resources/application.yml). - -### OIDC -Besides accepting JWTs from a trusted authentication provider, you can now enable OIDC authentication. -This will make it possible for admins to log in with their institute account in the UI. - -To enable this, add the following properties: - -``` -spring: - security: - oauth2: - client: - registration: - molgenis: - client-id: - client-secret: -``` - -### Armadillo Settings -The `datashield` property has been renamed to `armadillo`, and some new settings have been -introduced: - -``` -armadillo: - oidc-permission-enabled: false - oidc-admin-user: user@yourinstitute.org - docker-management-enabled: true -``` - -These settings are explained in more detail below. - -#### OIDC Permission Enabled -By default, roles and permissions are managed in Armadillo itself. However, you can still accept -roles from your authentication provider by setting `oidc-permission-enabled` to `true`. This is -not recommended. - -#### OIDC Admin User -You can configure a default OIDC admin user by setting the `oidc-admin-user` property. Armadillo -will add this user when the application starts. This admin will then immediately be able to login -with their institute account. - -#### Docker Management Enabled -Armadillo can manage the Docker containers used for profiles. To enable, set `docker-management-enabled` -to `true`. Keep in mind that Armadillo needs to be able to access a local Docker instance. - -### Profiles -Profiles can now be created and managed at runtime in the UI or via the profiles API. However, -it is still possible to configure one or more profiles, which will be created for you when -the application starts. - -Before 3.0, profiles and R environments had to be defined separately: - -``` -rserve: - environments: - - name: default - host: localhost - port: 6311 - -datashield: - profiles: - - name: default - environment: default - whitelist: - - dsBase - options: - datashield: - seed: 342325352 -``` - -This is no longer the case. You now have to configure everything inside a profile: - -``` -armadillo: - profiles: - - name: default - image: datashield/armadillo-rserver - host: localhost - port: 6311 - whitelist: - - dsBase - options: - datashield: - seed: 342325352 -``` - -Note that the root `datashield` property has been renamed to `armadillo`. - -Also note the `image` property: this one is optional and only necessary when you have enabled -[Docker Management](#docker-management). The image should be an image available on DockerHub. - -### Storage -You can now choose where Armadillo should store data: on a MinIO server (like before) or on the local file system. - -If you want to keep using MinIO, you don't need to change anything. The old configuration will still work: - -``` -minio: - url: http://localhost - port: 9000 - access-key: - secret-key: -``` - -To use the local file system, set the following parameter: - -``` -storage: - root-dir: -``` - -MinIO has precedence over local file storage, so keep that in mind when you have both configured. - -## Migrating users and data -Since users and permissions are now managed in Armadillo instead of the authentication server, this -information needs to be migrated. And if you choose to host the data on the local file system -instead of a MinIO server, the data needs to be migrated as well. For both scenarios we have migration -scripts. - -### User migration script: Fusion Auth to Armadillo -To migrate users from Fusion Auth to Armadillo, you can use the script found [here](/scripts/migrate-auth.py). -More information on how to run it can be found in the script or by calling it with the `-h` flag. Make -sure Armadillo is NOT running when you run the script. After the script is done you need to start -Armadillo and it will automatically create the projects based on the folders that were copied. - -### Data migration script: MinIO to local file system -To migrate data from MinIO to the local file system, you can use the script found [here](/scripts/migrate-minio.py). -More information on how to run it can be found in the script or by calling it with the `-h` flag. Make -sure Armadillo is running when you run the script. - diff --git a/docs/v3.4.0/release-test.md b/docs/v3.4.0/release-test.md deleted file mode 100644 index f33afc601..000000000 --- a/docs/v3.4.0/release-test.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release testing -## Prerequisites -- Testserver with release candidate available -- OIDC user with admin permissions on testserver -- Following libraries installed in R: - - cli - - getPass - - arrow - - httr - - jsonlite - - future - - MolgenisArmadillo (2.0.0 >) - - DSI - - dsBaseClient - - DSMolgenisArmadillo (2.0.0 >) - - resourcer (1.4.0 >) - -*For full testing* -- Admin password by hand - -*Minimal* -- OIDC user with admin/superuser permissions on testserver -- Someone with admin permissions available to take and regrant admin permissions to OIDC (super)user - -With these minimal prerequisites met, tests for basic auth will be skipped and permissions will have to be set by hand -when asked by script. - -OR -- A basic admin password - -With these minimal prerequisites, the script will then skip the resources testing (is not possible with basic auth) and -testing as regular user - -## Running the tests -1. Open your commandline -2. `cd` to the `scripts/release` folder of this repository -3. To run tests, type: `Rscript release-test.R` -4. Follow the directions in the script - -If the script fails somewhere during the process, make sure you give your OIDC account back their admin permissions and -throw away the projects: cohort1, cohort2 and omics. diff --git a/docs/v3.4.0/scripts/armadillo-setup.sh b/docs/v3.4.0/scripts/armadillo-setup.sh deleted file mode 100644 index f7cf3ed35..000000000 --- a/docs/v3.4.0/scripts/armadillo-setup.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/bin/bash - -ARMADILLO_SETUP_VER=1.0.1 -ARMADILLO_URL=https://github.com/molgenis/molgenis-service-armadillo/ -ARMADILLO_PROFILE=default -ARMADILLO_PATH=/usr/share/armadillo -ARMADILLO_CFG_PATH=/etc/armadillo -ARMADILLO_SYS_USER=armadillo -ARMADILLO_LOG_PATH=/var/log/armadillo -ARMADILLO_AUDITLOG=$ARMADILLO_LOG_PATH/audit.log -ARMADILLO_DATADIR=$ARMADILLO_PATH/data - - -handle_args() { - while : - do - case "$1" in - --version) - ARMADILLO_VERSION=$2 - shift 2 - ;; - --admin-user) - ARMADILLO_ADMIN=$2 - shift 2 - ;; - --admin-password) - ARMADILLO_ADMIN_PW=$2 - shift 2 - ;; - --domain) - ARMADILLO_DOMAIN=$2 - shift 2 - ;; - --datadir) - ARMADILLO_DATADIR=$2 - shift 2 - ;; - --oidc) - ARMADILLO_OIDC_ENABLED=1 - shift - ;; - --oidc_url) - OIDC_ISSUER_URL=$2 - shift 2 - ;; - --oidc_clientid) - OIDC_CLIENTID=$2 - shift 2 - ;; - --oidc_clientsecret) - OIDC_CLIENTSECRET=$2 - shift 2 - ;; - --admin-email) - ARMADILLO_OIDC_ADMIN_EMAIL=$2 - shift 2 - ;; - --cleanup) - ARMADILLO_CLEANUP=1 - shift ;; - - -h | --help) - parameters_help - exit 0 - ;; - -*) - parameters_help - exit 0 - ;; - *) - break; - esac - - done - if [ ! "$ARMADILLO_CLEANUP" ]; then - if [ ! "$ARMADILLO_DOMAIN" ] || [ ! "$ARMADILLO_ADMIN_PW" ]; then - echo "Arguments --domain --admin-password must be provided" - echo "You need a host or domain to use Armadillo. Example: cohort.armadillo.organisation.com" - echo "Also for security reasons you must provide a secure admin password" - parameters_help; - exit 1; - fi - fi - if [ "$ARMADILLO_OIDC_ENABLED" ]; then - if [ ! "$OIDC_CLIENTID" ] || [ ! "$OIDC_ISSUER_URL" ] || [ ! "$OIDC_CLIENTSECRET" ]; then - echo "OIDC Option called but mandatory config items are missing --admin-email user@oidc-mailadres.tld --oidc_url --oidc_clientid --oidc_clientsecret " - exit 1; - fi - fi - - -} - - -setup_environment() { - mkdir -p $ARMADILLO_PATH/application - mkdir -p $ARMADILLO_PATH/services - mkdir -p "$ARMADILLO_LOG_PATH" - mkdir -p "$ARMADILLO_CFG_PATH" - mkdir -p "$ARMADILLO_DATADIR" - useradd -rs /bin/false "$ARMADILLO_SYS_USER" - chgrp -R "$ARMADILLO_SYS_USER" "$ARMADILLO_PATH" - chgrp -R "$ARMADILLO_SYS_USER" "$ARMADILLO_CFG_PATH" - chgrp -R "$ARMADILLO_SYS_USER" "$ARMADILLO_LOG_PATH" - chgrp -R "$ARMADILLO_SYS_USER" "$ARMADILLO_DATADIR" - - chmod g+rw "$ARMADILLO_LOG_PATH" - chmod g+rw "$ARMADILLO_DATADIR" - usermod -aG docker "$ARMADILLO_SYS_USER" - echo "Environment is being set up correctly" -} - -setup_systemd() { - cat > /etc/systemd/system/armadillo.service << EOF -[Unit] -Description=DataSHIELD Armadillo 3 -After=syslog.target - -[Service] -User=$ARMADILLO_SYS_USER -Environment=SPRING_PROFILES_ACTIVE=$ARMADILLO_PROFILE -Environment=SPRING_CONFIG_LOCATION=$ARMADILLO_CFG_PATH/application.yml -WorkingDirectory=$ARMADILLO_PATH -ExecStart=java -jar $ARMADILLO_PATH/application/armadillo.jar -StandardOutput=append:$ARMADILLO_LOG_PATH/armadillo.log -StandardError=append:$ARMADILLO_LOG_PATH/error.log -Type=simple -Restart=on-failure -RestartSec=10 - -[Install] -WantedBy=multi-user.target - -EOF - -systemctl daemon-reload -echo "Armadillo Installed under systemd" -} - -setup_armadillo_config() { - SEED=$(tr -cd '[:digit:]' < /dev/urandom | fold -w 9 | head -n 1) - wget -q -O /etc/armadillo/application.yml https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/v3.4.0/scripts/install/conf/application.yml - - - if [ ! "$ADMINUSER" ]; then - ADMINUSER="admin" - else - ADMINUSER=$ARMADILLO_ADMIN - fi - - sed -i -e 's|@LOGPATH@|'"$ARMADILLO_LOG_PATH"'|' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@ADMINUSER@/'"$ADMINUSER"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@ADMINPASS@/'"${ARMADILLO_ADMIN_PW}"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's|@DATADIR@|'"$ARMADILLO_DATADIR"'|' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@SEED@/'"$SEED"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's|@AUDITLOG@|'"$ARMADILLO_AUDITLOG"'|' $ARMADILLO_CFG_PATH/application.yml - - - if [ "$ARMADILLO_OIDC_ENABLED" ]; then - - sed -i -e 's|@ISSUERURL@|'"$OIDC_ISSUER_URL"'|g' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@CLIENTID@/'"$OIDC_CLIENTID"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@CLIENTSECRET@/'"$OIDC_CLIENTSECRET"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's/@ARMADILLODOMAIN@/'"$ARMADILLO_DOMAIN"'/' $ARMADILLO_CFG_PATH/application.yml - sed -i -e 's|# oidc-admin-user: @ADMIN_EMAIL@|oidc-admin-user: '"$ARMADILLO_OIDC_ADMIN_EMAIL"'|' $ARMADILLO_CFG_PATH/application.yml - fi - - - - echo "Config downloaded" - -} - -download_armadillo() { - - if [ -z "$ARMADILLO_VERSION" ]; then - LATEST_RELEASE=$(curl -L -s -H 'Accept: application/json' -s $ARMADILLO_URL/releases/latest) - ARMADILLO_TAG=$(echo "$LATEST_RELEASE" | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') - ARMADILLO_VERSION=$(echo $ARMADILLO_TAG | sed -e 's/.*v//') - - - - if [[ "$ARMADILLO_VERSION" =~ 'armadillo-service-2' ]]; then - echo "Armadillo version 2 not supported! Please use provide an armadillo 3 version with --version" - exit 1; - fi - fi - - DL_URL=https://github.com/molgenis/molgenis-service-armadillo/releases/download/v$ARMADILLO_VERSION/molgenis-armadillo-$ARMADILLO_VERSION.jar - - - if validate_url $DL_URL; then - - wget -q -O $ARMADILLO_PATH/application/armadillo-"$ARMADILLO_VERSION".jar "$DL_URL" - ln -s $ARMADILLO_PATH/application/armadillo-"$ARMADILLO_VERSION".jar $ARMADILLO_PATH/application/armadillo.jar - echo "$ARMADILLO_VERSION downloaded" - - else - echo "[ERROR] Error in downloading armadillo, please contact molgenis-support@umcg.nl with your error." - exit 1; - fi -} - - -check_req() { - for COMMAND in "java" "wget" "docker" "curl" "whoami"; do - command_exists "${COMMAND}" - done - - if [ "$(whoami)" != 'root' ]; then - echo '[ERROR] Please run this script with root or sudo rights!' - exit 1; - fi - -} - -setup_updatescript() { - # Download update script - DL_URL=https://raw.githubusercontent.com/molgenis/molgenis-service-armadillo/v3.4.0/scripts/install/armadillo-check-update.sh - - - if validate_url $DL_URL; then - - wget -q -O $ARMADILLO_PATH/application/armadillo-update.sh "$DL_URL" - echo "Update script downloaded" - chmod +x $ARMADILLO_PATH/application/armadillo-update.sh - ln -s /usr/share/armadillo/application/check-update.sh /etc/cron.weekly/check-armadillo-update - - else - echo "[ ERROR ] update script not downloaded" - fi - - -} - - -command_exists() { - # check if command exists and fail otherwise - command -v "$1" >/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo "I require $1 but it's not installed. Abort." - exit 1 - fi -} - - -cleanup(){ - if [ "$ARMADILLO_CLEANUP" ]; then - echo "--cleanup variable defined! Are you sure? Potential data loss! Type y/N" - read -r -p "Type y/N" response - if [ "$response" = "y" ] - then - systemctl stop armadillo - systemctl disable armadillo - rm -Rf $ARMADILLO_PATH - rm -Rf $ARMADILLO_LOG_PATH - userdel $ARMADILLO_SYS_USER - rm -Rf /etc/systemd/system/armadillo.service - systemctl daemon-reload - rm -Rf $ARMADILLO_LOG_PATH - rm -Rf /etc/cron.weekly/check-armadillo-update - echo "Armadillo cleaned!" - else - echo "No cleanup .. please remove the --cleanup argument" - exit 0 - fi - fi -} - -startup_armadillo() { - systemctl enable armadillo - systemctl start armadillo - echo "Armadillo started" - -} - -validate_url(){ - if [[ `wget -S --spider $1 2>&1 | grep 'HTTP/1.1 200 OK'` ]]; then - return 0 - else - return 1 - fi -} - -#Parameters passed in help -parameters_help() { - - echo 'Usage: bash armadillo-setup.sh PARAMS - example bash armadillo-setup.sh --admin-user admin --admin-password welcome01 --domain armadillo.cohort.study.com' - echo - echo 'Install Script for Armadillo Service' - echo - echo 'Params:' - echo - echo ' --version armadillo_version Specify witch version to install' - echo ' --admin-user user Specify the Basic-Auth admin user' - echo ' --admin-password pass Password for the admin user' - echo ' --datadir /storage/dir If defined this would be the Location to store the data otherwise, it would be /usr/share/armadillo/data' - echo ' --domain URL domain which is used for accessing armadillo' - echo '' - echo ' --oidc For central authentication you can enable oidc' - echo ' --oidc_url URL where the oidc server is listening on' - echo ' --oidc_clientid Client id of the oidc config' - echo ' --oidc_clientsecret Secret of the client' - echo ' --admin-email Email adres of the oidc Admin User' - - - -} - - - - -if [ "$#" -eq 0 ]; then - echo 'No parameters provided, please provide the correct parameters' - parameters_help - exit 0 -fi - - -handle_args "$@" -check_req -cleanup -setup_environment -download_armadillo -setup_updatescript -setup_armadillo_config -setup_systemd -startup_armadillo - diff --git a/docs/v3.4.0/ui.md b/docs/v3.4.0/ui.md deleted file mode 100644 index 78169d295..000000000 --- a/docs/v3.4.0/ui.md +++ /dev/null @@ -1,229 +0,0 @@ -# Armadillo User Interface - -Since Armadillo version 3, a lot has changed compared to its previous version. One of these changes is the addition of -a user interface, or UI for short. This UI will be replacing the MinIO file storage and the permission management page, -as well as adding several new features that will be extended upon. - -## Table of contents - -1. [Login](#login) - 1. [Superuser](#superuser) -2. [Projects](#projects) - 1. [Editing projects](#edit-projects) - 2. [Adding projects](#add-projects) -3. [Project explorer](#project-explorer) - 1. [Resources](#resources) -4. [Users](#users) - 1. [Editing users](#edit-users) - 2. [Adding users](#add-users) -5. [Profiles](#profiles) - -## Login - -![Login screen of Armadillo 3](img/ui/login.png) - -To login to the UI, select the **Institute account (oath2)** button and login using the institute or organisation login -screen you will be redirected to. - -### Superuser - -You need to have admin or superuser permissions if you want to add projects, users or profiles. This means you need to -be granted permission in order to be able to use the UI. If you don't have correct permissions, you will receive the -following error: - -![Error message in case you are not a superuser](img/ui/no-superuser.png) - -If you receive this error, contact someone in your institute that is already able to login without an error, or if you -don't have anyone available, send an email to MOLGENIS Support (molgenis-support@umcg.nl). - -To grant a user superuser permissions simply search for that user in the `Users` tab of the UI, and tick off the -_admin_ checkbox for that user: - -![Grant user superuser rights](img/ui/admin.png) - -## Projects - -Once you're logged in, you will be redirected to the `Projects` page. On this page you can add and edit projects. - -You can add users to projects and navigate to the "project-editor"-view and search through the projects using -the search bar on the top right. - -![Armadillo 3 projects page](img/ui/projects.png) - -### Editing projects - -To edit your project, click on the edit button in front of the project you want to edit: -. - -The row will be opened in edit mode: - -![Edit a specific row on the projects page](img/ui/edit-project.png) - -The edit mode can be recognized by it's blue background color and you have the option to add new users to your project -by clicking on the + button of the users column. Then, you can either -select an existing user from the dropdown, or add the email address of a new user. - -![Add an existing user to a project or enter the email address of the new user](img/ui/edit-projects-add-user.png) - -In case of adding a user in this screen, a warning will be shown to prevent email addresses with typographical errors from being added to -your system. To illustrate this, imagine you have the user `j.doe@example.com` in your users table. You want to give -them permissions for a certain project, but you mistakingly type the email address as `j.die@example.com`. A warning message will pop up -asking you whether you really want to add a new user. You will probably think "that's weird, j.doe -already exists, right?", hopefully this will help noticing the incorrect email address. You will probably cancel -adding the user and instead select the existing user, as you intended to. Keep in mind that, just as the warning message -suggests, the user will only be added if you save the row you are editing in the projects. - -![Warning message to remind you to save the project in order to finalize adding a new user](img/ui/add-user-warning.png) - -It is not possible to edit the name of your project; this was done intentionally in order to ensure tables, resources, -users, and permissions are transferred to the new project name correctly. - -Click on the checkmark to save the edited row and the X - to cancel. Be careful, if you do cancel your changes will be lost. - -### Adding projects - -To add a new project, click on the + button on top of the table. If -you click this button, an empty row will be opened in edit mode. - -![Add a new project to Armadillo](img/ui/add-project.png) - -Click on the checkmark to save the edited row and the X - to cancel. Be careful, if you do cancel your changes will be lost. - -## Project explorer - -If you click on the icon next to the project name, you will be directed to -the `project editor`. In this screen you can upload and preview data in projects. To go back to your `projects` page, -press the back button . - -![Armadillo project editor](img/ui/project-editor.png) - -If you click on a folder, it will open. - -![Armadillo project folder](img/ui/view-project-folder.png) - -Here you can upload files to that folder, or click on the tables (files) to preview their contents. - -![Armadillo project file preview](img/ui/preview-file.png) - -To upload files, either drag a file from your file browser to the file upload area, or click the area and select the -file. - -![Armadillo upload a file](img/ui/upload-a-file.png) - -After selecting the file, click on _upload_ to upload it. Depending on the size of the file it can take a second, -generally it is a fast process. - -It is also possible to create new folders. To do so, click the "add folder" button - just below the project name. An input dialog will be -presented: - -![Armadillo add a folder](img/ui/add-folder.png) - -Fill in the name you want to use and click on the checkmark button -. Please keep in mind that, just as the success message suggests, -the folder will only be saved if you put data in it. Select the new folder to select files to upload. - -### Resources - -In theory, all files can be uploaded into the projects. The only filetypes that allowed for a preview are `.parquet` -files. Other files that can be uploaded are treated as _resources_. Resource filetypes usually are `.rda` files or -`.Rdata` files. - -To be able to use these resources as a researcher, first an `.rds` file must be generated. How to create these files, is -described [here](https://molgenis.github.io/molgenis-r-armadillo/articles/create_resources.html). - -The URL of your resources is build up as follows: - -```r -{your url}/storage/projects/{project name}/objects/{name of the folder}%2F{the resource file} -``` - -Here's an example, with some example parameters: - -```r -url = "https://armadillo3.demo.molgenis.net" -project = "omics" -folder = "ewas" -file = "gse66351_1.rda" -``` - -Which results in the following url: - -```r -https://armadillo3.demo.molgenis.net/storage/projects/omics/objects/ewas%2Fgse66351_1.rda -``` - -## Users - -The `Users` page works just as the `Projects` page. You can search users by entering (part of) their email address or name -of the user into the search box: - -![Search for a user in the Users page](img/ui/admin.png) - -### Editing users - -Users can be edited, **except** for their email addresses. A reason for this is that when a user's email address changes, that user is possibly not -working for the same institution any longer and therefore might not be allowed to access the data anymore. - -![Editing a user on the Users page](img/ui/edit-user.png) - -In edit mode, the row will turn blue. Projects can be added by clicking on the + icon - in the projects column. - - - -You can add a new project by simply typing it and clicking the checkmark button -. You will be prompted a warning message, asking you to confirm to -add a new project, once the user is saved. - -Alternatively, you can select an existing project by using the search box or scrolling through the presented list, and selecting the desired project. - -### Adding users - -By clicking on the plus button on top op the table, a new user can be -added. The row with the new user will turn blue in edit mode. - - - -Users can be added before they have logged in previously. These users can be added to projects, this will grant them -permission to use the data from those projects upon their first login. However, to be able to login to the UI, users have to be admin or superuser. -Researchers should not be set to be admin. - -## Profiles - -![Armadillo Profiles page](img/ui/profiles.png) - -Since the release of Armadillo 3.0.0, it is possible to create and manage profiles in the user interface, rather than -asking system administrators to manage these profiles. You can start -and stop profiles. - -When you start a profile for the first time, it will take a bit longer to load because the profile needs to be -downloaded and installed before it can be started. - -![Stopping a profile](img/ui/loading-stop-profile.png) - -If you switch to the other screens while either starting or stopping a profile, the profiles page will not show the -loading information anymore. It is however still loading, and when it's done, if you reload the page, you will see that -your profile started or stopped successfully. - -Just like in the other screens, you can add profiles with the add-button -. - -![Add a profile](img/ui/add-profile.png) - -By default, some fields will be set. Please update them to install the correct profile. - -Possible images can be found on [dockerhub](https://hub.docker.com/search?q=datashield%2Farmadillo-rserver). We -recommend selecting one of the -[DataSHIELD standard profiles](https://www.datashield.org/help/standard-profiles-and-plaforms). The image name of those -profiles can be found on the dockerhub link above. - -Although the default `port` setting should find an available port, please keep in mind that the port has to be unique, -otherwise you cannot start your profile and will receive and error message. - -R packages can be whitelisted by adding them to the `package whitelist` column so researchers can use them. If you want -to whitelist a package, you need to make sure it is installed on the image you selected. Additionally, it's possible to -blacklist certain R functions in the `blacklist function` column. This can be interesting to use when certain functions -are not allowed to be used on certain data or within certain cohorts. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..04f5260e6 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,88 @@ +site_name: MOLGENIS Armadillo Docs +repo_name: MOLGENIS Armadillo +repo_url: https://github.com/molgenis/molgenis-service-armadillo +theme: + name: material + custom_dir: docs/overrides + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: custom + accent: custom + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: custom + accent: custom + toggle: + icon: material/brightness-4 + name: Switch to light mode + favicon: img/favicon.ico + logo: img/armadillo-logo.png + features: + - content.code.copy + - content.tooltips + - navigation.indexes + - navigation.footer +extra_css: + - css/simple.css +copyright: When using MOLGENIS Armadillo, don't forget to cite us https://doi.org/10.1093/bioinformatics/btae726 +extra: + social: + - icon: fontawesome/brands/linkedin + link: https://www.linkedin.com/company/molgenis + - icon: fontawesome/brands/x-twitter + link: https://x.com/molgenis +nav: + - "Basic Concepts": pages/basic_concepts.md + - "Quick Start" : pages/quick_start.md + - "About us": pages/about_us.md + - Basic Usage: + - pages/basic_usage/index.md + - "Armadillo User Interface": pages/basic_usage/armadillo_ui.md + - "Central Analysis Server (CAS)": pages/basic_usage/central_analysis_server.md + - "DSMolgenisArmadillo": pages/basic_usage/ds_molgenis_armadillo.md + - "MolgenisArmadillo": pages/basic_usage/molgenis_armadillo.md + - "dsUpload & dsDictionaries": pages/basic_usage/dsupload_dsdictionaries.md + - "dsLite": pages/basic_usage/dslite.md + - "Authentication Server": pages/basic_usage/auth.md +# - "Example Usage": pages/examples_usage.md +# - "Advanced Usage": pages/advanced_usage.md + - Install, Setup and Management: + - pages/install_management/index.md + - "Armadillo Installation": pages/install_management/armadillo_install.md + - "Armadillo Management": pages/install_management/armadillo_management.md + - "Armadillo Minor Release Update": pages/install_management/armadillo_minor_release_update.md + - "Migrate Armadillo 2 to 3": pages/install_management/armadillo_migrate_2_to_3.md + - "Migrate Armadillo 3 to 4": pages/install_management/armadillo_migrate_3_to_4.md + - "Developer guides": pages/dev_guides.md + - "License": pages/license.md + - "FAQ": pages/faq.md +# - "Troubleshooting": pages/troubleshooting.md + - "Contact": pages/contact.md +markdown_extensions: + - attr_list + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - def_list + - md_in_html + - pymdownx.blocks.caption + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - admonition + - pymdownx.details + - abbr + - pymdownx.keys \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index 6d74c1f26..3de471bed 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,7 +8,7 @@ "preview": "vite preview", "format": "prettier src --write --config ./.prettierrc.js", "test": "jest", - "docs": "docsify serve ../docs" + "docs": "mkdocs serve -f ../mkdocs.yml" }, "dependencies": { "@popperjs/core": "2.11.8",