Showing preview only (2,330K chars total). Download the full file or copy to clipboard to get everything.
Repository: prisma-flowdiagram/PRISMA2020
Branch: main
Commit: c6f9cac1dc21
Files: 48
Total size: 2.2 MB
Directory structure:
gitextract_rzi0l72b/
├── .Rbuildignore
├── .github/
│ ├── .gitignore
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── build-and-check-CRAN.yml
│ ├── check-and-deploy.yml
│ └── docker-build-push.yml
├── .gitignore
├── CITATION.cff
├── DESCRIPTION
├── Dockerfile
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── PRISMA2020.Rproj
├── R/
│ ├── PRISMA_flowdiagram.R
│ ├── globals.R
│ └── utils.R
├── README.md
├── code-of-conduct.md
├── contributing.md
├── inst/
│ ├── CITATION
│ ├── extdata/
│ │ ├── PRISMA.csv
│ │ ├── Template.html
│ │ └── citation.ris
│ └── shiny-examples/
│ └── PRISMA_flowdiagram/
│ ├── app.R
│ ├── rsconnect/
│ │ └── shinyapps.io/
│ │ └── estech/
│ │ └── prisma_flowdiagram.dcf
│ └── www/
│ ├── Haddaway_et_al_2022.ris
│ ├── PRISMA.csv
│ ├── kofi.js
│ └── labels.js
├── man/
│ ├── PRISMA_add_hyperlink_.Rd
│ ├── PRISMA_calc_filetype_.Rd
│ ├── PRISMA_data.Rd
│ ├── PRISMA_flowdiagram.Rd
│ ├── PRISMA_format_number_.Rd
│ ├── PRISMA_format_reasons_.Rd
│ ├── PRISMA_gen_tmp_svg_.Rd
│ ├── PRISMA_get_height_.Rd
│ ├── PRISMA_get_pos_.Rd
│ ├── PRISMA_insert_js_.Rd
│ ├── PRISMA_interactive_.Rd
│ ├── PRISMA_parse_reasons_.Rd
│ ├── PRISMA_save.Rd
│ ├── read_PRISMAdata.Rd
│ └── sr_flow_interactive.Rd
└── shiny-server.conf
================================================
FILE CONTENTS
================================================
================================================
FILE: .Rbuildignore
================================================
^.*\.Rproj$
^\.Rproj\.user$
^\.github$
.png
inst/shiny-examples
LICENSE.md
CITATION.cff
code-of-conduct.md
contributing.md
Dockerfile
shiny-server.conf
================================================
FILE: .github/.gitignore
================================================
*.html
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
open_collective: esmarconf
ko_fi: chriscpritchard
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/build-and-check-CRAN.yml
================================================
on:
push:
tags:
- 'v*'
name: Publish Release, Build & Check for CRAN
jobs:
R-CMD-Build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v2
with:
r-version: 'release'
use-public-rspm: true
- uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-tinytex@v2
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: |
devtools
rcmdcheck
- name: Build
run: R CMD build .
- uses: actions/upload-artifact@v3
with:
name: package
path: "*.tar.gz"
R-CMD-Check-As-CRAN:
runs-on: ubuntu-latest
needs: R-CMD-Build
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v2
with:
r-version: 'devel'
use-public-rspm: true
- uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-tinytex@v2
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: |
devtools
rcmdcheck
- uses: actions/download-artifact@v3
with:
name: package
- name: Check
run: R CMD check --as-cran *.tar.gz
- uses: actions/upload-artifact@v3
with:
name: check
path: "*.Rcheck"
release:
runs-on: ubuntu-latest
needs: R-CMD-Check-As-CRAN
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: ncipollo/release-action@v1
with:
artifacts: "**/*.tar.gz,**/*.Rcheck/*.log,**/*.Rcheck/*-manual.pdf"
deploy-release:
needs: release
uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1
with:
install-package: true
rsconnect-account-name: "estech"
rsconnect-app-name: "PRISMA_flowdiagram"
working-directory: "./inst/shiny-examples/PRISMA_flowdiagram"
environment: "release"
renvironment-contents: |
PRISMA_ANALYTICS=TRUE
KOFI_DONATE=TRUE
secrets:
rsconnect-account-token: ${{ secrets.TOKEN }}
rsconnect-account-secret: ${{ secrets.SECRET }}
================================================
FILE: .github/workflows/check-and-deploy.yml
================================================
on:
release:
types: [released]
pull_request:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
push:
branches:
- main
- master
name: Check & Deploy
jobs:
R-CMD-Check:
if: (github.event_name != 'issue_comment')
uses: prisma-flowdiagram/r-workflows/.github/workflows/R-CMD-check.yml@v1
pkgdown:
needs: R-CMD-check
if: (github.event_name == 'push')
uses: prisma-flowdiagram/r-workflows/.github/workflows/pkgdown.yml@v1
deploy-latest:
needs: pkgdown
if: (github.event_name == 'push')
uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1
with:
install-package: true
rsconnect-account-name: "estech"
rsconnect-app-name: "PRISMA_flowdiagram_latest"
working-directory: "./inst/shiny-examples/PRISMA_flowdiagram"
environment: "latest"
renvironment-contents: |
PRISMA_ANALYTICS=TRUE
KOFI_DONATE=TRUE
secrets:
rsconnect-account-token: ${{ secrets.TOKEN }}
rsconnect-account-secret: ${{ secrets.SECRET }}
check-pr-deploy:
runs-on: ubuntu-latest
if: github.event_name == 'issue_comment'
outputs:
triggered: ${{ steps.check-pr.outputs.triggered }}
steps:
- uses: khan/pull-request-comment-trigger@v1.1.0
id: check-pr
with:
trigger: '!deploy'
reaction: rocket
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
get-pull-ref:
runs-on: ubuntu-latest
needs: check-pr-deploy
if: (github.event_name == 'issue_comment' && github.event.issue.pull_request)
outputs:
repository: ${{ fromJson(steps.get-repo.outputs.data).head.repo.full_name }}
ref: ${{ fromJson(steps.get-repo.outputs.data).head.ref }}
steps:
- name: get-pull-request-ref
id: get-repo
uses: octokit/request-action@v2.x
with:
route: GET /repos/:repository/pulls/:issue_id
repository: ${{ github.repository }}
issue_id: ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
R-CMD-check-PR:
needs: [check-pr-deploy,get-pull-ref]
if: needs.check-pr-deploy.outputs.triggered == 'true'
uses: prisma-flowdiagram/r-workflows/.github/workflows/R-CMD-check.yml@v1
with:
repository: ${{ needs.get-pull-ref.outputs.repository || github.repository }}
ref: ${{ needs.get-pull-ref.outputs.ref || github.ref }}
deploy-pr-comment:
needs: [get-pull-ref,R-CMD-check-PR]
uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1
with:
repository: ${{ needs.get-pull-ref.outputs.repository || github.repository }}
ref: ${{ needs.get-pull-ref.outputs.ref || github.ref }}
install-package: true
rsconnect-account-name: "prismaflow-test"
rsconnect-app-name: "PRISMA_flowdiagram"
working-directory: "./inst/shiny-examples/PRISMA_flowdiagram"
environment: "pr"
secrets:
rsconnect-account-token: ${{ secrets.TOKEN }}
rsconnect-account-secret: ${{ secrets.SECRET }}
================================================
FILE: .github/workflows/docker-build-push.yml
================================================
name: Docker
on:
push:
branches:
- master
tags:
- v*
env:
IMAGE_NAME: prisma-flowdiagram/prisma-flowdiagram-shinyapp
jobs:
docker:
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Determine target image name
id: image-name
run: |
IMAGE_ID=ghcr.io/$IMAGE_NAME
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "master" ] && VERSION=latest
IMAGES=
IMAGES="$IMAGE_ID:$VERSION"$'\n'$IMAGES
# debug output
echo images $IMAGES
echo ::set-output name=images::"$IMAGES"
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.image-name.outputs.images }}
================================================
FILE: .gitignore
================================================
.Rproj.user
.Rhistory
.RData
.Ruserdata
.vscode/launch.json
**/.DS_Store
================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: Haddaway
given-names: Neal
orcid: https://orcid.org/0000-0003-3902-2234
- family-names: McGuinness
given-names: Luke A
orcid: https://orcid.org/0000-0001-8730-9761
- family-names: Pritchard
given-names: Chris C
orcid: https://orcid.org/0000-0002-1143-9751
title: "PRISMA2020: R package and ShinyApp for producing PRISMA 2020 compliant flow diagrams"
version: 1.1.0
doi: 10.5281/zenodo.4287834
date-released: 2023-02-06
license: MIT
url: "https://github.com/prisma-flowdiagram/PRISMA2020"
preferred-citation:
type: article
authors:
- family-names: Haddaway
given-names: Neal
orcid: https://orcid.org/0000-0003-3902-2234
- family-names: Page
given-names: Matthew J
orcid: https://orcid.org/0000-0002-4242-7526
- family-names: Pritchard
given-names: Chris C
orcid: https://orcid.org/0000-0002-1143-9751
- family-names: McGuinness
given-names: Luke A
orcid: https://orcid.org/0000-0001-8730-9761
doi: "10.1002/cl2.1230"
journal: "Campbell Systematic Reviews"
title: "PRISMA2020: an R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis"
year: 2022
================================================
FILE: DESCRIPTION
================================================
Package: PRISMA2020
Title: Make Interactive 'PRISMA' Flow Diagrams
Version: 1.1.2
Authors@R: c(
person(given = "Neal",
family = "Haddaway",
role = "aut",
email = "nealhaddaway@gmail.com",
comment = c(ORCID = "0000-0003-3902-2234")),
person("Luke", "McGuinness", role = "aut",
email = "luke.mcguinness@bristol.ac.uk",
comment = c(ORCID = "0000-0001-8730-9761")),
person("Chris", "Pritchard", role = c("aut", "cre"),
email = "chris.pritchard@ntu.ac.uk",
comment = c(ORCID = "0000-0002-1143-9751")),
person("Brennan", "Chapman", role = "ctb"),
person("Hossam", "Hammady", role= "ctb"))
Description: Systematic reviews should be described in a high degree of
methodological detail. The 'PRISMA' Statement calls for a high level of
reporting detail in systematic reviews and meta-analyses. An integral part
of the methodological description of a review is a flow diagram.
This package produces an interactive flow diagram that conforms to the
'PRISMA2020' preprint. When made interactive, the reader/user can click
on each box and be directed to another website or file online (e.g. a
detailed description of the screening methods, or a list of excluded full
texts), with a mouse-over tool tip that describes the information linked
to in more detail. Interactive versions can be saved as HTML files,
whilst static versions for inclusion in manuscripts can be saved as
HTML, PDF, PNG, SVG, PS or WEBP files.
Imports:
DiagrammeR,
DiagrammeRsvg,
htmltools,
htmlwidgets,
rsvg,
scales,
shiny,
shinyjs,
stats,
stringr,
utils,
xml2,
webp,
DT,
rio,
tools,
zip
License: MIT + file LICENSE
URL: https://github.com/prisma-flowdiagram/PRISMA2020
BugReports: https://github.com/prisma-flowdiagram/PRISMA2020/issues
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
================================================
FILE: Dockerfile
================================================
FROM rocker/shiny:latest
LABEL maintainer="Hossam Hammady <hossam@rayyan.ai>"
# Install system dependencies
RUN apt-get update -qq && \
apt-get install -y \
libssl-dev \
libfontconfig1-dev \
libcurl4-openssl-dev \
libxml2-dev \
libharfbuzz-dev \
libfribidi-dev \
libgit2-dev \
libfreetype6-dev \
libpng-dev \
libtiff-dev \
libv8-dev \
librsvg2-dev \
libwebp-dev \
pandoc
# Install R packages
RUN Rscript -e 'install.packages(c("DiagrammeR", "DiagrammeRsvg", "htmltools", "htmlwidgets", "scales", "shiny", "shinyjs", "stringr", "xml2", "DT", "rio", "zip", "rsvg", "webp", "devtools"))'
# Copy the shiny server configuration
COPY shiny-server.conf /etc/shiny-server/shiny-server.conf
# Install the PRISMA2020 package
COPY . /tmp/app
RUN R CMD INSTALL /tmp/app
# Copy the shiny app files
RUN mkdir -p /srv/shiny-server/prisma/app && \
echo "OK" > /srv/shiny-server/prisma/healthz.txt
COPY inst/shiny-examples/PRISMA_flowdiagram/. /srv/shiny-server/prisma/app/
# Clean up downloaded files
RUN rm -rf /tmp/*/downloaded_packages /tmp/app
================================================
FILE: LICENSE
================================================
YEAR: 2020
COPYRIGHT HOLDER: Neal R Haddaway
================================================
FILE: LICENSE.md
================================================
Copyright 2020 Neal R Haddaway
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: NAMESPACE
================================================
# Generated by roxygen2: do not edit by hand
export(PRISMA_data)
export(PRISMA_flowdiagram)
export(PRISMA_save)
importFrom(DT,addRow)
importFrom(rio,import)
importFrom(shiny,column)
importFrom(shinyjs,alert)
importFrom(stats,median)
importFrom(utils,apropos)
importFrom(webp,read_webp)
================================================
FILE: PRISMA2020.Rproj
================================================
Version: 1.0
RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default
EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8
RnwWeave: Sweave
LaTeX: pdfLaTeX
BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source
PackageRoxygenize: rd,collate,namespace
================================================
FILE: R/PRISMA_flowdiagram.R
================================================
# Suppress R CMD check note
#' @importFrom DT addRow
#' @importFrom rio import
#' @importFrom shiny column
#' @importFrom shinyjs alert
#' @importFrom stats median
#' @importFrom utils apropos
#' @importFrom webp read_webp
NULL
#' Plot interactive flow diagrams for systematic reviews
#' @description Produces a PRISMA2020 style flow diagram
#' for systematic reviews, with the option to add
#' interactivity through tooltips (mouseover popups) and
#' hyperlink URLs to each box.
#' Data can be imported from the standard CSV template provided.
#' @param data List of data inputs including numbers of studies,
#' box text, tooltips, and urls for hyperlinks.
#' Data inputted via the [PRISMA_data()] function.
#' If inputting individually, see the necessary parameters
#' listed in the [PRISMA_data()]) function and
#' combine them in a list using `data <- list()`.
#' @param interactive Logical argument TRUE or FALSE
#' whether to plot interactivity (tooltips and hyperlinked boxes).
#' @param previous Logical argument (TRUE or FALSE) specifying whether previous
#' studies were sought.
#' @param other Logical argument (TRUE or FALSE) specifying whether
#' other studies were sought.
#' @param detail_databases Logical argument (TRUE or FALSE) specifying whether
#' to list specific databases.
#' @param detail_registers Logical argument (TRUE or FALSE) specifying whether
#' to list specific registers.
#' @param meta_analysis Logical argument (TRUE or FALSE) specifying whether to
#' display a box for meta-analysis
#' @param font The font for text in each box. The default is 'Helvetica'.
#' @param fontsize The font size for text in each box. The default is '12'.
#' @param title_colour The colour for the upper middle title box (new studies).
#' The default is 'Goldenrod1'. See 'DiagrammeR' colour scheme.
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors>.
#' @param greybox_colour The colour for the left and right column boxes. The
#' default is 'Gainsboro'. See 'DiagrammeR' colour scheme
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors>.
#' @param main_colour The colour for the main box borders. The default is
#' 'Black'. See 'DiagrammeR' colour scheme
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors>.
#' @param arrow_colour The colour for the connecting lines. The default
#' is 'Black'. See 'DiagrammeR' colour scheme
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors>.
#' @param arrow_head The head shape for the line connectors. The default is
#' 'normal'. See DiagrammeR arrow shape specification
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#arrow-shapes>. #nolint
#' @param arrow_tail The tail shape for the line connectors. The default is
#' 'none'. See DiagrammeR arrow shape specification
#' <http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#arrow-shapes>. #nolint
#' @param side_boxes Whether or not to include the
#' blue label boxes along the side
#' @return A flow diagram plot.
#' @examples
#' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020")
#' data <- read.csv(csvFile);
#' data <- PRISMA_data(data);
#' plot <- PRISMA_flowdiagram(data,
#' fontsize = 12,
#' interactive = TRUE,
#' previous = FALSE,
#' other = TRUE);
#' plot
#' @export
PRISMA_flowdiagram <- function( #nolint
data,
interactive = FALSE,
previous = TRUE,
other = TRUE,
detail_databases = FALSE,
detail_registers = FALSE,
meta_analysis = FALSE,
fontsize = 7,
font = "Helvetica",
title_colour = "Goldenrod1",
greybox_colour = "Gainsboro",
main_colour = "Black",
arrow_colour = "Black",
arrow_head = "normal",
arrow_tail = "none",
side_boxes = TRUE) {
# removes the need to attach() the data
# https://stackoverflow.com/a/11505637
for (var in seq_len(length(data))) {
assign(names(data)[var], data[[var]])
}
# positional attributes, in inches
diagram_start_x <- 0
diagram_start_y <- 0
prev_study_width <- 0
prev_study_offset <- 0
prev_study_height <- 0
total_studies_height <- 0
total_ma_height <- 0
other_identified_height <- 0
other_sought_reports_height <- 0
other_notretrieved_height <- 0
other_assessed_height <- 0
other_excluded_height <- 0
default_box_width <- 3.5
min_box_height <- 0.5
default_box_spacing <- 0.5
section_label_length <- 0.4
top_box_width <- default_box_width * 2 + default_box_spacing
A <- "" #nolint
Aedge <- "" #nolint
bottomedge <- ""
previous_nodes <- ""
finalnode <- ""
metanode <- ""
prev_rank1 <- ""
prevnode1 <- ""
prevnode2 <- ""
#wrap exclusion reasons
dbr_excluded[, 1] <- stringr::str_wrap(
dbr_excluded[, 1],
width = 35
)
other_excluded[, 1] <- stringr::str_wrap(
other_excluded[, 1],
width = 35
)
# wrap specific database registers
database_specific_results[, 1] <- stringr::str_wrap(
database_specific_results[, 1],
width = 35
)
register_specific_results[, 1] <- stringr::str_wrap(
register_specific_results[, 1],
width = 35
)
#remove previous box if both values are zero
if (is.na(previous_studies) == TRUE && is.na(previous_reports) == TRUE) {
previous <- FALSE
}
if (previous == TRUE) {
#conditional studies and reports - empty text if blank
if (is.na(previous_studies) == TRUE) {
cond_prevstud <- ""
} else {
cond_prevstud <- stringr::str_wrap(
paste0(
previous_studies_text,
" (n = ", previous_studies, ")"
),
width = 40)
}
if (is.na(previous_reports) == TRUE) {
cond_prevrep <- ""
} else {
cond_prevrep <- paste0(
stringr::str_wrap(
previous_reports_text,
width = 40
),
"\n(n = ", previous_reports, ")"
)
}
if (is.na(previous_studies) == TRUE || is.na(previous_reports) == TRUE) {
dbl_br <- ""
} else {
dbl_br <- "\n"
}
prev_study_label <- paste0(cond_prevstud, dbl_br, cond_prevrep)
total_studies_label <- paste0(
stringr::str_wrap(
paste0(
total_studies_text,
" (n = ", total_studies, ")"
),
width = 33
),
"\n",
stringr::str_wrap(
paste0(
total_reports_text,
" (n = ", total_reports, ")"
),
width = 33
)
)
# we multiply by 2 because we need the width
# from the start, not just the centre
prev_study_width <- default_box_width * 2
prev_study_offset <- default_box_spacing
prev_box_x <- PRISMA_get_pos_(
diagram_start_x,
default_box_spacing,
default_box_width,
prev_study_offset
)
prev_study_height <- PRISMA_get_height_(
stringr::str_count(prev_study_label, "\n"),
min_box_height
)
total_studies_height <- PRISMA_get_height_(
stringr::str_count(total_studies_label, "\n"),
min_box_height
)
}
# conditionals for the node labels
if (
is.na(website_results) == TRUE &&
is.na(organisation_results) == TRUE &&
is.na(citations_results) == TRUE
) {
other <- FALSE
}
if (other == TRUE) {
# conditionals for the node labels
if (is.na(website_results) == FALSE) {
cond_websites <- paste0(
"\n",
website_results_text,
" (n = ", website_results, ")"
)
} else {
cond_websites <- ""
}
if (is.na(organisation_results) == FALSE) {
cond_organisation <- paste0(
"\n",
organisation_results_text,
" (n = ", organisation_results, ")"
)
} else {
cond_organisation <- ""
}
if (is.na(citations_results) == FALSE) {
cond_citation <- paste0(
"\n",
citations_results_text,
" (n = ", citations_results, ")"
)
} else {
cond_citation <- ""
}
other_excluded_data <- PRISMA_format_reasons_(other_excluded) #nolint
# labels
other_identified_label <- paste0(
"Records identified from:",
cond_websites,
cond_organisation,
cond_citation
)
other_sought_reports_label <- paste0(
other_sought_reports_text,
"\n(n = ",
other_sought_reports,
")"
)
other_notretrieved_label <- paste0(
other_notretrieved_reports_text,
"\n(n = ",
other_notretrieved_reports,
")"
)
other_assessed_label <- paste0(
other_assessed_text,
"\n(n = ",
other_assessed,
")"
)
other_excluded_label <- paste0(
other_excluded_text, other_excluded_data
)
# heights
other_identified_height <- PRISMA_get_height_(
stringr::str_count(other_identified_label, "\n"),
min_box_height
)
other_sought_reports_height <- PRISMA_get_height_(
stringr::str_count(other_sought_reports_label, "\n"),
min_box_height
)
other_notretrieved_height <- PRISMA_get_height_(
stringr::str_count(other_notretrieved_label, "\n"),
min_box_height
)
other_assessed_height <- PRISMA_get_height_(
stringr::str_count(other_assessed_label, "\n"),
min_box_height
)
other_excluded_height <- PRISMA_get_height_(
stringr::str_count(other_excluded_label, "\n"),
min_box_height
)
}
if (is.na(new_studies) == FALSE) {
cond_newstud <- paste0(
stringr::str_wrap(new_studies_text, width = 40),
"\n(n = ", new_studies, ")\n"
)
} else {
cond_newstud <- ""
}
if (is.na(new_reports) == FALSE) {
cond_newreports <- paste0(
stringr::str_wrap(new_reports_text, width = 40),
"\n(n = ", new_reports, ")"
)
} else {
cond_newreports <- ""
}
if (is.na(total_studies_ma) == FALSE) {
cond_total_studies_ma <- paste0(
stringr::str_wrap(
paste0(
total_studies_ma_text,
" (n = ", total_studies_ma, ")"
),
width = 33
),
"\n"
)
} else {
cond_total_studies_ma <- ""
}
if (is.na(total_reports_ma) == FALSE) {
cond_total_reports_ma <- stringr::str_wrap(
paste0(
total_reports_ma_text,
" (n = ", total_reports_ma, ")"
),
width = 33
)
} else {
cond_total_reports_ma <- ""
}
if (detail_databases == TRUE) {
db_specific_data <- PRISMA_format_reasons_(database_specific_results) #nolint
} else {
db_specific_data <- ""
db_specific_data_nr <- ""
}
if (detail_registers == TRUE) {
reg_specific_data <- PRISMA_format_reasons_(register_specific_results) #nolint
} else {
reg_specific_data <- ""
reg_specific_data_nr <- ""
}
if (is.na(database_results) == FALSE) {
cond_database <- paste0(
"\n",
database_results_text,
" (n = ", database_results, ")", db_specific_data)
} else {
cond_database <- paste0("", db_specific_data_nr)
}
if (is.na(register_results) == FALSE) {
cond_register <- paste0(
"\n",
register_results_text,
" (n = ", register_results, ")", reg_specific_data)
} else {
cond_register <- paste0("", reg_specific_data_nr)
}
dbr_excluded_data <- PRISMA_format_reasons_(dbr_excluded) #nolint
if (is.na(duplicates) == FALSE) {
cond_duplicates <- paste0(
stringr::str_wrap(
paste0(
duplicates_text,
" (n = ", duplicates, ")"
),
width = 42
),
"\n"
)
} else {
cond_duplicates <- ""
}
if (is.na(excluded_automatic) == FALSE) {
cond_automatic <- paste0(
stringr::str_wrap(
paste0(
excluded_automatic_text,
" (n = ", excluded_automatic, ")"
),
width = 42
),
"\n"
)
} else {
cond_automatic <- ""
}
if (is.na(excluded_other) == FALSE) {
cond_exclother <- paste0(
stringr::str_wrap(
paste0(
excluded_other_text,
" (n = ", excluded_other, ")"
),
width = 42
)
)
} else {
cond_exclother <- ""
}
if (
is.na(duplicates) == TRUE &&
is.na(excluded_automatic) == TRUE &&
is.na(excluded_other) == TRUE
) {
cond_duplicates <- "(n = 0)"
}
# labels for the nodes
newstudy_newreports_label <- paste0(cond_newstud, cond_newreports)
total_ma_label <- paste0(cond_total_studies_ma, cond_total_reports_ma)
dbr_assessed_label <- paste0(
dbr_assessed_text,
"\n(n = ",
dbr_assessed,
")"
)
dbr_sought_label <- paste0(
dbr_sought_reports_text,
"\n(n = ",
dbr_sought_reports,
")"
)
dbr_screened_label <- paste0(
records_screened_text,
"\n(n = ",
records_screened,
")"
)
dbr_identified_label <- paste0(
"Records identified from:",
cond_database,
cond_register
)
dbr_excluded_label <- paste0(
dbr_excluded_text,
dbr_excluded_data
)
dbr_notretrieved_label <- paste0(
dbr_notretrieved_reports_text,
"\n(n = ",
dbr_notretrieved_reports,
")"
)
dbr_screened_excluded_label <- paste0(
records_excluded_text,
"\n(n = ",
records_excluded,
")"
)
dbr_notscreened_label <- paste0(
"Records removed before screening:\n",
cond_duplicates,
cond_automatic,
cond_exclother
)
# we set the height of various nodes here
newstudy_newreports_height <- PRISMA_get_height_(
stringr::str_count(newstudy_newreports_label, "\n"),
min_box_height
)
if (is.na(total_reports_ma) == TRUE && is.na(total_studies_ma) == TRUE) {
meta_analysis <- FALSE
}
if (meta_analysis == TRUE) {
total_ma_height <- PRISMA_get_height_(
stringr::str_count(total_ma_label, "\n"),
min_box_height
)
}
dbr_assessed_height <- PRISMA_get_height_(
stringr::str_count(dbr_assessed_label, "\n"),
min_box_height
)
dbr_sought_height <- PRISMA_get_height_(
stringr::str_count(dbr_sought_label, "\n"),
min_box_height
)
dbr_screened_height <- PRISMA_get_height_(
stringr::str_count(dbr_screened_label, "\n"),
min_box_height
)
dbr_identified_height <- PRISMA_get_height_(
stringr::str_count(dbr_identified_label, "\n"),
min_box_height
)
dbr_excluded_height <- PRISMA_get_height_(
stringr::str_count(dbr_excluded_label, "\n"),
min_box_height
)
dbr_notretrieved_height <- PRISMA_get_height_(
stringr::str_count(dbr_notretrieved_label, "\n"),
min_box_height
)
dbr_screened_excluded_height <- PRISMA_get_height_(
stringr::str_count(dbr_screened_excluded_label, "\n"),
min_box_height
)
dbr_notscreened_height <- PRISMA_get_height_(
stringr::str_count(dbr_notscreened_label, "\n"),
min_box_height
)
screening_box_height <- max(
c(
dbr_screened_height,
dbr_screened_excluded_height
)
) + max(
c(
dbr_notretrieved_height,
dbr_sought_height,
other_sought_reports_height,
other_notretrieved_height
)
) + max(
c(
dbr_assessed_height,
dbr_excluded_height,
other_assessed_height,
other_excluded_height
)
) +
default_box_spacing * 2
identification_box_height <-
max(
c(
dbr_identified_height,
dbr_notscreened_height,
prev_study_height,
other_identified_height
)
) +
default_box_spacing
included_box_height <-
newstudy_newreports_height +
total_studies_height +
total_ma_height +
default_box_spacing * 3
assessed_height <- max(
c(
dbr_assessed_height,
other_assessed_height,
dbr_excluded_height,
other_excluded_height
)
)
sought_height <- max(
c(
dbr_sought_height,
other_sought_reports_height,
dbr_notretrieved_height,
other_notretrieved_height
)
)
screened_height <- max(
c(
dbr_screened_height,
dbr_screened_excluded_height
)
)
identified_height <- max(
c(
dbr_identified_height,
dbr_notscreened_height,
other_identified_height,
prev_study_height
)
)
warning(included_box_height)
# here we set the x and y co-ordinates for new studies
# (other items depend on this being created)
dbr_box_x <- PRISMA_get_pos_(
diagram_start_x,
prev_study_offset + default_box_spacing,
prev_study_width,
default_box_width
)
dbr_removed_x <- PRISMA_get_pos_(
dbr_box_x,
default_box_spacing,
default_box_width,
default_box_width
)
newstudy_newreports_y <- PRISMA_get_pos_(
diagram_start_y,
default_box_spacing,
total_studies_height,
newstudy_newreports_height
)
if (previous == TRUE) {
total_ma_y <- PRISMA_get_pos_(
diagram_start_y,
default_box_spacing,
total_studies_height,
total_ma_height,
negative_offset = TRUE
)
} else {
total_ma_y <- PRISMA_get_pos_(
newstudy_newreports_y,
default_box_spacing,
newstudy_newreports_height,
total_ma_height,
negative_offset = TRUE
)
}
assessed_y <- PRISMA_get_pos_(
newstudy_newreports_y,
default_box_spacing * 2,
newstudy_newreports_height,
assessed_height
)
sought_y <- PRISMA_get_pos_(
assessed_y,
default_box_spacing,
assessed_height,
sought_height
)
screened_y <- PRISMA_get_pos_(
sought_y,
default_box_spacing,
sought_height,
screened_height
)
identified_y <- PRISMA_get_pos_(
screened_y,
default_box_spacing * 2,
screened_height,
identified_height
)
top_box_y <- PRISMA_get_pos_(
identified_y,
default_box_spacing,
identified_height,
section_label_length
)
screening_y <-
mean(
c(
(screened_y + (screened_height / 2)),
(assessed_y - (dbr_excluded_height / 2))
)
)
included_y <- if (total_studies_height > 0 && total_ma_height > 0) {
mean(c(diagram_start_y, newstudy_newreports_y, total_ma_y))
} else if (total_studies_height > 0) {
mean(c(diagram_start_y, newstudy_newreports_y))
} else if (total_ma_height > 0) {
mean(c(newstudy_newreports_y, total_ma_y))
} else {
newstudy_newreports_y
}
if (side_boxes == TRUE) {
sidebox <- paste0(
"node [
shape = box,
fontsize = ", fontsize, ",
fontname = ", font, ",
color = ", title_colour, "
]
identification [
color = LightSteelBlue2,
label = ' ',
style = 'filled,rounded',
pos = '", diagram_start_x, ",", identified_y, "!',
width = ", section_label_length, ",
height = ", identification_box_height, ",
tooltip = '", tooltips["identification"], "'
];
screening [
color = LightSteelBlue2,
label = ' ',
style = 'filled,rounded',
pos = '", diagram_start_x, ",", screening_y, "!',
width = ", section_label_length, ",
height = ", screening_box_height, ",
tooltip = '", tooltips["screening"], "'
];
included [
color = LightSteelBlue2,
label = ' ',
style = 'filled,rounded',
pos = '", diagram_start_x, ",", included_y, "!',
width = ", section_label_length, ",
height = ", included_box_height, ",
tooltip = '", tooltips["included"], "'
];\n"
)
} else {
sidebox <- ""
}
if (previous == TRUE) {
A <- paste0( #nolint
"A [
label = '',
pos = '", prev_box_x, ",", diagram_start_y + 0, "!',
tooltip = ''
]"
)
Aedge <- paste0( #nolint
"subgraph cluster0 {
edge [
color = White,
arrowhead = none,
arrowtail = none
]
1->2;
edge [
color = ", arrow_colour, ",
arrowhead = none,
arrowtail = ", arrow_tail, "
]
2->A;
edge [
color = ", arrow_colour, ",
arrowhead = ", arrow_head, ",
arrowtail = none,
constraint = FALSE
]
A->19;
}"
)
bottomedge <- paste0(
"edge [
color = '", arrow_colour, "',
arrowhead = '", arrow_head, "',
arrowtail = '", arrow_tail, "'
]
12->19;\n"
)
previous_nodes <- paste0(
"node [
shape = box,
fontsize = ", fontsize, ",
fontname = ", font, ",
color = ", greybox_colour, "
]
1 [
label = '", previous_text, "',
style = 'rounded,filled',
width = ", default_box_width, ",
height = ", section_label_length, ",
pos = '",
prev_box_x,
",",
top_box_y,
"!',
tooltip = '", tooltips["prevstud"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour, "
]
2 [
label = '", prev_study_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", prev_study_height, ",
fixed = 'true',
pos = '", prev_box_x, ",", identified_y, "!',
tooltip = '", tooltips["previous_studies"], "'
]"
)
finalnode <- paste0(
"node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour, "
]
19 [
label = '", total_studies_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", total_studies_height, ",
fixedsize = 'true',
pos = '",
PRISMA_get_pos_(
diagram_start_x,
prev_study_offset + default_box_spacing,
prev_study_width,
default_box_width
),
",",
diagram_start_y,
"!',
tooltip = '", tooltips["total_studies"], "'
]"
)
prev_rank1 <- "{rank = same; A; 19}"
prevnode1 <- "1; "
prevnode2 <- "2; "
}
if (other == TRUE) {
# positions
other_box_x <- PRISMA_get_pos_(
dbr_removed_x,
default_box_spacing,
default_box_width,
default_box_width
)
other_removed_x <- PRISMA_get_pos_(
other_box_x,
default_box_spacing,
default_box_width,
default_box_width
)
B <- paste0( #nolint
"B [
label = '',
pos = '", other_box_x, ",", newstudy_newreports_y, "!',
tooltip = ''
]"
)
cluster2 <- paste0(
"subgraph cluster2 {
edge [
color = White,
arrowhead = none,
arrowtail = none
]
13->14;
edge [
color = ", arrow_colour, ",
arrowhead = ", arrow_head, ",
arrowtail = ", arrow_tail,
"]
14->15;
15->16;
15->17;
17->18;
edge [
color = ", arrow_colour, ",
arrowhead = none,
arrowtail = ", arrow_tail,
"]
17->B;
edge [
color = ", arrow_colour, ",
arrowhead = ", arrow_head, ",
arrowtail = none,
constraint = FALSE
]
B->12;
}"
)
othernodes <- paste0(
"node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour,
"]
13 [
label = '", other_text, "',
style = 'rounded,filled',
width = ", top_box_width, ",
height = ", section_label_length, ",
pos = '",
mean(c(other_box_x, other_removed_x)),
",",
top_box_y,
"!',
tooltip = '", tooltips["othstud"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour,
"]
14 [
label = '", other_identified_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", other_identified_height, ",
pos = '", other_box_x, ",", identified_y, "!',
tooltip = '", tooltips["website_results"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour,
"]
15 [
label = '", other_sought_reports_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", other_sought_reports_height, ",
pos = '", other_box_x, ",", sought_y, "!',
tooltip = '", tooltips["other_sought_reports"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour,
"]
16 [
label = '", other_notretrieved_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", other_notretrieved_height, ",
pos = '", other_removed_x, ",", sought_y, "!',
tooltip = '", tooltips["other_notretrieved_reports"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour, "
]
17 [
label = '", other_assessed_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", other_assessed_height, ",
pos = '", other_box_x, ",", assessed_y, "!',
tooltip = '", tooltips["other_assessed"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", greybox_colour,
"]
18 [
label = '", other_excluded_label, "',
style = 'filled',
width = ", default_box_width, ",
height = ", other_excluded_height, ",
pos = '",
other_removed_x,
",",
assessed_y,
"!',
tooltip = '", tooltips["other_excluded"], "'
]\n"
)
extraedges <- "16->18;"
othernode13 <- "; 13"
othernode14 <- "; 14"
othernode1516 <- "; 15; 16"
othernode1718 <- "; 17; 18"
othernodeB <- "; B" #nolint
} else {
B <- "" #nolint
cluster2 <- ""
othernodes <- ""
extraedges <- ""
othernode13 <- ""
othernode14 <- ""
othernode1516 <- ""
othernode1718 <- ""
othernodeB <- "" #nolint
}
if (meta_analysis == TRUE) {
metanode <- paste0("node [
shape = box,
fontname = ", font, ",
color = ", main_colour, ",
fillcolor = '',
style = solid
]
23 [
label = '", total_ma_label, "',
width = ", default_box_width, ",
height = ", total_ma_height, ",
pos = '", dbr_box_x, ",", total_ma_y, "!',
tooltip = '", tooltips["total_studies_ma"], "'
]")
if (previous == TRUE) {
bottomedge <- paste0(
bottomedge, "edge [
color = '", arrow_colour, "',
arrowhead = '", arrow_head, "',
arrowtail = '", arrow_tail, "'
]
19->23;\n"
)
} else {
bottomedge <- paste0(
bottomedge, "edge [
color = '", arrow_colour, "',
arrowhead = '", arrow_head, "',
arrowtail = '", arrow_tail, "'
]
12->23;\n"
)
}
}
x <- DiagrammeR::grViz(
paste0(
"digraph TD {
graph[
splines = ortho,
layout = neato,
tooltip = 'Click the boxes for further information',
outputorder = edgesfirst,
]",
sidebox,
previous_nodes,
"node [
shape = box,
fontsize = ", fontsize, ",
fontname = ", font, ",
color = ", title_colour,
"]
3 [
label = '", newstud_text, "',
style = 'rounded,filled',
width = ", top_box_width, ",
height = ", section_label_length, ",
pos = '",
mean(c(dbr_box_x, dbr_removed_x)),
",",
top_box_y,
"!',
tooltip = '", tooltips["newstud"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour,
"]
4 [
label = '", dbr_identified_label, "',
width = ", default_box_width, ",
height = ", dbr_identified_height, ",
pos = '", dbr_box_x, ",", identified_y, "!',
tooltip = '", tooltips["database_results"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour,
"]
5 [
label = '", dbr_notscreened_label, "',
width = ", default_box_width, ",
height = ", min_box_height, ",
pos = '", dbr_removed_x, ",", identified_y, "!',
tooltip = '", tooltips["duplicates"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour, "
]
6 [
label = '", dbr_screened_label, "',
width = ", default_box_width, ",
height = ", dbr_screened_height, ",
pos = '", dbr_box_x, ",", screened_y, "!',
tooltip = '", tooltips["records_screened"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour, "
]
7 [
label = '", dbr_screened_excluded_label, "',
width = ", default_box_width, ",
height = ", min_box_height, ",
pos = '", dbr_removed_x, ",", screened_y, "!',
tooltip = '", tooltips["records_excluded"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour,
"]
8 [
label = '", dbr_sought_label, "',
width = ", default_box_width, ",
height = ", dbr_sought_height, ",
pos = '", dbr_box_x, ",", sought_y, "!',
tooltip = '", tooltips["dbr_sought_reports"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour,
"]
9 [
label = '", dbr_notretrieved_label, "',
width = ", default_box_width, ",
height = ", min_box_height, ",
pos = '", dbr_removed_x, ",", sought_y, "!',
tooltip = '", tooltips["dbr_notretrieved_reports"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour,
"]
10 [
label = '", dbr_assessed_label, "',
width = ", default_box_width, ",
height = ", dbr_assessed_height, ",
pos = '", dbr_box_x, ",", assessed_y, "!',
tooltip = '", tooltips["dbr_assessed"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour, ",
fillcolor = White,
style = filled
]
11 [
label = '", dbr_excluded_label, "',
width = ", default_box_width, ",
height = ", min_box_height, ",
pos = '",
dbr_removed_x,
",",
assessed_y,
"!',
tooltip = '", tooltips["dbr_excluded"], "'
]
node [
shape = box,
fontname = ", font, ",
color = ", main_colour, ",
fillcolor = '',
style = solid
]
12 [
label = '", newstudy_newreports_label, "',
width = ", default_box_width, ",
height = ", newstudy_newreports_height, ",
pos = '", dbr_box_x, ",", newstudy_newreports_y, "!',
tooltip = '", tooltips["new_studies"], "'
]",
othernodes,
finalnode,
metanode,
"node [
shape = square,
width = 0,
color=White
]\n",
A,
"\n",
B,
"\n",
Aedge,
"node [
shape = square,
width = 0,
style=invis
]
C [
label = '',
width = ", default_box_width, ",
height = ", min_box_height, ",
pos = '", dbr_removed_x, ",", assessed_y, "!',
tooltip = ''
]
subgraph cluster1 {
edge [
style = invis
]
3->4;
3->5;
edge [
color = ", arrow_colour, ",
arrowhead = ", arrow_head, ",
arrowtail = ", arrow_tail, ",
style = filled
]
4->5;
4->6;
6->7;
6->8;
8->9;
8->10;
10->C;
10->12;
edge [
style = invis
]
5->7;
7->9;
9->11;
", extraedges, "
}",
cluster2,
"\n",
bottomedge,
"\n\n",
prev_rank1,
"\n",
"{
rank = same; ",
prevnode1,
"3",
othernode13,
"}
{
rank = same; ",
prevnode2,
"4; 5",
othernode14,
"}
{
rank = same; 6; 7
}
{
rank = same; 8; 9",
othernode1516,
"}
{
rank = same; 10; 11",
othernode1718,
"}
{
rank = same; 12",
othernodeB,
"}
}"
)
)
if (side_boxes == TRUE) {
x <- PRISMA_insert_js_(
x,
identification_text = identification_text,
screening_text = screening_text,
included_text = included_text
)
}
if (interactive == TRUE) {
x <- PRISMA_interactive_(
x,
urls,
previous = previous,
other = other
)
}
return(x)
}
#' Read in PRISMA flow diagram data
#'
#' @description Read in a template CSV containing data for the flow diagram
#' @param data File to read in.
#' @return A list of objects needed to plot the flow diagram
#' @examples
#' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020")
#' data <- read.csv(csvFile);
#' data <- PRISMA_data(data);
#' @export
PRISMA_data <- function(data) { #nolint
# Ensure data is a df, not a tibble;
# tibbles do not return vectors using df[, 1].
data <- as.data.frame(data)
#Set parameters
previous_studies <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"previous_studies",
data[, 1]
),
]$n
)
)
previous_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"previous_reports",
data[, 1]
),
]$n
)
)
register_results <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"register_results",
data[, 1]
),
]$n
)
)
database_results <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"database_results",
data[, 1]
),
]$n
)
)
database_specific_results <- PRISMA_parse_reasons_(data[ #nolint
grep(
"database_specific_results",
data[, 1]
),
]$n
)
register_specific_results <- PRISMA_parse_reasons_(data[ #nolint
grep(
"register_specific_results",
data[, 1]
),
]$n
)
website_results <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"website_results",
data[, 1]
),
]$n
)
)
organisation_results <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"organisation_results",
data[, 1]
),
]$n
)
)
citations_results <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"citations_results",
data[, 1]
),
]$n
)
)
duplicates <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"duplicates",
data[, 1]
),
]$n
)
)
excluded_automatic <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"excluded_automatic",
data[, 1]
),
]$n
)
)
excluded_other <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"excluded_other",
data[, 1]
),
]$n
)
)
records_screened <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"records_screened",
data[, 1]
),
]$n
)
)
records_excluded <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"records_excluded",
data[, 1]
),
]$n
)
)
dbr_sought_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"dbr_sought_reports",
data[, 1]
),
]$n
)
)
dbr_notretrieved_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"dbr_notretrieved_reports",
data[, 1]
),
]$n
)
)
other_sought_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"other_sought_reports",
data[, 1]
),
]$n
)
)
other_notretrieved_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"other_notretrieved_reports",
data[, 1]
),
]$n
)
)
dbr_assessed <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"dbr_assessed",
data[, 1]
),
]$n
)
)
dbr_excluded <- PRISMA_parse_reasons_(data[ #nolint
grep(
"dbr_excluded",
data[, 1]
),
]$n
)
other_assessed <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"other_assessed",
data[, 1]
),
]$n
)
)
other_excluded <- PRISMA_parse_reasons_(data[ #nolint
grep(
"other_excluded",
data[, 1]
),
]$n
)
new_studies <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"new_studies",
data[, 1]
),
]$n
)
)
new_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"new_reports",
data[, 1]
),
]$n
)
)
total_studies <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"total_studies$",
data[, 1]
),
]$n
)
)
total_reports <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"total_reports$",
data[, 1]
),
]$n
)
)
total_studies_ma <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"total_studies_ma",
data[, 1]
),
]$n
)
)
total_reports_ma <- scales::comma(
PRISMA_format_number_( #nolint
data[
grep(
"total_reports_ma",
data[, 1]
),
]$n
)
)
tooltips <- list()
for (i in seq_len(nrow(data))) {
if (!is.na(data[i, ]$tooltips)) {
if (is.na(data[i, ]$data)) {
name <- data[i, ]$box
} else {
name <- data[i, ]$data
}
tooltips[[name]] <- data[i, ]$tooltips
}
}
urls <- data.frame(
box = data[!duplicated(data$box), ]$box,
url = data[!duplicated(data$box), ]$url
)
#set text - if text >33 characters,
previous_text <- data[grep("prevstud", data[, 3]), ]$boxtext
newstud_text <- data[grep("newstud", data[, 3]), ]$boxtext
other_text <- data[grep("othstud", data[, 3]), ]$boxtext
previous_studies_text <- data[grep("previous_studies", data[, 1]), ]$boxtext
previous_reports_text <- data[grep("previous_reports", data[, 1]), ]$boxtext
register_results_text <- data[grep("register_results", data[, 1]), ]$boxtext
database_results_text <- data[grep("database_results", data[, 1]), ]$boxtext
website_results_text <- data[grep("website_results", data[, 1]), ]$boxtext
organisation_results_text <- data[
grep(
"organisation_results",
data[, 1]
),
]$boxtext
citations_results_text <- data[grep("citations_results", data[, 1]), ]$boxtext
duplicates_text <- data[grep("duplicates", data[, 1]), ]$boxtext
excluded_automatic_text <- data[
grep(
"excluded_automatic",
data[, 1]
),
]$boxtext
excluded_other_text <- data[grep("excluded_other", data[, 1]), ]$boxtext
records_screened_text <- data[grep("records_screened", data[, 1]), ]$boxtext
records_excluded_text <- data[grep("records_excluded", data[, 1]), ]$boxtext
dbr_sought_reports_text <- data[
grep(
"dbr_sought_reports",
data[, 1]
),
]$boxtext
dbr_notretrieved_reports_text <- data[
grep(
"dbr_notretrieved_reports",
data[, 1]
),
]$boxtext
other_sought_reports_text <- data[
grep(
"other_sought_reports",
data[, 1]
),
]$boxtext
other_notretrieved_reports_text <- data[ #nolint
grep(
"other_notretrieved_reports",
data[, 1]
),
]$boxtext
dbr_assessed_text <- data[grep("dbr_assessed", data[, 1]), ]$boxtext
dbr_excluded_text <- data[grep("dbr_excluded", data[, 1]), ]$boxtext
other_assessed_text <- data[grep("other_assessed", data[, 1]), ]$boxtext
other_excluded_text <- data[grep("other_excluded", data[, 1]), ]$boxtext
new_studies_text <- data[grep("new_studies", data[, 1]), ]$boxtext
new_reports_text <- data[grep("new_reports", data[, 1]), ]$boxtext
total_studies_text <- data[grep("total_studies$", data[, 1]), ]$boxtext
total_reports_text <- data[grep("total_reports$", data[, 1]), ]$boxtext
identification_text <- data[grep("identification", data[, 1]), ]$boxtext
screening_text <- data[grep("screening", data[, 1]), ]$boxtext
included_text <- data[grep("included", data[, 1]), ]$boxtext
total_studies_ma_text <- data[grep("total_studies_ma", data[, 1]), ]$boxtext
total_reports_ma_text <- data[grep("total_reports_ma", data[, 1]), ]$boxtext
x <- list(
previous_studies = previous_studies,
previous_reports = previous_reports,
register_results = register_results,
database_results = database_results,
database_specific_results = database_specific_results,
register_specific_results = register_specific_results,
website_results = website_results,
organisation_results = organisation_results,
citations_results = citations_results,
duplicates = duplicates,
excluded_automatic = excluded_automatic,
excluded_other = excluded_other,
records_screened = records_screened,
records_excluded = records_excluded,
dbr_sought_reports = dbr_sought_reports,
dbr_notretrieved_reports = dbr_notretrieved_reports,
other_sought_reports = other_sought_reports,
other_notretrieved_reports = other_notretrieved_reports,
dbr_assessed = dbr_assessed,
dbr_excluded = dbr_excluded,
other_assessed = other_assessed,
other_excluded = other_excluded,
new_studies = new_studies,
new_reports = new_reports,
total_studies = total_studies,
total_reports = total_reports,
total_studies_ma = total_studies_ma,
total_reports_ma = total_reports_ma,
previous_text = previous_text,
newstud_text = newstud_text,
other_text = other_text,
previous_studies_text = previous_studies_text,
previous_reports_text = previous_reports_text,
register_results_text = register_results_text,
database_results_text = database_results_text,
website_results_text = website_results_text,
organisation_results_text = organisation_results_text,
citations_results_text = citations_results_text,
duplicates_text = duplicates_text,
excluded_automatic_text = excluded_automatic_text,
excluded_other_text = excluded_other_text,
records_screened_text = records_screened_text,
records_excluded_text = records_excluded_text,
dbr_sought_reports_text = dbr_sought_reports_text,
dbr_notretrieved_reports_text = dbr_notretrieved_reports_text,
other_sought_reports_text = other_sought_reports_text,
other_notretrieved_reports_text = other_notretrieved_reports_text,
dbr_assessed_text = dbr_assessed_text,
dbr_excluded_text = dbr_excluded_text,
other_assessed_text = other_assessed_text,
other_excluded_text = other_excluded_text,
new_studies_text = new_studies_text,
new_reports_text = new_reports_text,
total_studies_text = total_studies_text,
total_reports_text = total_reports_text,
total_studies_ma_text = total_studies_ma_text,
total_reports_ma_text = total_reports_ma_text,
identification_text = identification_text,
screening_text = screening_text,
included_text = included_text,
tooltips = tooltips,
urls = urls
)
return(x)
}
#' Save PRISMA2020 flow diagram
#' @description Save the output from [PRISMA_flowdiagram()] to the
#' working directory.
#' @param plotobj A plot produced using [PRISMA_flowdiagram()].
#' @param filename The filename to save (including extension)
#' @param filetype The filetype to save the plot in, supports:
#' HTML, ZIP, PDF, PNG, SVG, PS and WEBP
#' (if NA, the filetype will be calculated out based on the file extension)
#' HTML files maintain hyperlinks and tooltips
#' The ZIP option creates an archive containing the HTML file,
#' alongside supporting javascript and css files in an adjacent folder,
#' instead of embedded base64 within the HTML file
#' @param overwrite if TRUE, will overwrite an existing file
#' @param width passed as the width argument to
#' [rsvg::rsvg()] and similar functions
#' @param height passed as the height argument to
#' [rsvg::rsvg()] and similar functions
#' @param css passed as the css argument to
#' [rsvg::rsvg()] and similar functions
#' @return the absolute filename of the saved diagram plot.
#' @examples
#' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020")
#' data <- read.csv(csvFile);
#' data <- PRISMA_data(data);
#' plot <- PRISMA_flowdiagram(data,
#' fontsize = 12,
#' interactive = TRUE,
#' previous = FALSE,
#' other = TRUE);
#' PRISMA_save(plot, filename = tempfile(), filetype="html");
#' @export
PRISMA_save <- function( #nolint
plotobj,
filename = "PRISMA2020_flowdiagram.html",
filetype = NA,
overwrite = FALSE,
width = NULL,
height = NULL,
css = NULL
) {
if (!file.exists(filename) || overwrite == TRUE) {
format_real <- PRISMA_calc_filetype_(filename, filetype) #nolint
switch(
format_real,
"HTML" = {
tmp_html <- tempfile(
pattern = "PRISMA2020_",
tmpdir = tempdir(),
fileext = ".html"
)
htmlwidgets::saveWidget(
plotobj,
file = tmp_html,
title = "PRISMA2020 Flowdiagram"
)
if (!(file.copy(tmp_html, filename, overwrite = TRUE))) {
stop("Error saving HTML")
}
file.remove(tmp_html)
},
"ZIP" = {
curr_wd <- getwd()
tmp_dir <- tempdir()
setwd(tmp_dir)
tmp_zipfile <- tempfile(
pattern = "PRISMA2020_",
tmpdir = tempdir(),
fileext = ".zip"
)
tmp_html <- paste0(
tools::file_path_sans_ext(basename(filename)),
".html"
)
tmp_libdir <- paste0(tmp_html, "_files")
htmlwidgets::saveWidget(
plotobj,
file = tmp_html,
libdir = tmp_libdir,
selfcontained = FALSE,
title = "PRISMA2020 Flowdiagram"
)
zip::zip(zipfile = tmp_zipfile, files = c(tmp_html, tmp_libdir))
setwd(curr_wd)
if (!(file.copy(paste0(tmp_zipfile), filename, overwrite = TRUE))) {
stop("Error saving ZIP File")
}
},
"PDF" = {
tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint
rsvg::rsvg_pdf(
tmp_svg,
filename,
width = width,
height = height,
css = css
)
file.remove(tmp_svg)
},
"PNG" = {
tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint
rsvg::rsvg_png(
tmp_svg,
filename,
width = width,
height = height,
css = css
)
file.remove(tmp_svg)
},
"SVG" = {
tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint
if (!(file.copy(tmp_svg, filename, overwrite = TRUE))) {
stop("Error saving SVG")
}
file.remove(tmp_svg)
},
"PS" = {
tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint
rsvg::rsvg_ps(
tmp_svg,
filename,
width = width,
height = height,
css = css
)
file.remove(tmp_svg)
},
"WEBP" = {
tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint
rsvg::rsvg_webp(
tmp_svg,
filename,
width = width,
height = height,
css = css
)
file.remove(tmp_svg)
},
stop("Please choose one of the supported file types")
)
return(tools::file_path_as_absolute(filename))
} else {
stop("File exists, please set overwite = TRUE to overwrite")
}
}
#' Defunct function - replaced by "PRISMA_interactive_"
#' @description Defunct function - replaced by "PRISMA_interactive_"
#' @seealso [PRISMA_interactive_()]
#' @param plot A plot object from [PRISMA_flowdiagram()].
#' @param urls A dataframe consisting of two columns: nodes and urls. The first
#' column should contain 19 rows for the nodes from node1 to node19. The second
#' column should contain a corresponding URL for each node.
#' @param previous Logical argument (TRUE or FALSE) (supplied through
#' [PRISMA_flowdiagram()]) specifying whether previous studies were sought.
#' @param other Logical argument (TRUE or FALSE) (supplied through
#' [PRISMA_flowdiagram()]) specifying whether other studies were sought.
sr_flow_interactive <- function(plot,
urls,
previous,
other) {
.Defunct("PRISMA_interactive_")
}
#' Defunct function - replaced by "PRISMA_data"
#' @description Defunct function - replaced by "PRISMA_data"
#' @seealso [PRISMA_data()]
#' @param data File to read in.
read_PRISMAdata <- function(data){ #nolint
.Defunct("PRISMA_data")
}
================================================
FILE: R/globals.R
================================================
utils::globalVariables(c("previous_studies",
"previous_reports",
"register_results",
"database_results",
"website_results",
"organisation_results",
"citations_results",
"duplicates",
"excluded_automatic",
"excluded_other",
"records_screened",
"records_excluded",
"dbr_sought_reports",
"dbr_notretrieved_reports",
"other_sought_reports",
"other_notretrieved_reports",
"dbr_assessed",
"dbr_excluded",
"other_assessed",
"other_excluded",
"new_studies",
"new_reports",
"total_studies",
"total_reports",
"total_studies_ma",
"total_reports_ma",
"previous_text",
"newstud_text",
"other_text",
"previous_studies_text",
"previous_reports_text",
"register_results_text",
"database_results_text",
"website_results_text",
"organisation_results_text",
"citations_results_text",
"duplicates_text",
"excluded_automatic_text",
"excluded_other_text",
"records_screened_text",
"records_excluded_text",
"dbr_sought_reports_text",
"dbr_notretrieved_reports_text",
"other_sought_reports_text",
"other_notretrieved_reports_text",
"dbr_assessed_text",
"dbr_excluded_text",
"other_assessed_text",
"other_excluded_text",
"new_studies_text",
"new_reports_text",
"total_studies_text",
"total_reports_text",
"tooltips",
"urls",
"identification_text",
"included_text",
"screening_text",
"total_studies_ma_text",
"total_reports_ma_text"))
================================================
FILE: R/utils.R
================================================
# Utility functions for PRISMA_flowdiagram
#' Calculate the correct height of a box from a list (e.g. of exclusion reasons)
#' @description Get the correct height for a box
#' @param n the number of rows of text in the label
#' @param offset the offset height (e.g. 3.5)
#' @param min the minimum number of rows before adjusting
#' @return the height of the box
#' @keywords internal
PRISMA_get_height_ <- function (n, offset, min = 2) { #nolint
lines <- n + 1
if (lines > min) {
height <- offset + (lines * 0.25) - (min * 0.25)
} else {
height <- offset
}
return(height)
}
#' Calculate the correct position of a node
#' @description Get the correct position for a node
#' @param first_box_location the location of the first node
#' @param offset the offset from the first node
#' @param length_orig the width/height of the original node
#' @param length_new the width/height of the new node
#' @param negative_offset is the offset negative (defaults to false)
#' @return the position of the node
#' @keywords internal
PRISMA_get_pos_ <- function (first_box_location, offset, length_orig, length_new, negative_offset = FALSE) { #nolint
if (negative_offset == FALSE) {
pos <- first_box_location + offset + (length_orig / 2) + (length_new / 2)
} else {
pos <- first_box_location - offset - (length_orig / 2) - (length_new / 2)
}
return(pos)
}
#' Generate / insert JS for labels
#' @description Generate the javascript method to insert the side labels
#' @param plot the plot object (without side labels)
#' @param identification_text the text to use as the "identification" label
#' @param screening_text the text to use as the "screening" label
#' @param included_text the text to use as the "identification" label
#' @return the plot object (with JS to generate side labels)
#' @keywords internal
PRISMA_insert_js_ <- function ( #nolint
plot,
identification_text,
screening_text,
included_text
) {
# This JS loops through each node, and
# locates the relevent <text> tag containing the label
# The blank label is replaced with the relevent descriptive label
# This is done in the loop as positioning due
# to rotation otherwise being difficult
# we rotate the text and adjust the position to ensure that
# the now rotated text is displayed correctly
javascript <- htmltools::HTML(paste0('
const nodeMap = new Map([["node1","',
identification_text,
'"], ["node2","',
screening_text,
'"], ["node3","',
included_text,
'"]]);
for (const [node, label] of nodeMap) {
var theDiv = document.getElementById(node);
var theText = theDiv.querySelector("text");
var attrX = theText.getAttribute("x");
var attrY = theText.getAttribute("y");
theText.setAttribute("y",parseFloat(attrX))
theText.setAttribute("x",parseFloat(attrY)*-1)
theText.setAttribute("style","transform: rotate(-90deg);")
theText.setAttribute("dominant-baseline", "middle")
theText.innerHTML = label;
}
'))
plot <- htmlwidgets::appendContent(
plot, htmlwidgets::onStaticRenderComplete(javascript)
)
return(plot)
}
#' Add the hyperlink to the given node
#'
#' @description Generate the javascript method to insert the side labels
#' @param node the relevent node
#' @param url the URL the node should link to
#' @return An interactive flow diagram plot.
#' @keywords internal
PRISMA_add_hyperlink_ <- function( #nolint
node,
url
) {
t <- paste0( #nolint
"const ",
node,
' = document.getElementById("',
node,
'");
var link',
node,
' = "<a href=\'',
url,
'\' target=\'_blank\'>" + ',
node,
'.innerHTML + "</a>";',
"\n",
node,
".innerHTML = link",
node,
";"
)
}
#' Plot interactive flow diagram for systematic reviews
#' @description Converts a PRISMA systematic review flow diagram into an
#' interactive HTML plot, for embedding links from each box.
#' @seealso [PRISMA_interactive_()]
#' @param plot A plot object from [PRISMA_flowdiagram()].
#' @param urls A dataframe consisting of two columns: nodes and urls. The first
#' column should contain 19 rows for the nodes from node1 to node19. The second
#' column should contain a corresponding URL for each node.
#' @param previous Logical argument (TRUE or FALSE) (supplied through
#' [PRISMA_flowdiagram()]) specifying whether previous studies were sought.
#' @param other Logical argument (TRUE or FALSE) (supplied through
#' [PRISMA_flowdiagram()]) specifying whether other studies were sought.
#' @return An interactive flow diagram plot.
#' @keywords internal
PRISMA_interactive_ <- function( #nolint
plot,
urls,
previous,
other
) {
if (previous == TRUE && other == TRUE) {
link <- data.frame(
boxname = c(
"identification",
"screening",
"included",
"prevstud",
"box1",
"newstud",
"box2",
"box3",
"box4",
"box5",
"box6",
"box7",
"box8",
"box9",
"box10",
"othstud",
"box11",
"box12",
"box13",
"box14",
"box15",
"box16",
"A",
"B"
),
node = paste0("node", seq(1, 24))
)
target <- c(
"node1",
"node2",
"node3",
"node4",
"node5",
"node23",
"node6",
"node7",
"node8",
"node9",
"node10",
"node11",
"node12",
"node13",
"node14",
"node15",
"node22",
"node16",
"node17",
"node18",
"node19",
"node20",
"node21",
"node24"
)
} else if (previous == FALSE && other == TRUE) {
link <- data.frame(
boxname = c(
"identification",
"screening",
"included",
"newstud",
"box2",
"box3",
"box4",
"box5",
"box6",
"box7",
"box8",
"box9",
"box10",
"othstud",
"box11",
"box12",
"box13",
"box14",
"box15",
"B"
),
node = paste0("node", seq(1, 20))
)
target <- c(
"node1",
"node2",
"node3",
"node4",
"node5",
"node6",
"node7",
"node8",
"node9",
"node10",
"node11",
"node12",
"node13",
"node14",
"node15",
"node16",
"node17",
"node18",
"node19",
"node20"
)
} else if (previous == TRUE && other == FALSE) {
link <- data.frame(
boxname = c(
"identification",
"screening",
"included",
"prevstud",
"box1",
"newstud",
"box2",
"box3",
"box4",
"box5",
"box6",
"box7",
"box8",
"box9",
"box10",
"box16",
"A"
),
node = paste0("node", seq(1, 17))
)
target <- c(
"node1",
"node2",
"node3",
"node4",
"node5",
"node6",
"node7",
"node8",
"node9",
"node10",
"node11",
"node12",
"node13",
"node14",
"node15",
"node16",
"node17"
)
} else {
link <- data.frame(
boxname = c(
"identification",
"screening",
"included",
"newstud",
"box2",
"box3",
"box4",
"box5",
"box6",
"box7",
"box8",
"box9",
"box10"
),
node = paste0("node", seq(1, 13))
)
target <- c(
"node1",
"node2",
"node3",
"node4",
"node5",
"node6",
"node7",
"node8",
"node9",
"node10",
"node11",
"node12",
"node13"
)
}
link <- merge(link, urls, by.x = "boxname", by.y = "box", all.x = TRUE)
link <- link[match(target, link$node), ]
node <- link$node
url <- link$url
#the following code adds the location link for the new window
javascript <- htmltools::HTML(
paste(
mapply(
PRISMA_add_hyperlink_,
node,
url
),
collapse = "\n"
)
)
htmlwidgets::prependContent(
plot,
htmlwidgets::onStaticRenderComplete(javascript)
)
}
#' Calculate the correct filetime
#'
#' @description Work out the correct filetype to save the file as
#' @param fn The filename (including extension)
#' @param ft The filetype (which can be NA or NULL)
#' @return the filetype taken from the filename, or overriden by the ft param
#' @keywords internal
PRISMA_calc_filetype_ <- function(fn, ft) { #nolint
# if the filetype is set, return that, otherwise
# calculate the filetype from the extension (HTM becomes HTML)
if (!is.na(ft) && !is.null(ft)) {
the_ft <- toupper(ft)
} else {
the_ft <- toupper(tools::file_ext(fn))
if (the_ft == "HTM") {
the_ft <- "HTML"
}
}
return(the_ft)
}
#' Format numbers with commas into numbers
#'
#' @description Turn strings containing numbers +/- commas into numbers
#' @param x the number to format
#' @return the number with commas removed
#' @keywords internal
PRISMA_format_number_ <- function(x) { #nolint
if (is.character(x)) {
x <- gsub(",", "", x)
x <- gsub("[^0-9.]", "", x)
}
return(as.numeric(x))
}
#' Parse an exclusion reason into a data frame
#'
#' @description Parse an exclusion reason string and returns a dataframe
#' containing reasons and number
#' @param reasons the string to parse
#' @return a dataframe containing reasons and number applicable
#' @keywords internal
#'
PRISMA_parse_reasons_ <- function(reasons) { #nolint
reasons_out <- NA
if (grepl("[^0-9,]", as.character(reasons))) {
reasons_out <- data.frame(
reason = gsub(
",.*$",
"",
unlist(
strsplit(
as.character(reasons),
split = "(;)( )?"
)
)
),
n = scales::comma(
PRISMA_format_number_( #nolint
gsub(
".*?,([ 0-9,]*)|.*()",
"\\1",
unlist(
strsplit(
as.character(reasons),
split = "(;)( )?"
)
)
)
)
)
)
} else {
reasons_out <- data.frame(
reason = "",
n = scales::comma(
PRISMA_format_number_(as.character(reasons))
)
)
}
return(reasons_out)
}
#' Formats multiple exclusion reasons properly for printing
#'
#' @description Parse an exclusion reason dataframe from
#' [PRISMA2020::PRISMA_parse_reasons_()] and returns a properly formatted string
#' @param df the dataframe to parse
#' @return a string ready for printing
#' @keywords internal
#'
PRISMA_format_reasons_ <- function(df) { #nolint
out_string <- paste0(
":",
paste(
paste(
"\n",
df[, 1],
" (n = ", df[, 2], ")",
sep = ""
),
collapse = ""
)
)
return(out_string)
}
#' Generate a temporary SVG from a plot object
#'
#' @description Generate and save a temporary SVG from a plot object
#' @param obj the plot object
#' @return the full path to the saved SVG
#' @keywords internal
PRISMA_gen_tmp_svg_ <- function(obj) { #nolint
# generate temporary filenames
tmpfilehtml <- tempfile(
pattern = "PRISMA2020_",
tmpdir = tempdir(),
fileext = ".html"
)
tmpfilesvg <- tempfile(
pattern = "PRISMA2020_",
tmpdir = tempdir(),
fileext = ".svg"
)
# save the widget as HTML and read it into a variable
htmlwidgets::saveWidget(obj, file = tmpfilehtml)
htmldata <- xml2::read_html(tmpfilehtml)
# extract our labelling javascript using xml_find_first and xpath
# it finds the first script element follwing the grViz class
# this looks to be quite fragile if we change our injected JS
js <- xml2::xml_text(
xml2::xml_find_first(
htmldata,
'//div[contains(@class, "grViz")]//following-sibling::script'
)
)
# use DiagrammeRsvg to export an SVG from the htmlwidgets code
# this uses the V8 engine in the background so takes time
# then read the SVG's XML into a variable
svg <- DiagrammeRsvg::export_svg(obj)
svg <- xml2::read_xml(svg)
# we need to extract the node names and the label values from our JS
# so find the appropriate part of the code
# (again, sensitive to script changes)
# we then extract the node names and labels and insert them into the SVG
# in a similar manner to the original JS code
jsnode <- stringr::str_split(
stringr::str_remove_all(
stringr::str_match(
js, "const nodeMap = new Map\\(\\[(.*)\\]\\);"
)[1, 2],
"\\[|\"|]"
),
",\\s",
simplify = TRUE
)
len <- length(jsnode)
for (i in 1:len) {
matsp <- stringr::str_split_fixed(jsnode[i], ",", 2)
namespace <- xml2::xml_ns(svg)
xml_text_node <- xml2::xml_find_first(
svg,
paste0('//d1:g[@id="', matsp[, 1], '"]//d1:text'),
namespace
)
attr_x <- xml2::xml_attr(xml_text_node, "x")
attr_y <- xml2::xml_attr(xml_text_node, "y")
xml2::xml_attr(xml_text_node, "x") <- as.double(attr_y) * -1
xml2::xml_attr(xml_text_node, "y") <- as.double(attr_x) + 2
# libRSVG does not support css transforms
# so we need to use the native SVG transform attribute
xml2::xml_attr(xml_text_node, "transform") <- "rotate(-90)"
xml2::xml_text(xml_text_node) <- matsp[, 2]
}
xml2::write_xml(svg, file = tmpfilesvg)
return(tmpfilesvg)
}
================================================
FILE: README.md
================================================
<!-- badges: start -->
[](https://github.com/prisma-flowdiagram/PRISMA2020/actions/workflows/check-and-deploy.yml)


[](https://doi.org/10.5281/zenodo.4287834)
[](https://doi.org/10.1002/cl2.1230)
[](https://ko-fi.com/O5O4JQJ5W)
<!-- badges: end -->
# PRISMA2020 Flow Diagram <img src="https://raw.githubusercontent.com/prisma-flowdiagram/PRISMA2020/master/PRISMA2020-hex.png" align="right" width="15%"/>
You can use this package to produce a flow diagram that conforms to the PRISMA 2020 standards using the `PRISMA_flowdiagram()` function. The data can be manually entered into the function, or loaded up using the template CSV file provided in 'INST/EXTDATA/'. The function, (if 'interactive = TRUE') produces an interactive HTML ouput with each box linking to a specific page (e.g. of search results or methods details), and hover-over tooltips for further information.
<br>
<img src="https://raw.githubusercontent.com/prisma-flowdiagram/PRISMA2020/master/inst/extdata/PRISMA.png" width="70%" />
<br>
The 'Previous' and 'Other' study arms of the flowchart can be toggled on and off and removed or added to the diagram by specifying this in the function inputs.
A static version is produced otherwise.
<a href="https://srflowdiagram.github.io/template.html" target="_blank">See the interactive template here.</a><br>
<a href="https://estech.shinyapps.io/prisma_flowdiagram/" target="_blank">Visit the web-based Shiny app for a point-and-click user interface here.</a>
---
## Docker Installation
You can quickly install the PRISMA2020 package and run the included
example shinyapp using [Docker](https://docs.docker.com/engine/install/).
```bash
docker build . -t prisma-shiny:1
docker run -it --rm -p 3838:3838 prisma-shiny:1
```
Then visit http://localhost:3838/app in your web browser.
To stop the app, press `Ctrl+C` in the terminal.
---
Please cite as:<br>
Haddaway, N. R., Page, M. J., Pritchard, C. C., & McGuinness, L. A. (2022). PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis. Campbell Systematic Reviews, 18, e1230. <a href=https://doi.org/10.1002/cl2.1230>https://doi.org/10.1002/cl2.1230</a><br>
<a id="raw-url" href="https://raw.githubusercontent.com/nealhaddaway/PRISMA2020/master/inst/extdata/citation.ris">Citation in .ris format (right click 'Save Link As')</a>
================================================
FILE: code-of-conduct.md
================================================
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or
imagery, derogatory comments or personal attacks, trolling, public or private harassment,
insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
opening an issue, emailing the project lead , Neal Haddaway (<nealhaddaway@gmail.com>), or one of the core developers.
This Code of Conduct is adapted from the Contributor Covenant
(http://contributor-covenant.org), version 1.0.0, available at
http://contributor-covenant.org/version/1/0/0/
================================================
FILE: contributing.md
================================================
# Contributing to PRISMA2020
The goal of this guide is to help you get up and contributing to PRISMA2020 as
quickly as possible.
Please note that PRISMA2020 is released with a [Contributor Code of Conduct](code-of-conduct.md). By contributing to this project, you agree to abide by its terms.
### Fixing typos
Small typos or grammatical errors in documentation may be edited directly using
the GitHub web interface, so long as the changes are made in the _source_ file.
* DO: edit a roxygen comment in a `.R` file below `R/`.
* DO NOT: edit an `.Rd` file below `man/`.
Similarly, typos/errors in the supporting documents (e.g. these contributing guidelines, code-of-conduct.md)
may be edited directly using the GitHub web interface.
The exception to the above is when typos/errors occur in the README.md document. To correct these:
* Fork the respository to your personal GitHub account
* Edit the README.Rmd document to fix the error
* Knit the README.Rmd document to produce the corrected README.md
* Commit changes and push both corrected documents to your forked respository
* Issue a pull request (see the section on [Pull Requests](#pull-requests), below).
### Filing an issue
When filing an issue, the most important thing is to include a minimal
reproducible example so that we can quickly verify the problem, and then figure
out how to fix it. See "[Writing a good reproducible example](https://reprex.tidyverse.org/articles/reprex-dos-and-donts.html)".
1. **Packages** should be loaded at the top of the script, so it's easy to
see which ones the example needs.
1. Spend a little bit of time ensuring that your **code** is easy for others to
read:
* make sure you've used spaces and your variable names are concise, but
informative
* use comments generously to indicate where your problem lies
* do your best to remove everything that is not related to the problem.
You can check you have actually made a reproducible example by starting up a
fresh R session and pasting your script in.
### Pull Requests
#### Getting Started
* Make sure you have a [GitHub account](https://github.com/signup/free).
* Familiarise yourself with Git and Github, using the [resources](#additional-resources) at the end of this page.
#### Prerequisites
Before you make a substantial pull request, you should always file an issue and
make sure someone from the team agrees that it’s a problem. If you’ve found a
bug, create an associated issue and illustrate the bug with a minimal
[reproducible example](https://reprex.tidyverse.org/articles/reprex-dos-and-donts.html).
#### Pull request process
* We recommend that you create a Git branch for each pull request (PR).
* Look at the Travis and AppVeyor build status before and after making changes.
The `README` should contain badges for any continuous integration services used
by the package.
* New code should follow the tidyverse [style guide](http://style.tidyverse.org).
You can use the [styler](https://CRAN.R-project.org/package=styler) package to
apply these styles, but please don't restyle code that has nothing to do with
your PR.
* We use [roxygen2](https://cran.r-project.org/package=roxygen2), with
[Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/markdown.html),
for documentation.
* We use [testthat](https://cran.r-project.org/package=testthat). Contributions
with test cases included are easier to accept.
### Additional Resources
* The [Git and Github](http://r-pkgs.had.co.nz/git.html) section of the __R Packages__ book by Hadley Wickham
* [Happy Git and GitHub for the useR](https://happygitwithr.com/)
* General GitHub [documentation](https://help.github.com/)
* GitHub pull request [documentation]](https://help.github.com/articles/creating-a-pull-request/)
================================================
FILE: inst/CITATION
================================================
bibentry(
header = "To cite PRISMA2020 in publications, please use:",
bibtype = "Article",
title = "PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis",
author = "Neal R Haddaway, Matthew J Page, Chris C Pritchard and Luke A McGuinness",
year = "2022",
doi = "10.1002/cl2.1230",
journal = "Campbell Systematic Reviews",
number = "2",
volume = "18",
pages = "e1230",
)
================================================
FILE: inst/extdata/PRISMA.csv
================================================
data,node,box,description,boxtext,tooltips,url,n
NA,node4,prevstud,Grey title box; Previous studies,Previous studies,Grey title box; Previous studies,prevstud.html,0
previous_studies,node5,box1,Studies included in previous version of review,Studies included in previous version of review,Studies included in previous version of review,previous_studies.html,0
previous_reports,NA,box1,Reports of studies included in previous version of review,Reports of studies included in previous version of review,NA,previous_reports.html,0
NA,node6,newstud,Yellow title box; Identification of new studies via databases and registers,Identification of new studies via databases and registers,Yellow title box; Identification of new studies via databases and registers,newstud.html,0
database_results,node7,box2,Records identified from: Databases,Databases,Records identified from: Databases and Registers,database_results.html,0
database_specific_results,NA,box2,Records identified from: specific databases,Specific Databases,NA,database_results.html,"Database 1, xxx; Database 2, xxx; Database 3, xxx"
register_results,NA,box2,Records identified from: Registers,Registers,NA,NA,0
register_specific_results,NA,box2,Records identified from: specific registers,Specific Registers,NA,database_results.html,"Register 1, xxx; Register 2, xxx; Register 3, xxx"
NA,node16,othstud,Grey title box; Identification of new studies via other methods,Identification of new studies via other methods,Grey title box; Identification of new studies via other methods,othstud.html,0
website_results,node17,box11,Records identified from: Websites,Websites,"Records identified from: Websites, Organisations and Citation Searching",website_results.html,0
organisation_results,,box11,Records identified from: Organisations,Organisations,NA,NA,0
citations_results,NA,box11,Records identified from: Citation searching,Citation searching,NA,NA,0
duplicates,node8,box3,Duplicate records,Duplicate records,Duplicate records,duplicates.html,0
excluded_automatic,NA,box3,Records marked as ineligible by automation tools,Records marked as ineligible by automation tools,NA,NA,0
excluded_other,NA,box3,Records removed for other reasons,Records removed for other reasons,NA,NA,0
records_screened,node9,box4,Records screened (databases and registers),Records screened,Records screened (databases and registers),records_screened.html,0
records_excluded,node10,box5,Records excluded (databases and registers),Records excluded,Records excluded (databases and registers),records_excluded.html,0
dbr_sought_reports,node11,box6,Reports sought for retrieval (databases and registers),Reports sought for retrieval,Reports sought for retrieval (databases and registers),dbr_sought_reports.html,0
dbr_notretrieved_reports,node12,box7,Reports not retrieved (databases and registers),Reports not retrieved,Reports not retrieved (databases and registers),dbr_notretrieved_reports.html,0
other_sought_reports,node18,box12,Reports sought for retrieval (other),Reports sought for retrieval,Reports sought for retrieval (other),other_sought_reports.html,0
other_notretrieved_reports,node19,box13,Reports not retrieved (other),Reports not retrieved,Reports not retrieved (other),other_notretrieved_reports.html,0
dbr_assessed,node13,box8,Reports assessed for eligibility (databases and registers),Reports assessed for eligibility,Reports assessed for eligibility (databases and registers),dbr_assessed.html,0
dbr_excluded,node14,box9,"Reports excluded (databases and registers): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (databases and registers),dbrexcludedrecords.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx"
other_assessed,node20,box14,Reports assessed for eligibility (other),Reports assessed for eligibility,Reports assessed for eligibility (other),other_assessed.html,0
other_excluded,node21,box15,"Reports excluded (other): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (other),other_excluded.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx"
new_studies,node15,box10,New studies included in review,New studies included in review,New studies included in review,new_studies.html,0
new_reports,NA,box10,Reports of new included studies,Reports of new included studies,NA,NA,0
total_studies,node22,box16,Total studies included in review,Total studies included in review,Total studies included in review,total_studies.html,0
total_reports,NA,box16,Reports of total included studies,Reports of total included studies,NA,NA,0
identification,node1,identification,Blue identification box,Identification,Blue identification box,identification.html,0
screening,node2,screening,Blue screening box,Screening,Blue screening box,screening.html,0
included,node3,included,Blue included box,Included,Blue included box,included.html,0
total_studies_ma,node23,box17,Total studies included in meta-analysis,Total studies included in meta-analysis,Total studies included in meta-analysis,total_studies_meta_analysis.html,0
total_reports_ma,NA,box17,Reports of total included studies in meta-analysis,Reports of total included studies in meta-analysis,NA,NA,0
================================================
FILE: inst/extdata/Template.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>body{background-color:white;}</style>
<script>(function() {
// If window.HTMLWidgets is already defined, then use it; otherwise create a
// new object. This allows preceding code to set options that affect the
// initialization process (though none currently exist).
window.HTMLWidgets = window.HTMLWidgets || {};
// See if we're running in a viewer pane. If not, we're in a web browser.
var viewerMode = window.HTMLWidgets.viewerMode =
/\bviewer_pane=1\b/.test(window.location);
// See if we're running in Shiny mode. If not, it's a static document.
// Note that static widgets can appear in both Shiny and static modes, but
// obviously, Shiny widgets can only appear in Shiny apps/documents.
var shinyMode = window.HTMLWidgets.shinyMode =
typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings;
// We can't count on jQuery being available, so we implement our own
// version if necessary.
function querySelectorAll(scope, selector) {
if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) {
return scope.find(selector);
}
if (scope.querySelectorAll) {
return scope.querySelectorAll(selector);
}
}
function asArray(value) {
if (value === null)
return [];
if ($.isArray(value))
return value;
return [value];
}
// Implement jQuery's extend
function extend(target /*, ... */) {
if (arguments.length == 1) {
return target;
}
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
}
return target;
}
// IE8 doesn't support Array.forEach.
function forEach(values, callback, thisArg) {
if (values.forEach) {
values.forEach(callback, thisArg);
} else {
for (var i = 0; i < values.length; i++) {
callback.call(thisArg, values[i], i, values);
}
}
}
// Replaces the specified method with the return value of funcSource.
//
// Note that funcSource should not BE the new method, it should be a function
// that RETURNS the new method. funcSource receives a single argument that is
// the overridden method, it can be called from the new method. The overridden
// method can be called like a regular function, it has the target permanently
// bound to it so "this" will work correctly.
function overrideMethod(target, methodName, funcSource) {
var superFunc = target[methodName] || function() {};
var superFuncBound = function() {
return superFunc.apply(target, arguments);
};
target[methodName] = funcSource(superFuncBound);
}
// Add a method to delegator that, when invoked, calls
// delegatee.methodName. If there is no such method on
// the delegatee, but there was one on delegator before
// delegateMethod was called, then the original version
// is invoked instead.
// For example:
//
// var a = {
// method1: function() { console.log('a1'); }
// method2: function() { console.log('a2'); }
// };
// var b = {
// method1: function() { console.log('b1'); }
// };
// delegateMethod(a, b, "method1");
// delegateMethod(a, b, "method2");
// a.method1();
// a.method2();
//
// The output would be "b1", "a2".
function delegateMethod(delegator, delegatee, methodName) {
var inherited = delegator[methodName];
delegator[methodName] = function() {
var target = delegatee;
var method = delegatee[methodName];
// The method doesn't exist on the delegatee. Instead,
// call the method on the delegator, if it exists.
if (!method) {
target = delegator;
method = inherited;
}
if (method) {
return method.apply(target, arguments);
}
};
}
// Implement a vague facsimilie of jQuery's data method
function elementData(el, name, value) {
if (arguments.length == 2) {
return el["htmlwidget_data_" + name];
} else if (arguments.length == 3) {
el["htmlwidget_data_" + name] = value;
return el;
} else {
throw new Error("Wrong number of arguments for elementData: " +
arguments.length);
}
}
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function hasClass(el, className) {
var re = new RegExp("\\b" + escapeRegExp(className) + "\\b");
return re.test(el.className);
}
// elements - array (or array-like object) of HTML elements
// className - class name to test for
// include - if true, only return elements with given className;
// if false, only return elements *without* given className
function filterByClass(elements, className, include) {
var results = [];
for (var i = 0; i < elements.length; i++) {
if (hasClass(elements[i], className) == include)
results.push(elements[i]);
}
return results;
}
function on(obj, eventName, func) {
if (obj.addEventListener) {
obj.addEventListener(eventName, func, false);
} else if (obj.attachEvent) {
obj.attachEvent(eventName, func);
}
}
function off(obj, eventName, func) {
if (obj.removeEventListener)
obj.removeEventListener(eventName, func, false);
else if (obj.detachEvent) {
obj.detachEvent(eventName, func);
}
}
// Translate array of values to top/right/bottom/left, as usual with
// the "padding" CSS property
// https://developer.mozilla.org/en-US/docs/Web/CSS/padding
function unpackPadding(value) {
if (typeof(value) === "number")
value = [value];
if (value.length === 1) {
return {top: value[0], right: value[0], bottom: value[0], left: value[0]};
}
if (value.length === 2) {
return {top: value[0], right: value[1], bottom: value[0], left: value[1]};
}
if (value.length === 3) {
return {top: value[0], right: value[1], bottom: value[2], left: value[1]};
}
if (value.length === 4) {
return {top: value[0], right: value[1], bottom: value[2], left: value[3]};
}
}
// Convert an unpacked padding object to a CSS value
function paddingToCss(paddingObj) {
return paddingObj.top + "px " + paddingObj.right + "px " + paddingObj.bottom + "px " + paddingObj.left + "px";
}
// Makes a number suitable for CSS
function px(x) {
if (typeof(x) === "number")
return x + "px";
else
return x;
}
// Retrieves runtime widget sizing information for an element.
// The return value is either null, or an object with fill, padding,
// defaultWidth, defaultHeight fields.
function sizingPolicy(el) {
var sizingEl = document.querySelector("script[data-for='" + el.id + "'][type='application/htmlwidget-sizing']");
if (!sizingEl)
return null;
var sp = JSON.parse(sizingEl.textContent || sizingEl.text || "{}");
if (viewerMode) {
return sp.viewer;
} else {
return sp.browser;
}
}
// @param tasks Array of strings (or falsy value, in which case no-op).
// Each element must be a valid JavaScript expression that yields a
// function. Or, can be an array of objects with "code" and "data"
// properties; in this case, the "code" property should be a string
// of JS that's an expr that yields a function, and "data" should be
// an object that will be added as an additional argument when that
// function is called.
// @param target The object that will be "this" for each function
// execution.
// @param args Array of arguments to be passed to the functions. (The
// same arguments will be passed to all functions.)
function evalAndRun(tasks, target, args) {
if (tasks) {
forEach(tasks, function(task) {
var theseArgs = args;
if (typeof(task) === "object") {
theseArgs = theseArgs.concat([task.data]);
task = task.code;
}
var taskFunc = tryEval(task);
if (typeof(taskFunc) !== "function") {
throw new Error("Task must be a function! Source:\n" + task);
}
taskFunc.apply(target, theseArgs);
});
}
}
// Attempt eval() both with and without enclosing in parentheses.
// Note that enclosing coerces a function declaration into
// an expression that eval() can parse
// (otherwise, a SyntaxError is thrown)
function tryEval(code) {
var result = null;
try {
result = eval(code);
} catch(error) {
if (!error instanceof SyntaxError) {
throw error;
}
try {
result = eval("(" + code + ")");
} catch(e) {
if (e instanceof SyntaxError) {
throw error;
} else {
throw e;
}
}
}
return result;
}
function initSizing(el) {
var sizing = sizingPolicy(el);
if (!sizing)
return;
var cel = document.getElementById("htmlwidget_container");
if (!cel)
return;
if (typeof(sizing.padding) !== "undefined") {
document.body.style.margin = "0";
document.body.style.padding = paddingToCss(unpackPadding(sizing.padding));
}
if (sizing.fill) {
document.body.style.overflow = "hidden";
document.body.style.width = "100%";
document.body.style.height = "100%";
document.documentElement.style.width = "100%";
document.documentElement.style.height = "100%";
if (cel) {
cel.style.position = "absolute";
var pad = unpackPadding(sizing.padding);
cel.style.top = pad.top + "px";
cel.style.right = pad.right + "px";
cel.style.bottom = pad.bottom + "px";
cel.style.left = pad.left + "px";
el.style.width = "100%";
el.style.height = "100%";
}
return {
getWidth: function() { return cel.offsetWidth; },
getHeight: function() { return cel.offsetHeight; }
};
} else {
el.style.width = px(sizing.width);
el.style.height = px(sizing.height);
return {
getWidth: function() { return el.offsetWidth; },
getHeight: function() { return el.offsetHeight; }
};
}
}
// Default implementations for methods
var defaults = {
find: function(scope) {
return querySelectorAll(scope, "." + this.name);
},
renderError: function(el, err) {
var $el = $(el);
this.clearError(el);
// Add all these error classes, as Shiny does
var errClass = "shiny-output-error";
if (err.type !== null) {
// use the classes of the error condition as CSS class names
errClass = errClass + " " + $.map(asArray(err.type), function(type) {
return errClass + "-" + type;
}).join(" ");
}
errClass = errClass + " htmlwidgets-error";
// Is el inline or block? If inline or inline-block, just display:none it
// and add an inline error.
var display = $el.css("display");
$el.data("restore-display-mode", display);
if (display === "inline" || display === "inline-block") {
$el.hide();
if (err.message !== "") {
var errorSpan = $("<span>").addClass(errClass);
errorSpan.text(err.message);
$el.after(errorSpan);
}
} else if (display === "block") {
// If block, add an error just after the el, set visibility:none on the
// el, and position the error to be on top of the el.
// Mark it with a unique ID and CSS class so we can remove it later.
$el.css("visibility", "hidden");
if (err.message !== "") {
var errorDiv = $("<div>").addClass(errClass).css("position", "absolute")
.css("top", el.offsetTop)
.css("left", el.offsetLeft)
// setting width can push out the page size, forcing otherwise
// unnecessary scrollbars to appear and making it impossible for
// the element to shrink; so use max-width instead
.css("maxWidth", el.offsetWidth)
.css("height", el.offsetHeight);
errorDiv.text(err.message);
$el.after(errorDiv);
// Really dumb way to keep the size/position of the error in sync with
// the parent element as the window is resized or whatever.
var intId = setInterval(function() {
if (!errorDiv[0].parentElement) {
clearInterval(intId);
return;
}
errorDiv
.css("top", el.offsetTop)
.css("left", el.offsetLeft)
.css("maxWidth", el.offsetWidth)
.css("height", el.offsetHeight);
}, 500);
}
}
},
clearError: function(el) {
var $el = $(el);
var display = $el.data("restore-display-mode");
$el.data("restore-display-mode", null);
if (display === "inline" || display === "inline-block") {
if (display)
$el.css("display", display);
$(el.nextSibling).filter(".htmlwidgets-error").remove();
} else if (display === "block"){
$el.css("visibility", "inherit");
$(el.nextSibling).filter(".htmlwidgets-error").remove();
}
},
sizing: {}
};
// Called by widget bindings to register a new type of widget. The definition
// object can contain the following properties:
// - name (required) - A string indicating the binding name, which will be
// used by default as the CSS classname to look for.
// - initialize (optional) - A function(el) that will be called once per
// widget element; if a value is returned, it will be passed as the third
// value to renderValue.
// - renderValue (required) - A function(el, data, initValue) that will be
// called with data. Static contexts will cause this to be called once per
// element; Shiny apps will cause this to be called multiple times per
// element, as the data changes.
window.HTMLWidgets.widget = function(definition) {
if (!definition.name) {
throw new Error("Widget must have a name");
}
if (!definition.type) {
throw new Error("Widget must have a type");
}
// Currently we only support output widgets
if (definition.type !== "output") {
throw new Error("Unrecognized widget type '" + definition.type + "'");
}
// TODO: Verify that .name is a valid CSS classname
// Support new-style instance-bound definitions. Old-style class-bound
// definitions have one widget "object" per widget per type/class of
// widget; the renderValue and resize methods on such widget objects
// take el and instance arguments, because the widget object can't
// store them. New-style instance-bound definitions have one widget
// object per widget instance; the definition that's passed in doesn't
// provide renderValue or resize methods at all, just the single method
// factory(el, width, height)
// which returns an object that has renderValue(x) and resize(w, h).
// This enables a far more natural programming style for the widget
// author, who can store per-instance state using either OO-style
// instance fields or functional-style closure variables (I guess this
// is in contrast to what can only be called C-style pseudo-OO which is
// what we required before).
if (definition.factory) {
definition = createLegacyDefinitionAdapter(definition);
}
if (!definition.renderValue) {
throw new Error("Widget must have a renderValue function");
}
// For static rendering (non-Shiny), use a simple widget registration
// scheme. We also use this scheme for Shiny apps/documents that also
// contain static widgets.
window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || [];
// Merge defaults into the definition; don't mutate the original definition.
var staticBinding = extend({}, defaults, definition);
overrideMethod(staticBinding, "find", function(superfunc) {
return function(scope) {
var results = superfunc(scope);
// Filter out Shiny outputs, we only want the static kind
return filterByClass(results, "html-widget-output", false);
};
});
window.HTMLWidgets.widgets.push(staticBinding);
if (shinyMode) {
// Shiny is running. Register the definition with an output binding.
// The definition itself will not be the output binding, instead
// we will make an output binding object that delegates to the
// definition. This is because we foolishly used the same method
// name (renderValue) for htmlwidgets definition and Shiny bindings
// but they actually have quite different semantics (the Shiny
// bindings receive data that includes lots of metadata that it
// strips off before calling htmlwidgets renderValue). We can't
// just ignore the difference because in some widgets it's helpful
// to call this.renderValue() from inside of resize(), and if
// we're not delegating, then that call will go to the Shiny
// version instead of the htmlwidgets version.
// Merge defaults with definition, without mutating either.
var bindingDef = extend({}, defaults, definition);
// This object will be our actual Shiny binding.
var shinyBinding = new Shiny.OutputBinding();
// With a few exceptions, we'll want to simply use the bindingDef's
// version of methods if they are available, otherwise fall back to
// Shiny's defaults. NOTE: If Shiny's output bindings gain additional
// methods in the future, and we want them to be overrideable by
// HTMLWidget binding definitions, then we'll need to add them to this
// list.
delegateMethod(shinyBinding, bindingDef, "getId");
delegateMethod(shinyBinding, bindingDef, "onValueChange");
delegateMethod(shinyBinding, bindingDef, "onValueError");
delegateMethod(shinyBinding, bindingDef, "renderError");
delegateMethod(shinyBinding, bindingDef, "clearError");
delegateMethod(shinyBinding, bindingDef, "showProgress");
// The find, renderValue, and resize are handled differently, because we
// want to actually decorate the behavior of the bindingDef methods.
shinyBinding.find = function(scope) {
var results = bindingDef.find(scope);
// Only return elements that are Shiny outputs, not static ones
var dynamicResults = results.filter(".html-widget-output");
// It's possible that whatever caused Shiny to think there might be
// new dynamic outputs, also caused there to be new static outputs.
// Since there might be lots of different htmlwidgets bindings, we
// schedule execution for later--no need to staticRender multiple
// times.
if (results.length !== dynamicResults.length)
scheduleStaticRender();
return dynamicResults;
};
// Wrap renderValue to handle initialization, which unfortunately isn't
// supported natively by Shiny at the time of this writing.
shinyBinding.renderValue = function(el, data) {
Shiny.renderDependencies(data.deps);
// Resolve strings marked as javascript literals to objects
if (!(data.evals instanceof Array)) data.evals = [data.evals];
for (var i = 0; data.evals && i < data.evals.length; i++) {
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]);
}
if (!bindingDef.renderOnNullValue) {
if (data.x === null) {
el.style.visibility = "hidden";
return;
} else {
el.style.visibility = "inherit";
}
}
if (!elementData(el, "initialized")) {
initSizing(el);
elementData(el, "initialized", true);
if (bindingDef.initialize) {
var result = bindingDef.initialize(el, el.offsetWidth,
el.offsetHeight);
elementData(el, "init_result", result);
}
}
bindingDef.renderValue(el, data.x, elementData(el, "init_result"));
evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]);
};
// Only override resize if bindingDef implements it
if (bindingDef.resize) {
shinyBinding.resize = function(el, width, height) {
// Shiny can call resize before initialize/renderValue have been
// called, which doesn't make sense for widgets.
if (elementData(el, "initialized")) {
bindingDef.resize(el, width, height, elementData(el, "init_result"));
}
};
}
Shiny.outputBindings.register(shinyBinding, bindingDef.name);
}
};
var scheduleStaticRenderTimerId = null;
function scheduleStaticRender() {
if (!scheduleStaticRenderTimerId) {
scheduleStaticRenderTimerId = setTimeout(function() {
scheduleStaticRenderTimerId = null;
window.HTMLWidgets.staticRender();
}, 1);
}
}
// Render static widgets after the document finishes loading
// Statically render all elements that are of this widget's class
window.HTMLWidgets.staticRender = function() {
var bindings = window.HTMLWidgets.widgets || [];
forEach(bindings, function(binding) {
var matches = binding.find(document.documentElement);
forEach(matches, function(el) {
var sizeObj = initSizing(el, binding);
if (hasClass(el, "html-widget-static-bound"))
return;
el.className = el.className + " html-widget-static-bound";
var initResult;
if (binding.initialize) {
initResult = binding.initialize(el,
sizeObj ? sizeObj.getWidth() : el.offsetWidth,
sizeObj ? sizeObj.getHeight() : el.offsetHeight
);
elementData(el, "init_result", initResult);
}
if (binding.resize) {
var lastSize = {
w: sizeObj ? sizeObj.getWidth() : el.offsetWidth,
h: sizeObj ? sizeObj.getHeight() : el.offsetHeight
};
var resizeHandler = function(e) {
var size = {
w: sizeObj ? sizeObj.getWidth() : el.offsetWidth,
h: sizeObj ? sizeObj.getHeight() : el.offsetHeight
};
if (size.w === 0 && size.h === 0)
return;
if (size.w === lastSize.w && size.h === lastSize.h)
return;
lastSize = size;
binding.resize(el, size.w, size.h, initResult);
};
on(window, "resize", resizeHandler);
// This is needed for cases where we're running in a Shiny
// app, but the widget itself is not a Shiny output, but
// rather a simple static widget. One example of this is
// an rmarkdown document that has runtime:shiny and widget
// that isn't in a render function. Shiny only knows to
// call resize handlers for Shiny outputs, not for static
// widgets, so we do it ourselves.
if (window.jQuery) {
window.jQuery(document).on(
"shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets",
resizeHandler
);
window.jQuery(document).on(
"hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets",
resizeHandler
);
}
// This is needed for the specific case of ioslides, which
// flips slides between display:none and display:block.
// Ideally we would not have to have ioslide-specific code
// here, but rather have ioslides raise a generic event,
// but the rmarkdown package just went to CRAN so the
// window to getting that fixed may be long.
if (window.addEventListener) {
// It's OK to limit this to window.addEventListener
// browsers because ioslides itself only supports
// such browsers.
on(document, "slideenter", resizeHandler);
on(document, "slideleave", resizeHandler);
}
}
var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']");
if (scriptData) {
var data = JSON.parse(scriptData.textContent || scriptData.text);
// Resolve strings marked as javascript literals to objects
if (!(data.evals instanceof Array)) data.evals = [data.evals];
for (var k = 0; data.evals && k < data.evals.length; k++) {
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]);
}
binding.renderValue(el, data.x, initResult);
evalAndRun(data.jsHooks.render, initResult, [el, data.x]);
}
});
});
invokePostRenderHandlers();
}
function has_jQuery3() {
if (!window.jQuery) {
return false;
}
var $version = window.jQuery.fn.jquery;
var $major_version = parseInt($version.split(".")[0]);
return $major_version >= 3;
}
/*
/ Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
/ on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
/ really means $(setTimeout(fn)).
/ https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
/
/ Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
/ one tick later than it did before, which means staticRender() is
/ called renderValue() earlier than (advanced) widget authors might be expecting.
/ https://github.com/rstudio/shiny/issues/2630
/
/ For a concrete example, leaflet has some methods (e.g., updateBounds)
/ which reference Shiny methods registered in initShiny (e.g., setInputValue).
/ Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
/ delay execution of those methods (until Shiny methods are ready)
/ https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
/
/ Ideally widget authors wouldn't need to use this setTimeout() hack that
/ leaflet uses to call Shiny methods on a staticRender(). In the long run,
/ the logic initShiny should be broken up so that method registration happens
/ right away, but binding happens later.
*/
function maybeStaticRenderLater() {
if (shinyMode && has_jQuery3()) {
window.jQuery(window.HTMLWidgets.staticRender);
} else {
window.HTMLWidgets.staticRender();
}
}
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", function() {
document.removeEventListener("DOMContentLoaded", arguments.callee, false);
maybeStaticRenderLater();
}, false);
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", arguments.callee);
maybeStaticRenderLater();
}
});
}
window.HTMLWidgets.getAttachmentUrl = function(depname, key) {
// If no key, default to the first item
if (typeof(key) === "undefined")
key = 1;
var link = document.getElementById(depname + "-" + key + "-attachment");
if (!link) {
throw new Error("Attachment " + depname + "/" + key + " not found in document");
}
return link.getAttribute("href");
};
window.HTMLWidgets.dataframeToD3 = function(df) {
var names = [];
var length;
for (var name in df) {
if (df.hasOwnProperty(name))
names.push(name);
if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") {
throw new Error("All fields must be arrays");
} else if (typeof(length) !== "undefined" && length !== df[name].length) {
throw new Error("All fields must be arrays of the same length");
}
length = df[name].length;
}
var results = [];
var item;
for (var row = 0; row < length; row++) {
item = {};
for (var col = 0; col < names.length; col++) {
item[names[col]] = df[names[col]][row];
}
results.push(item);
}
return results;
};
window.HTMLWidgets.transposeArray2D = function(array) {
if (array.length === 0) return array;
var newArray = array[0].map(function(col, i) {
return array.map(function(row) {
return row[i]
})
});
return newArray;
};
// Split value at splitChar, but allow splitChar to be escaped
// using escapeChar. Any other characters escaped by escapeChar
// will be included as usual (including escapeChar itself).
function splitWithEscape(value, splitChar, escapeChar) {
var results = [];
var escapeMode = false;
var currentResult = "";
for (var pos = 0; pos < value.length; pos++) {
if (!escapeMode) {
if (value[pos] === splitChar) {
results.push(currentResult);
currentResult = "";
} else if (value[pos] === escapeChar) {
escapeMode = true;
} else {
currentResult += value[pos];
}
} else {
currentResult += value[pos];
escapeMode = false;
}
}
if (currentResult !== "") {
results.push(currentResult);
}
return results;
}
// Function authored by Yihui/JJ Allaire
window.HTMLWidgets.evaluateStringMember = function(o, member) {
var parts = splitWithEscape(member, '.', '\\');
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
// part may be a character or 'numeric' member name
if (o !== null && typeof o === "object" && part in o) {
if (i == (l - 1)) { // if we are at the end of the line then evalulate
if (typeof o[part] === "string")
o[part] = tryEval(o[part]);
} else { // otherwise continue to next embedded object
o = o[part];
}
}
}
};
// Retrieve the HTMLWidget instance (i.e. the return value of an
// HTMLWidget binding's initialize() or factory() function)
// associated with an element, or null if none.
window.HTMLWidgets.getInstance = function(el) {
return elementData(el, "init_result");
};
// Finds the first element in the scope that matches the selector,
// and returns the HTMLWidget instance (i.e. the return value of
// an HTMLWidget binding's initialize() or factory() function)
// associated with that element, if any. If no element matches the
// selector, or the first matching element has no HTMLWidget
// instance associated with it, then null is returned.
//
// The scope argument is optional, and defaults to window.document.
window.HTMLWidgets.find = function(scope, selector) {
if (arguments.length == 1) {
selector = scope;
scope = document;
}
var el = scope.querySelector(selector);
if (el === null) {
return null;
} else {
return window.HTMLWidgets.getInstance(el);
}
};
// Finds all elements in the scope that match the selector, and
// returns the HTMLWidget instances (i.e. the return values of
// an HTMLWidget binding's initialize() or factory() function)
// associated with the elements, in an array. If elements that
// match the selector don't have an associated HTMLWidget
// instance, the returned array will contain nulls.
//
// The scope argument is optional, and defaults to window.document.
window.HTMLWidgets.findAll = function(scope, selector) {
if (arguments.length == 1) {
selector = scope;
scope = document;
}
var nodes = scope.querySelectorAll(selector);
var results = [];
for (var i = 0; i < nodes.length; i++) {
results.push(window.HTMLWidgets.getInstance(nodes[i]));
}
return results;
};
var postRenderHandlers = [];
function invokePostRenderHandlers() {
while (postRenderHandlers.length) {
var handler = postRenderHandlers.shift();
if (handler) {
handler();
}
}
}
// Register the given callback function to be invoked after the
// next time static widgets are rendered.
window.HTMLWidgets.addPostRenderHandler = function(callback) {
postRenderHandlers.push(callback);
};
// Takes a new-style instance-bound definition, and returns an
// old-style class-bound definition. This saves us from having
// to rewrite all the logic in this file to accomodate both
// types of definitions.
function createLegacyDefinitionAdapter(defn) {
var result = {
name: defn.name,
type: defn.type,
initialize: function(el, width, height) {
return defn.factory(el, width, height);
},
renderValue: function(el, x, instance) {
return instance.renderValue(x);
},
resize: function(el, width, height, instance) {
return instance.resize(width, height);
}
};
if (defn.find)
result.find = defn.find;
if (defn.renderError)
result.renderError = defn.renderError;
if (defn.clearError)
result.clearError = defn.clearError;
return result;
}
})();
</script>
<script>/*
Viz.js 1.8.2 (Graphviz 2.40.1, Expat 2.2.5, Emscripten 1.37.33)
Copyright (c) 2014-2018 Michael Daines
Licensed under MIT license
This distribution contains other software in object code form:
Graphviz
Licensed under Eclipse Public License - v 1.0
http://www.graphviz.org
Expat
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
Licensed under MIT license
http://www.libexpat.org
zlib
Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
http://www.zlib.net/zlib_license.html
*/
(function(global) {
var Module = function(Module) {
Module = Module || {};
var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw toThrow});Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER}if(ENVIRONMENT_IS_NODE){var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;ret=tryParseAsDataURI(filename);if(!ret){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename)}return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",(function(reason,p){process["exit"](1)}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){var data=tryParseAsDataURI(f);if(data){return intArrayToString(data)}return read(f)}}Module["readBinary"]=function readBinary(f){var data;data=tryParseAsDataURI(f);if(data){return data}if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)};if(typeof arguments!="undefined"){Module["arguments"]=arguments}Module["setWindowTitle"]=(function(title){document.title=title})}Module["print"]=typeof console!=="undefined"?console.log:typeof print!=="undefined"?print:null;Module["printErr"]=typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn||Module["print"];Module.print=Module["print"];Module.printErr=Module["printErr"];for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;function staticAlloc(size){assert(!staticSealed);var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function dynamicAlloc(size){assert(DYNAMICTOP_PTR);var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=ret+size+15&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;if(end>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){HEAP32[DYNAMICTOP_PTR>>2]=ret;return 0}}return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;return ret}function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return 4}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;Module.printErr(text)}}var functionPointers=new Array(0);var funcWrappers={};function dynCall(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}else{return Module["dynCall_"+sig].call(null,ptr)}}var GLOBAL_BASE=8;var ABORT=0;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}var JSfuncs={"stackSave":(function(){stackSave()}),"stackRestore":(function(){stackRestore()}),"arrayToC":(function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};function ccall(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0){stackRestore(stack)}return ret}function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}var ALLOC_STATIC=2;var ALLOC_NONE=4;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[typeof _malloc==="function"?_malloc:staticAlloc,stackAlloc,staticAlloc,dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var stop;ptr=ret;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return UTF8ToString(ptr)}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx){var endPtr=idx;while(u8Array[endPtr])++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}}function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function allocateUTF8(str){var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8Array(str,HEAP8,ret,size);return ret}function demangle(func){return func}function demangleAll(text){var regex=/__Z[\w\d_]+/g;return text.replace(regex,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"])js+="\n"+Module["extraStackTrace"]();return demangleAll(js)}var PAGE_SIZE=16384;var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed;var STACK_BASE,STACKTOP,STACK_MAX;var DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0;staticSealed=false;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||134217728;if(TOTAL_MEMORY<TOTAL_STACK)Module.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();function getTotalMemory(){return TOTAL_MEMORY}HEAP32[0]=1668509029;HEAP16[1]=25459;if(HEAPU8[2]!==115||HEAPU8[3]!==99)throw"Runtime error: expected the system to be little-endian!";function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];if(!Math["trunc"])Math["trunc"]=(function(x){return x<0?Math.ceil(x):Math.floor(x)});Math.trunc=Math["trunc"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_round=Math.round;var Math_min=Math.min;var Math_max=Math.max;var Math_clz32=Math.clz32;var Math_trunc=Math.trunc;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var ASM_CONSTS=[(function($0,$1){var path=Pointer_stringify($0);var data=Pointer_stringify($1);FS.createPath("/",PATH.dirname(path));FS.writeFile(PATH.join("/",path),data)})];function _emscripten_asm_const_iii(code,a0,a1){return ASM_CONSTS[code](a0,a1)}STATIC_BASE=GLOBAL_BASE;STATICTOP=STATIC_BASE+197232;__ATINIT__.push();memoryInitializer="data:application/octet-stream;base64,AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/ACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAIMEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSQAAAAAAAAFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJAAAAAAAAAUkAAIMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSQAAAAAAAAFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJAAAAAAAAAUkACEAAAAAAAAAAAAAAAABBAoC8AAAgAAAABAAAAAAAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICWQAAAAAAAgJZABJDDAAAAAAAAAAAAAAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWEAAAAAAAABYQAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYQAAAAAAAAFhAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUkAAAAAAAABSQAAgAwIAAAAAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAABCQAAAAAAAAEJAAAAAAAAgg0AAAAAAAMCIQAAAAAAAAFJAAAAAAAAAUkAAAAAAAAAAAAAAAAAAAEJAAAAAAAAAQkAAAAAAACCDQAAAAAAAwIhAAAAAAAAAUkAAAAAAAABSQACwwQAAAAAAAAAAAAAAEEBANgAAkwAAAAEAAAAAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJAAAAAAAAAUkAAEAACAAAAAAAAAAAAABBAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYQAAAAAAAAFhAALDBAAAAAAAAAAAAAAAAABw6AAAQAAAAAQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWEAAAAAAAABYQEAgPgMAAAAAAAAAAAAAEEAkOwAAegAAAAEAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJAAAAAAAAAUkAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSQAAAAAAAAFJA////////73/////////vf////////+//////////7/8AAAAAAAD4PzMzMzMzM9M/PAAAAAAAAACamZmZmZmpPwEAAAAAAAAAAQAAAAAAAAAAAAAAAADwPwEAAAAAAAAAAgAAAAAAAAAAAAAAAADwPwIAAAAAAAAAAwAAAAAAAAAAAAAAAADgPwMAAAAAAAAABAAAAAAAAAAAAAAAAADwPwQAAAAAAAAABQAAAAAAAAAzMzMzMzPzPwUAAAAAAAAABgAAAAAAAACamZmZmZnpPwYAAAAAAAAABwAAAAAAAAAAAAAAAADwPwcAAAAAAAAACAAAAAAAAAAAAAAAAADgPwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAMzMzMzMz078AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAgEZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAmpmZmZmZ2b8AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzPjPwAAAAAAAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAAAAAexSuR+F65L8AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAEAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABr4VQAAAQAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAAgGZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAgGZAmpmZmZmZ2b8AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAgGZAexSuR+F65L8AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAgEZAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAQAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkAAAAAAAAAAAEAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAAAAQAAAAAAAAAAAAAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAAAADQPwAAAAAAANA/AAAAAAAA0D8AAAAA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YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8YTLjP1HaG3xhMuM/UdobfGEy4z9R2ht8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AQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAwAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAABAAAAAUAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAGAAAAAQAAAAEAAAAHAAAACAAAAAkAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAAQAAAAEAAAALAAAAAQAAAAwAAAABAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABMAAAATAAAAEwAAABMAAAAXAAAAGAAAABkAAAATAAAAGgAAABsAAAAcAAAAHQAAABMAAAATAAAAEwAAABMAAAATAAAAAQAAAB4AAAABAAAAAQAAABMAAAABAAAAHwAAACAAAAAhAAAAIgAAACMAAAATAAAAJAAAACUAAAAmAAAAEwAAABMAAAATAAAAEwAAACcAAAAoAAAAKQAAABMAAAAqAAAAKwAAACwAAAAtAAAAEwAAABMAAAATAAAAEwAAABMAAAABAAAAAQAAAAEAAAABAAAAAQAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAALgAAABMAAAATAAAAEwAAAC8AAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAA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
gitextract_rzi0l72b/ ├── .Rbuildignore ├── .github/ │ ├── .gitignore │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── build-and-check-CRAN.yml │ ├── check-and-deploy.yml │ └── docker-build-push.yml ├── .gitignore ├── CITATION.cff ├── DESCRIPTION ├── Dockerfile ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── PRISMA2020.Rproj ├── R/ │ ├── PRISMA_flowdiagram.R │ ├── globals.R │ └── utils.R ├── README.md ├── code-of-conduct.md ├── contributing.md ├── inst/ │ ├── CITATION │ ├── extdata/ │ │ ├── PRISMA.csv │ │ ├── Template.html │ │ └── citation.ris │ └── shiny-examples/ │ └── PRISMA_flowdiagram/ │ ├── app.R │ ├── rsconnect/ │ │ └── shinyapps.io/ │ │ └── estech/ │ │ └── prisma_flowdiagram.dcf │ └── www/ │ ├── Haddaway_et_al_2022.ris │ ├── PRISMA.csv │ ├── kofi.js │ └── labels.js ├── man/ │ ├── PRISMA_add_hyperlink_.Rd │ ├── PRISMA_calc_filetype_.Rd │ ├── PRISMA_data.Rd │ ├── PRISMA_flowdiagram.Rd │ ├── PRISMA_format_number_.Rd │ ├── PRISMA_format_reasons_.Rd │ ├── PRISMA_gen_tmp_svg_.Rd │ ├── PRISMA_get_height_.Rd │ ├── PRISMA_get_pos_.Rd │ ├── PRISMA_insert_js_.Rd │ ├── PRISMA_interactive_.Rd │ ├── PRISMA_parse_reasons_.Rd │ ├── PRISMA_save.Rd │ ├── read_PRISMAdata.Rd │ └── sr_flow_interactive.Rd └── shiny-server.conf
SYMBOL INDEX (3 symbols across 1 files)
FILE: inst/shiny-examples/PRISMA_flowdiagram/www/labels.js
function renderLabel (line 5) | function renderLabel(node, label) {
function waitForAddedNode (line 22) | function waitForAddedNode(id, parent, recursive, done) {
function createLabels (line 39) | function createLabels(nodeMap) {
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,336K chars).
[
{
"path": ".Rbuildignore",
"chars": 151,
"preview": "^.*\\.Rproj$\n^\\.Rproj\\.user$\n^\\.github$\n.png\ninst/shiny-examples\nLICENSE.md\nCITATION.cff\ncode-of-conduct.md\ncontributing."
},
{
"path": ".github/.gitignore",
"chars": 7,
"preview": "*.html\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 97,
"preview": "# These are supported funding model platforms\nopen_collective: esmarconf\nko_fi: chriscpritchard\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/workflows/build-and-check-CRAN.yml",
"chars": 2234,
"preview": "on:\n push:\n tags:\n - 'v*'\nname: Publish Release, Build & Check for CRAN\njobs:\n R-CMD-Build:\n runs-on: ubunt"
},
{
"path": ".github/workflows/check-and-deploy.yml",
"chars": 3105,
"preview": "on:\n release:\n types: [released]\n pull_request:\n types: [opened, synchronize, reopened]\n issue_comment:\n typ"
},
{
"path": ".github/workflows/docker-build-push.yml",
"chars": 1544,
"preview": "name: Docker\n\non:\n push:\n branches:\n - master\n tags:\n - v*\n\nenv:\n IMAGE_NAME: prisma-flowdiagram/prism"
},
{
"path": ".gitignore",
"chars": 72,
"preview": ".Rproj.user\n.Rhistory\n.RData\n.Ruserdata\n.vscode/launch.json\n**/.DS_Store"
},
{
"path": "CITATION.cff",
"chars": 1325,
"preview": "cff-version: 1.2.0\nmessage: \"If you use this software, please cite it as below.\"\nauthors:\n - family-names: Haddaway\n "
},
{
"path": "DESCRIPTION",
"chars": 1996,
"preview": "Package: PRISMA2020\nTitle: Make Interactive 'PRISMA' Flow Diagrams\nVersion: 1.1.2\nAuthors@R: c(\n person(given = \"Neal"
},
{
"path": "Dockerfile",
"chars": 1159,
"preview": "FROM rocker/shiny:latest\n\nLABEL maintainer=\"Hossam Hammady <hossam@rayyan.ai>\"\n\n# Install system dependencies\nRUN apt-ge"
},
{
"path": "LICENSE",
"chars": 44,
"preview": "YEAR: 2020\nCOPYRIGHT HOLDER: Neal R Haddaway"
},
{
"path": "LICENSE.md",
"chars": 1054,
"preview": "Copyright 2020 Neal R Haddaway\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this sof"
},
{
"path": "NAMESPACE",
"chars": 287,
"preview": "# Generated by roxygen2: do not edit by hand\n\nexport(PRISMA_data)\nexport(PRISMA_flowdiagram)\nexport(PRISMA_save)\nimportF"
},
{
"path": "PRISMA2020.Rproj",
"chars": 343,
"preview": "Version: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSp"
},
{
"path": "R/PRISMA_flowdiagram.R",
"chars": 50234,
"preview": "# Suppress R CMD check note\n#' @importFrom DT addRow\n#' @importFrom rio import\n#' @importFrom shiny column\n#' @importFro"
},
{
"path": "R/globals.R",
"chars": 2783,
"preview": "utils::globalVariables(c(\"previous_studies\",\n \"previous_reports\",\n \"regi"
},
{
"path": "R/utils.R",
"chars": 13806,
"preview": "# Utility functions for PRISMA_flowdiagram\n\n#' Calculate the correct height of a box from a list (e.g. of exclusion reas"
},
{
"path": "README.md",
"chars": 2906,
"preview": "<!-- badges: start -->\n[ "
},
{
"path": "inst/extdata/citation.ris",
"chars": 3202,
"preview": "TY - JOUR\nT1 - PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interact"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/app.R",
"chars": 33260,
"preview": "library(shiny)\nlibrary(shinyjs)\nlibrary(rsvg)\nlibrary(DT) #nolint\nlibrary(rio)\nlibrary(devtools)\nlibrary(PRISMA2020) #no"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/rsconnect/shinyapps.io/estech/prisma_flowdiagram.dcf",
"chars": 256,
"preview": "name: prisma_flowdiagram\ntitle:\nusername:\naccount: estech\nserver: shinyapps.io\nhostUrl: https://api.shinyapps.io/v1\nappI"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/www/Haddaway_et_al_2022.ris",
"chars": 3202,
"preview": "TY - JOUR\nT1 - PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interact"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/www/PRISMA.csv",
"chars": 5264,
"preview": "data,node,box,description,boxtext,tooltips,url,n\r\nNA,node4,prevstud,Grey title box; Previous studies,Previous studies,Gr"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/www/kofi.js",
"chars": 298,
"preview": "/** \n *\tKo-fi button\n*/\nkofiWidgetOverlay.draw(\n 'chriscpritchard', {\n 'type': 'floating-chat',\n 'float"
},
{
"path": "inst/shiny-examples/PRISMA_flowdiagram/www/labels.js",
"chars": 1554,
"preview": "/** \n *\t@param {HTMLElement} node the node to label \n * @param {string} label the label for the node\n*/\nfunction render"
},
{
"path": "man/PRISMA_add_hyperlink_.Rd",
"chars": 458,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_add_hyperlink_}\n\\alia"
},
{
"path": "man/PRISMA_calc_filetype_.Rd",
"chars": 497,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_calc_filetype_}\n\\alia"
},
{
"path": "man/PRISMA_data.Rd",
"chars": 526,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/PRISMA_flowdiagram.R\n\\name{PRISMA_data}\n\\a"
},
{
"path": "man/PRISMA_flowdiagram.Rd",
"chars": 3700,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/PRISMA_flowdiagram.R\n\\name{PRISMA_flowdiag"
},
{
"path": "man/PRISMA_format_number_.Rd",
"chars": 405,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_format_number_}\n\\alia"
},
{
"path": "man/PRISMA_format_reasons_.Rd",
"chars": 513,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_format_reasons_}\n\\ali"
},
{
"path": "man/PRISMA_gen_tmp_svg_.Rd",
"chars": 399,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_gen_tmp_svg_}\n\\alias{"
},
{
"path": "man/PRISMA_get_height_.Rd",
"chars": 540,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_get_height_}\n\\alias{P"
},
{
"path": "man/PRISMA_get_pos_.Rd",
"chars": 703,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_get_pos_}\n\\alias{PRIS"
},
{
"path": "man/PRISMA_insert_js_.Rd",
"chars": 689,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_insert_js_}\n\\alias{PR"
},
{
"path": "man/PRISMA_interactive_.Rd",
"chars": 1185,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_interactive_}\n\\alias{"
},
{
"path": "man/PRISMA_parse_reasons_.Rd",
"chars": 473,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/utils.R\n\\name{PRISMA_parse_reasons_}\n\\alia"
},
{
"path": "man/PRISMA_save.Rd",
"chars": 1823,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/PRISMA_flowdiagram.R\n\\name{PRISMA_save}\n\\a"
},
{
"path": "man/read_PRISMAdata.Rd",
"chars": 390,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/PRISMA_flowdiagram.R\n\\name{read_PRISMAdata"
},
{
"path": "man/sr_flow_interactive.Rd",
"chars": 1073,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/PRISMA_flowdiagram.R\n\\name{sr_flow_interac"
},
{
"path": "shiny-server.conf",
"chars": 394,
"preview": "run_as shiny;\n\n# Define a server that listens on port 3838\nserver {\n listen 3838;\n\n # Define a location at the base UR"
}
]
About this extraction
This page contains the full source code of the prisma-flowdiagram/PRISMA2020 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (2.2 MB), approximately 582.5k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.