diff --git a/.Rbuildignore b/.Rbuildignore index 3648426..98ecc07 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -15,3 +15,4 @@ ^cran-comments\.md$ ^data-raw$ ^CRAN-SUBMISSION$ +^\.tmp$ diff --git a/.gitignore b/.gitignore index d15d3cf..6f63c0d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ run_remote.md *.csv .DS_Store + +.tmp diff --git a/DESCRIPTION b/DESCRIPTION index 56d710e..7c4c55b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: simaerep Title: Find Clinical Trial Sites Under-Reporting Adverse Events -Version: 0.5.0 +Version: 0.5.0.900 Authors@R: c( person(given = "Bjoern", family = "Koneswarakantha", @@ -34,7 +34,8 @@ Imports: furrr (>= 0.2.1), progressr, knitr, - tibble + tibble, + dbplyr Suggests: testthat, devtools, @@ -42,8 +43,11 @@ Suggests: spelling, haven, vdiffr, - lintr + lintr, + DBI, + duckdb, + ggExtra Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.2 Language: en-US Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 08043a3..c11f5e9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,6 +16,7 @@ export(get_pat_pool_config) export(get_portf_perf) export(is_orivisit) export(is_simaerep) +export(max_rank) export(orivisit) export(pat_aggr) export(pat_pool) @@ -29,14 +30,17 @@ export(prep_for_sim) export(prob_lower_site_ae_vs_study_ae) export(purrr_bar) export(sim_after_prep) +export(sim_inframe) export(sim_scenario) export(sim_sites) export(sim_studies) export(sim_test_data_patient) export(sim_test_data_portfolio) export(sim_test_data_study) +export(sim_ur) export(sim_ur_scenarios) export(simaerep) +export(simaerep_inframe) export(site_aggr) export(with_progress_cnd) import(ggplot2) @@ -45,13 +49,18 @@ importFrom(cowplot,draw_label) importFrom(cowplot,get_plot_component) importFrom(cowplot,ggdraw) importFrom(cowplot,plot_grid) +importFrom(dbplyr,window_order) importFrom(dplyr,across) +importFrom(dplyr,all_of) importFrom(dplyr,any_of) importFrom(dplyr,arrange) importFrom(dplyr,between) importFrom(dplyr,bind_cols) importFrom(dplyr,bind_rows) importFrom(dplyr,case_when) +importFrom(dplyr,collect) +importFrom(dplyr,cross_join) +importFrom(dplyr,cume_dist) importFrom(dplyr,dense_rank) importFrom(dplyr,desc) importFrom(dplyr,distinct) @@ -61,6 +70,7 @@ importFrom(dplyr,group_by) importFrom(dplyr,group_by_at) importFrom(dplyr,inner_join) importFrom(dplyr,is_grouped_df) +importFrom(dplyr,join_by) importFrom(dplyr,lag) importFrom(dplyr,lead) importFrom(dplyr,left_join) @@ -69,17 +79,23 @@ importFrom(dplyr,mutate_all) importFrom(dplyr,mutate_at) importFrom(dplyr,n) importFrom(dplyr,n_distinct) +importFrom(dplyr,near) importFrom(dplyr,one_of) +importFrom(dplyr,percent_rank) importFrom(dplyr,pull) importFrom(dplyr,rename) importFrom(dplyr,right_join) importFrom(dplyr,row_number) importFrom(dplyr,sample_n) importFrom(dplyr,select) +importFrom(dplyr,slice_sample) +importFrom(dplyr,starts_with) importFrom(dplyr,summarise) importFrom(dplyr,summarise_all) importFrom(dplyr,summarise_at) +importFrom(dplyr,tbl) importFrom(dplyr,ungroup) +importFrom(dplyr,union_all) importFrom(dplyr,vars) importFrom(forcats,fct_relevel) importFrom(furrr,furrr_options) @@ -114,6 +130,7 @@ importFrom(stats,rpois) importFrom(stats,runif) importFrom(stats,sd) importFrom(stringr,str_count) +importFrom(stringr,str_detect) importFrom(stringr,str_length) importFrom(stringr,str_pad) importFrom(tibble,tibble) diff --git a/NEWS.md b/NEWS.md index 50b7e5d..a0d3a95 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +# simaerep 0.6.0 +- inframe method use table operations only, do not use visit_med75 +- https://github.com/openpharma/simaerep/issues/59 +- https://github.com/openpharma/simaerep/issues/16 +- update vignettes + # simaerep 0.5.0 - allow flexible AE rates in data simulations - add vignette comparing simaerep to gsm performance diff --git a/R/0_imports.R b/R/0_imports.R index 4e51703..4ec94f3 100644 --- a/R/0_imports.R +++ b/R/0_imports.R @@ -9,15 +9,17 @@ #' @importFrom purrr map_int map_chr #' @importFrom furrr future_map future_pmap furrr_options #' @importFrom progressr with_progress -#' @importFrom stringr str_count str_pad str_length +#' @importFrom stringr str_count str_pad str_length str_detect #' @importFrom rlang := .data enexpr env_has .env #' @importFrom dplyr select mutate filter summarise group_by summarise_all summarise_at #' @importFrom dplyr mutate_all mutate_at ungroup vars bind_cols bind_rows pull #' @importFrom dplyr n_distinct distinct arrange right_join left_join inner_join #' @importFrom dplyr rename sample_n between row_number dense_rank desc case_when #' @importFrom dplyr group_by_at n is_grouped_df everything one_of lag any_of across -#' @importFrom dplyr lead +#' @importFrom dplyr lead tbl cross_join join_by union_all cume_dist slice_sample +#' @importFrom dplyr percent_rank all_of starts_with collect near #' @importFrom tidyr tibble unnest nest fill #' @importFrom knitr kable #' @importFrom tibble tibble +#' @importFrom dbplyr window_order NULL diff --git a/R/S3_orivisit.R b/R/S3_orivisit.R index 9fb0b0b..2f71f6f 100644 --- a/R/S3_orivisit.R +++ b/R/S3_orivisit.R @@ -15,8 +15,7 @@ validate_orivisit <- function(x) { comp <- sort(attributes(x)$names) == sort(c("dim", "df_summary", "str_call")) stopifnot(all(comp)) stopifnot(length(x$dim) == 2) - stopifnot(is.data.frame(x$df_summary)) - stopifnot(nrow(x$df_summary) == 1) + inherits(x$df_summary, "data.frame") | inherits(x$df_summary, "tbl") stopifnot(is.character(x$str_call) | is.na(x$str_call)) return(x) } @@ -44,7 +43,7 @@ get_str_var <- function(call, env) { str_call <- deparse(call) - if (str_length(str_call) > 80) { + if (sum(str_length(str_call)) > 80) { return(NA) } @@ -94,7 +93,7 @@ orivisit <- function(df_visit, call = NULL, env = parent.frame()) { call <- rlang::enexpr(df_visit) } - stopifnot(is.data.frame(df_visit)) + stopifnot(inherits(df_visit, "data.frame") | inherits(df_visit, "tbl")) dim <- dim(df_visit) df_summary <- summarise_df_visit(df_visit) @@ -117,11 +116,15 @@ as.data.frame.orivisit <- function(x, ..., env = parent.frame()) { df <- rlang::env_get(env, x$str_call, inherit = TRUE) - dim <- dim(df) - df_summary <- summarise_df_visit(df) + df_is_tbl <- ! inherits(df, "data.frame") & inherits(df, "tbl") - if (! all.equal(df_summary, x$df_summary, tolerance = 1e-4)) stop.orivisit() - if (! all(dim == x$dim)) stop.orivisit() + if (! df_is_tbl) { + dim <- dim(df) + df_summary <- summarise_df_visit(df) + + if (! all.equal(df_summary, x$df_summary, tolerance = 1e-4)) stop.orivisit() + if (! all(dim == x$dim)) stop.orivisit() + } return(df) } diff --git a/R/S3_simaerep.R b/R/S3_simaerep.R index bf4378e..57d6d1c 100644 --- a/R/S3_simaerep.R +++ b/R/S3_simaerep.R @@ -3,6 +3,10 @@ new_simaerep <- function(visit, df_site, df_sim_sites, df_eval, + r, + visit_med75, + inframe, + under_only, param_site_aggr, param_sim_sites, param_eval_sites) { @@ -13,6 +17,10 @@ new_simaerep <- function(visit, df_site = df_site, df_sim_sites = df_sim_sites, df_eval = df_eval, + r = r, + visit_med75 = visit_med75, + inframe = inframe, + under_only = under_only, param_site_aggr = param_site_aggr, param_sim_sites = param_sim_sites, param_eval_sites = param_eval_sites @@ -31,6 +39,10 @@ validate_simaerep <- function(x) { "df_site", "df_sim_sites", "df_eval", + "r", + "visit_med75", + "inframe", + "under_only", "param_site_aggr", "param_sim_sites", "param_eval_sites" @@ -40,9 +52,9 @@ validate_simaerep <- function(x) { stopifnot(all(obj_names == obj_names_check)) stopifnot(is_orivisit(x$visit)) - stopifnot(is.data.frame(x$df_site)) - stopifnot(is.data.frame(x$df_sim_sites)) - stopifnot(is.data.frame(x$df_eval)) + stopifnot(is.data.frame(x$df_site) | inherits(x$df_site, "tbl")) + stopifnot(is.data.frame(x$df_sim_sites) | inherits(x$df_sim_sites, "tbl")) + stopifnot(is.data.frame(x$df_eval) | inherits(x$df_eval, "tbl")) stopifnot(is.list(x$param_site_aggr)) stopifnot(is.list(x$param_sim_sites)) stopifnot(is.list(x$param_eval_sites)) @@ -50,53 +62,263 @@ validate_simaerep <- function(x) { return(x) } -#'@title create simaerep object -#'@description simulate AE under-reporting probabilities -#'@param df_visit data frame with columns: study_id, site_number, patnum, visit, -#' n_ae -#'@param param_site_aggr list of parameters passed to [site_aggr()][site_aggr], -#' Default: list(method = "med75_adj", min_pat_pool = 0.2) -#'@param param_sim_sites list of parameters passed to [sim_sites()][sim_sites], -#' Default: list(r = 1000, poisson_test = FALSE, prob_lower = TRUE) -#'@param param_eval_sites list of parameters passed to -#' [eval_sites()][eval_sites], Default: list(method = "BH") -#'@param progress logical, display progress bar, Default = TRUE -#'@param check logical, perform data check and attempt repair with -#' [check_df_visit()][check_df_visit], computationally expensive on large data -#' sets. Default: TRUE -#'@param env optional, provide environment of original visit data, Default: -#' parent.frame() -#'@param under_only, logical compute under-reporting probabilities only, -#' superseeds under_only parameter passed to [eval_sites()][eval_sites] and -#' [sim_sites()][sim_sites], Default: TRUE -#'@return simaerep object -#'@details executes [site_aggr()][site_aggr], [sim_sites()][sim_sites] and -#' [eval_sites()][eval_sites] on original visit data and stores all -#' intermediate results. Stores lazy reference to original visit data for -#' facilitated plotting using generic plot(x). +#'@title Create simaerep object +#'@description Simulate AE under-reporting probabilities. +#'@param df_visit Data frame with columns: study_id, site_number, patnum, visit, +#' n_ae. +#'@param r Integer or tbl_object, number of repetitions for bootstrap +#' simulation. Pass a tbl object referring to a table with one column and as +#' many rows as desired repetitions. Default: 1000. +#'@param check Logical, perform data check and attempt repair with +#' [check_df_visit()]. Computationally expensive on large data sets. Default: +#' TRUE. +#'@param visit_med75 Logical, should evaluation point visit_med75 be used. +#' Default: TRUE. +#'@param inframe Logical, only table operations to be used; does not require +#' visit_med75. Compatible with dbplyr supported database backends. +#'@param param_site_aggr List of parameters passed to [site_aggr()]. Default: +#' list(method = "med75_adj", min_pat_pool = 0.2). +#'@param param_sim_sites List of parameters passed to [sim_sites()]. Default: +#' list(r = 1000, poisson_test = FALSE, prob_lower = TRUE). +#'@param param_eval_sites List of parameters passed to [eval_sites()]. Default: +#' list(method = "BH"). +#'@param progress Logical, display progress bar. Default: TRUE. +#'@param env Optional, provide environment of original visit data. Default: +#' parent.frame(). +#'@param under_only Logical, compute under-reporting probabilities only. +#' Supersedes under_only parameter passed to [eval_sites()] and [sim_sites()]. +#' Default: TRUE. +#'@return A simaerep object. +#'@details Executes [site_aggr()], [sim_sites()], and [eval_sites()] on original +#' visit data and stores all intermediate results. Stores lazy reference to +#' original visit data for facilitated plotting using generic plot(x). #' @examples -#' #' df_visit <- sim_test_data_study( #' n_pat = 100, #' n_sites = 5, #' frac_site_with_ur = 0.4, #' ur_rate = 0.6 #' ) -#' #' df_visit$study_id <- "A" -#' #' aerep <- simaerep(df_visit) -#' #' aerep -#' #' str(aerep) -#' +#' \donttest{ +#' # In-frame table operations +#' simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval +#' simaerep(df_visit, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval +#' # Database example +#' con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") +#' df_r <- tibble::tibble(rep = seq(1, 1000)) +#' dplyr::copy_to(con, df_visit, "visit") +#' dplyr::copy_to(con, df_r, "r") +#' tbl_visit <- dplyr::tbl(con, "visit") +#' tbl_r <- dplyr::tbl(con, "r") +#' simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval +#' simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval +#' DBI::dbDisconnect(con) +#' } +#'@seealso [site_aggr()], [sim_sites()], [eval_sites()], [orivisit()], +#' [plot.simaerep()] +#'@export #'@seealso [site_aggr()][site_aggr], [sim_sites()][sim_sites], #' [eval_sites()][eval_sites], [orivisit()][orivisit], #' [plot.simaerep()][plot.simaerep] #'@rdname simaerep #'@export simaerep <- function(df_visit, + r = 1000, + check = TRUE, + under_only = TRUE, + visit_med75 = TRUE, + inframe = FALSE, + progress = TRUE, + param_site_aggr = list( + method = "med75_adj", + min_pat_pool = 0.2 + ), + param_sim_sites = list( + r = 1000, + poisson_test = FALSE, + prob_lower = TRUE + ), + param_eval_sites = list( + method = "BH" + ), + env = parent.frame()) { + + call <- rlang::enexpr(df_visit) + + # when two tbl objects passed automatically switch to inframe + is_tbl_df_visit <- ! is.data.frame(df_visit) & inherits(df_visit, "tbl") + is_tbl_r <- ! is.data.frame(r) & inherits(r, "tbl") + + if (is_tbl_df_visit && is_tbl_r) { + inframe <- TRUE + } + + # save visit call + visit <- tryCatch({ + visit <- orivisit(df_visit, call, env = env) + as.data.frame(visit) + visit}, + error = function(e) df_visit + ) + + param_sim_sites$under_only <- under_only + param_eval_sites$under_only <- under_only + + if (visit_med75 && ! inframe) { + + if (r != param_sim_sites$r) { + param_sim_sites$r <- r + warning("r not equal param_sim_sites$r, overriding param_sim_sites$r") + } + + aerep <- simaerep_visit_med75( + df_visit = visit, + under_only = under_only, + progress = progress, + param_site_aggr = param_site_aggr, + param_sim_sites = param_sim_sites, + param_eval_sites = param_eval_sites, + env = env, + check = check + ) + + } else if (inframe) { + aerep <- simaerep_inframe( + df_visit = visit, + r = r, + under_only = under_only, + visit_med75 = visit_med75, + param_site_aggr = param_site_aggr, + param_eval_sites = param_eval_sites, + env = env, + check = check + ) + } else { + stop("visit_med75 parameter mus be TRUE if inframe is FALSE") + } + + return(aerep) + +} + +#' simulate in dataframe +#' @inheritParams simaerep +#' @keywords internal +#' @export +#' @examples +#' df_visit <- sim_test_data_study( +#' n_pat = 100, +#' n_sites = 5, +#' frac_site_with_ur = 0.4, +#' ur_rate = 0.6 +#' ) +#' df_visit$study_id <- "A" +#' +#' simaerep_inframe(df_visit) +#' simaerep_inframe(df_visit, visit_med75 = TRUE)$df_eval +#'\donttest{ +#'# Database +#'con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") +#'df_r <- tibble::tibble(rep = seq(1, 1000)) +#' +#'dplyr::copy_to(con, df_visit, "visit") +#'dplyr::copy_to(con, df_r, "r") +#' +#'tbl_visit <- dplyr::tbl(con, "visit") +#'tbl_r <- dplyr::tbl(con, "r") +#' +#'simaerep_inframe(tbl_visit, r = tbl_r)$df_eval +#'simaerep_inframe(tbl_visit, r = tbl_r, visit_med75 = TRUE)$df_eval +#' +#'DBI::dbDisconnect(con) +#'} +simaerep_inframe <- function(df_visit, + r = 1000, + under_only = FALSE, + visit_med75 = FALSE, + check = TRUE, + param_site_aggr = list( + method = "med75_adj", + min_pat_pool = 0.2 + ), + param_eval_sites = list( + method = "BH" + ), + env = parent.frame()) { + + if (inherits(df_visit, "orivisit")) { + visit <- df_visit + df_visit <- as.data.frame(df_visit, env = env) + } else { + call <- rlang::enexpr(df_visit) + visit <- orivisit(df_visit, call, env = env) + } + + # check + if (check) { + df_visit <- check_df_visit(df_visit) + } + + if (! visit_med75) { + param_site_aggr$method <- "max" + } + + df_site <- do.call( + site_aggr, + c( + list(df_visit = df_visit), + check = FALSE, + param_site_aggr + ) + ) + + if (visit_med75) { + df_sim_sites <- sim_inframe(df_visit, r = r, df_site = df_site) + } else { + df_sim_sites <- sim_inframe(df_visit, r = r) + } + + # evaluate + df_eval <- do.call( + eval_sites, + c( + list(df_sim_sites = df_sim_sites), + param_eval_sites + ) + ) + + if (visit_med75) { + df_eval <- df_eval %>% + left_join( + df_site %>% + select(- "n_pat"), + by = c("study_id", "site_number") + ) %>% + select(- "mean_ae_site_med75") + } + + validate_simaerep( + new_simaerep( + visit = visit, + df_site, + df_sim_sites, + df_eval, + r = r, + visit_med75 = visit_med75, + inframe = TRUE, + under_only = under_only, + param_site_aggr = param_site_aggr, + param_sim_sites = list(), + param_eval_sites = param_eval_sites + ) + ) +} + +simaerep_visit_med75 <- function(df_visit, param_site_aggr = list( method = "med75_adj", min_pat_pool = 0.2 @@ -109,15 +331,21 @@ simaerep <- function(df_visit, param_eval_sites = list( method = "BH" ), - progress = TRUE, check = TRUE, + progress = TRUE, env = parent.frame(), - under_only = TRUE) { + under_only = TRUE, + r = 1000) { - call <- rlang::enexpr(df_visit) - - visit <- orivisit(df_visit, call, env = env) + if (inherits(df_visit, "orivisit")) { + visit <- df_visit + df_visit <- as.data.frame(df_visit, env = env) + } else { + call <- rlang::enexpr(df_visit) + visit <- orivisit(df_visit, call, env = env) + } + # check if (check) { df_visit <- check_df_visit(df_visit) } @@ -161,6 +389,10 @@ simaerep <- function(df_visit, df_site, df_sim_sites, df_eval, + r = param_sim_sites$r, + visit_med75 = TRUE, + inframe = FALSE, + under_only = under_only, param_site_aggr = param_site_aggr, param_sim_sites = param_sim_sites, param_eval_sites = param_eval_sites @@ -218,7 +450,14 @@ plot.simaerep <- function(x, ) if (is.null(study)) { - study <- unique(x$df_eval$study_id)[[1]] + + studies <- x$df_eval %>% + distinct(.data$study_id) %>% + pull(.data$study_id) %>% + sort() + + study <- studies[1] + message(paste0("study = NULL, defaulting to study:", study)) } @@ -259,7 +498,7 @@ print.simaerep <- function(x, ...) { paste( c( "simaerep object:", - "Check aerep$df_eval prob_low_prob_ur column for AE under-reporting probabilites.", + "Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty.", "Plot results using plot() generic." ), collapse = "\n" diff --git a/R/inframe.R b/R/inframe.R new file mode 100644 index 0000000..0778505 --- /dev/null +++ b/R/inframe.R @@ -0,0 +1,283 @@ + + + +#' prune visits to visit_med75 using table operations +#'@inheritParams simaerep +#'@param df_site dataframe, as returned by [site_aggr()] +#'@keywords internal +prune_to_visit_med75_inframe <- function(df_visit, df_site) { + df_pat_aggr <- pat_aggr(df_visit) + + # drop patients that did not reach visit_med75 + df_pat <- df_pat_aggr %>% + left_join( + df_site, + by = c("study_id", "site_number") + ) %>% + filter(.data$max_visit_per_pat >= .data$visit_med75) %>% + select(c("study_id", "site_number", "patnum", "visit_med75")) + + # filter patients and drop excess visits + df_visit <- df_visit %>% + inner_join( + df_pat, + by = c("study_id", "site_number", "patnum") + ) %>% + filter(.data$visit <= .data$visit_med75) %>% + select(- c("visit_med75")) +} + + +#' Calculate prob_lower for study sites using table operations +#'@export +#'@inheritParams simaerep +#'@param df_site, dataframe as returned be [site_aggr()], Will switch to visit_med75. +#'Default: NULL +#'@examples +#' df_visit <- sim_test_data_study( +#' n_pat = 100, +#' n_sites = 5, +#' frac_site_with_ur = 0.4, +#' ur_rate = 0.6 +#' ) +#' df_visit$study_id <- "A" +#' +#' df_sim <- sim_inframe(df_visit) +#' df_eval <- eval_sites(df_sim) +#' df_eval +sim_inframe <- function(df_visit, r = 1000, df_site = NULL) { + + # db back-end, to not form ratios from integers + df_visit <- df_visit %>% + mutate( + n_ae = as.double(.data$n_ae), + visit = as.double(.data$visit) + ) + + if (! inherits(r, "tbl")) { + df_r <- tibble(rep = seq(1, r)) + } else { + df_r <- r %>% + rename(rep = 1) + } + + # aggregate per patient to get max visits + df_pat_aggr_pool <- pat_aggr(df_visit) + + # this implements visit_med75 + if (! is.null(df_site)) { + df_visit_prune <- prune_to_visit_med75_inframe(df_visit, df_site) + + df_calc_ori <- df_visit_prune %>% + filter(visit == max(.data$visit, na.rm = TRUE), .by = c("patnum", "study_id")) %>% + summarise( + events = sum(.data$n_ae), + visits = sum(.data$visit), + n_pat = n_distinct(.data$patnum), + events_per_visit_site_ori = sum(.data$n_ae, na.rm = TRUE) / sum(.data$visit, na.rm = TRUE), + .by = c("study_id", "site_number") + ) + + df_pat_aggr_site <- df_visit_prune %>% + prune_to_visit_med75_inframe(df_site) %>% + pat_aggr() + + remove(df_visit_prune) + + } else { + df_pat_aggr_site <- df_pat_aggr_pool + + df_calc_ori <- df_visit %>% + filter(visit == max(.data$visit, na.rm = TRUE), .by = c("patnum", "study_id")) %>% + summarise( + events = sum(.data$n_ae), + visits = sum(.data$visit), + n_pat = n_distinct(.data$patnum), + events_per_visit_site_ori = sum(.data$n_ae, na.rm = TRUE) / sum(.data$visit, na.rm = TRUE), + .by = c("study_id", "site_number") + ) + } + + # for every max visit in the data add all eligible patients + # assign a number to each patient + df_visit_pat_pool <- df_pat_aggr_pool %>% + distinct(.data$study_id, visit = .data$max_visit_per_pat) %>% + left_join( + df_pat_aggr_pool, + by = join_by( + "visit" <= "max_visit_per_pat", + "study_id" == "study_id" + ) + ) %>% + mutate( + rwn = row_number(), + .by = c("study_id", "visit") + ) %>% + mutate( + max_rwn = max(.data$rwn, na.rm = TRUE), + .by = c("study_id", "visit") + ) + + # for each site create repetitions using cross join + # add patients with maximum visit back to each site + df_sim_prep <- df_pat_aggr_site %>% + distinct(.data$study_id, .data$site_number) %>% + cross_join(df_r) %>% + left_join( + df_pat_aggr_site, + by = c("study_id", "site_number"), + relationship = "many-to-many" + ) %>% + # add number of eligible patients from pool + left_join( + df_visit_pat_pool %>% + distinct(.data$study_id, .data$visit, .data$max_rwn), + by = c("study_id", max_visit_per_pat = "visit") + ) %>% + mutate( + # we generate a random number between 1 and the maximum number + # of eligible patients + rnd = runif(n()), + # transform to values between 1 and max number of patients + rnd_rwn = floor(.data$rnd * .data$max_rwn) + 1 + ) + + df_sim <- df_sim_prep %>% + left_join( + df_visit_pat_pool %>% + select(c("study_id", "visit", patnum_new = "patnum", "rwn")), + by = c(study_id = "study_id", max_visit_per_pat = "visit", rnd_rwn = "rwn") + ) %>% + select(c("study_id", "site_number", "rep", patnum = "patnum_new", visit = "max_visit_per_pat")) + + + # add appropriate visit with cumulative event and visit count for every new patient + df_calc <- df_sim %>% + left_join( + df_visit %>% + select(c("study_id", "patnum", "visit", "n_ae")), + by = c("study_id", "patnum", "visit") + ) %>% + # calculate site level event rates for every repetition + summarise( + events_per_visit_site_rep = sum(.data$n_ae, na.rm = TRUE) / sum(.data$visit, na.rm = TRUE), + .by = c("study_id", "rep", "site_number") + ) + + # join original with simulations + df_calc_aggr <- df_calc %>% + left_join( + df_calc_ori, + by = c("study_id", "site_number") + ) %>% + summarise( + # calculate probability by comparing original stats with simulated stats + # how many times simulated value below original value + prob_low = sum( + ifelse( + .data$events_per_visit_site_rep <= .data$events_per_visit_site_ori, + 1, 0 + ), + na.rm = TRUE + ) / n(), + events_per_visit_study = mean(.data$events_per_visit_site_rep, na.rm = TRUE), + .by = c("study_id", "site_number", "events_per_visit_site_ori", "events", + "visits", "n_pat") + ) %>% + rename(events_per_visit_site = "events_per_visit_site_ori") + + return(df_calc_aggr) +} + +#' benjamini hochberg p value correction using table operations +#'@keywords internal +p_adjust_bh_inframe <- function(df_eval, col, suffix) { + + any_probx <- any(str_detect(colnames(df_eval), "probx_")) + + stopifnot("probx_ prefix in colnames(df_eval) not allowed" = ! any_probx) + + if (inherits(df_eval, "data.frame")) { + fun_arrange <- arrange + } else { + fun_arrange <- window_order + } + + col_adj <- paste0(col, "_adj") + col_suffix <- paste0(col, suffix) + + df_out <- df_eval %>% + mutate(probx = .data[[col]]) %>% + fun_arrange(.data$study_id, .data$probx) %>% + max_rank("probx", "probx_n_detected") %>% + mutate( + probx_min = min(ifelse(.data$probx == 0, NA, .data$probx), na.rm = TRUE), + .by = "study_id" + ) %>% + mutate( + probx_fp = .data$probx * n(), + probx_fp = ifelse(.data$probx_fp == 0, .data$probx_min / 10, .data$probx_fp) + ) %>% + mutate( + probx_p_vs_fp_ratio = .data$probx_n_detected / .data$probx_fp, + probx_p_vs_fp_ratio = ifelse(is.na(.data$probx_p_vs_fp_ratio), + 0, .data$probx_p_vs_fp_ratio), + # it is possible to get ratios lower than one if p < expected fp + # this can happen by chance and is meaningless + probx_p_vs_fp_ratio = ifelse(.data$probx_p_vs_fp_ratio < 1, 1, .data$probx_p_vs_fp_ratio) + ) %>% + mutate( + !! as.name(col_adj) := 1 / .data$probx_p_vs_fp_ratio, + !! as.name(col_suffix) := 1 - .data[[col_adj]] + ) %>% + select(- starts_with("probx")) + + return(df_out) +} + + +#' Calculate Max Rank +#' @description +#' like rank() with ties.method = "max", works on tbl objects +#' @param df dataframe +#' @param col character column name to rank y +#' @param col_new character column name for rankings +#' @details +#' this is needed for hochberg p value adjustment. We need to assign higher +#' rank when multiple sites have same p value +#' +#' @export +#' @examples +#' +#'df <- tibble::tibble(s = c(1, 2, 2, 2, 5, 10)) %>% +#' dplyr::mutate( +#' rank = rank(s, ties.method = "max") +#' ) +#' +#'df %>% +#' max_rank("s", "max_rank") +#'\donttest{ +#'# Database +#'con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") +#' +#'dplyr::copy_to(con, df, "df") +#'max_rank(dplyr::tbl(con, "df"), "s", "max_rank") +#' +#'DBI::dbDisconnect(con) +#'} +max_rank <- function(df, col, col_new) { + + any_na <- get_any_na(df, col) + + stopifnot("NA in max_rank col" = ! any_na) + + df %>% + mutate( + rwn = row_number(), + ) %>% + mutate( + !! as.name(col_new) := max(.data$rwn, na.rm = TRUE), + .by = all_of(col) + ) %>% + select(- "rwn") +} diff --git a/R/simaerep.R b/R/simaerep.R index 80d051c..e0d12af 100644 --- a/R/simaerep.R +++ b/R/simaerep.R @@ -38,38 +38,24 @@ check_df_visit <- function(df_visit) { df_visit <- ungroup(df_visit) stopifnot( - all(c("study_id", "site_number", "patnum", "n_ae", "visit") %in% names(df_visit)) + all(c("study_id", "site_number", "patnum", "n_ae", "visit") %in% colnames(df_visit)) ) - cols_na <- df_visit %>% - summarise_at( - vars(c( - "study_id", - "site_number", - "patnum", - "n_ae", - "visit" - )), - anyNA - ) %>% + no_na_cols <- c( + "study_id", + "site_number", + "patnum", + "n_ae", + "visit" + ) + + cols_na <- purrr::map(no_na_cols, ~ get_any_na(df_visit, .)) %>% unlist() if (any(cols_na)) { - stop(paste("NA detected in columns:", paste(names(cols_na)[cols_na], collapse = ","))) + stop(paste("NA detected in columns:", paste(no_na_cols[cols_na], collapse = ","))) } - df_visit %>% - summarise_at( - vars(c( - "n_ae", - "visit" - )), - ~ is.numeric(.) - ) %>% - unlist() %>% - all() %>% - stopifnot("n_ae and vist columns must be numeric" = .) - df_visit %>% group_by(.data$study_id, .data$patnum) %>% summarise(n_sites = n_distinct(.data$site_number), .groups = "drop") %>% @@ -86,9 +72,22 @@ check_df_visit <- function(df_visit) { all() %>% stopifnot("visit numbering should start at 1" = .) - df_visit <- exp_implicit_missing_visits(df_visit) + if (inherits(df_visit, "data.frame")) { + df_visit %>% + summarise_at( + vars(c( + "n_ae", + "visit" + )), + ~ is.numeric(.) + ) %>% + unlist() %>% + all() %>% + stopifnot("n_ae and visit columns must be numeric" = .) - df_visit <- aggr_duplicated_visits(df_visit) + df_visit <- exp_implicit_missing_visits(df_visit) + df_visit <- aggr_duplicated_visits(df_visit) + } return(df_visit) } @@ -245,19 +244,39 @@ get_visit_med75 <- function(df_pat, method = "med75_adj", min_pat_pool = 0.2) { + if (inherits(df_pat, "data.frame")) { + fun_arrange <- arrange + } else { + fun_arrange <- window_order + } + + # we have to separate the aggregation steps into two steps + # we need to reference visit_med75 for counting patients with + # visit_med75 not all db backends may support this + + df_visit_med75 <- df_pat %>% + summarise( + visit_med75 = ceiling(median(.data$max_visit_per_pat) * 0.75), + .by = c("study_id", "site_number") + ) + df_site <- df_pat %>% - group_by(.data$study_id, .data$site_number) %>% + left_join( + df_visit_med75, + by = c("study_id", "site_number") + ) %>% summarise( n_pat = n_distinct(.data$patnum), - visit_med75 = ceiling(median(.data$max_visit_per_pat) * 0.75), - n_pat_with_med75 = n_distinct( - .data$patnum[.data$max_visit_per_pat >= .data$visit_med75] + n_pat_with_med75 = sum(ifelse( + .data$max_visit_per_pat >= .data$visit_med75, + 1, 0) ), - .groups = "drop" + .by = c("study_id", "site_number", "visit_med75") ) if (method == "med75_adj") { + df_site <- df_site %>% right_join( df_pat, @@ -265,26 +284,49 @@ get_visit_med75 <- function(df_pat, ) %>% mutate( pat_has_visit_med75 = ifelse(.data$max_visit_per_pat >= .data$visit_med75, 1, 0) - ) %>% - group_by(.data$study_id) %>% - mutate( - study_qup8_max_visit = quantile(.data$max_visit_per_pat, probs = c(1 - min_pat_pool)), - study_qup8_max_visit = round(.data$study_qup8_max_visit, 0) - ) %>% - group_by( - .data$study_id, - .data$site_number, - .data$n_pat, - .data$n_pat_with_med75, - .data$study_qup8_max_visit, + ) + + if (inherits(df_pat, "data.frame")) { + # this cannot be exactly replicated, we maintain this method so that results + # do not change compared to previous versions + df_qup8 <- df_site %>% + summarise( + study_qup8_max_visit = quantile(.data$max_visit_per_pat, probs = c(1 - min_pat_pool)), + study_qup8_max_visit = round(.data$study_qup8_max_visit, 0), + .by = "study_id" ) %>% + select(c("study_id", "study_qup8_max_visit")) + + } else { + # calculate max_visit that covers at least min_map_pool + # wer are not using quantile for db backend compatibility + df_qup8 <- df_site %>% + mutate( + rnk = percent_rank(.data$max_visit_per_pat), + .by = "study_id" + ) %>% + filter(.data$rnk <= 1 - .env$min_pat_pool) %>% + fun_arrange(.data$study_id, desc(.data$max_visit_per_pat)) %>% + filter(row_number() == 1, .by = "study_id") %>% + select(c("study_id", study_qup8_max_visit = "max_visit_per_pat")) + } + + df_site <- df_site %>% + left_join( + df_qup8, + by = c("study_id") + ) %>% summarise( + # from all patients that have a visit_med75 we take the smallest maximum visit_med75 = min( - .data$max_visit_per_pat[.data$pat_has_visit_med75 == 1] + ifelse(.data$pat_has_visit_med75 == 1, .data$max_visit_per_pat, NA), + na.rm = TRUE ), - .groups = "drop" + .by = c("study_id", "site_number", "n_pat", "n_pat_with_med75", "study_qup8_max_visit") ) %>% mutate( + # we correct visit_med75 so that it cannot exceed a visit with less than + # the minimum ratio of patients visit_med75 = ifelse( .data$visit_med75 > .data$study_qup8_max_visit, .data$study_qup8_max_visit, @@ -360,77 +402,118 @@ eval_sites <- function(df_sim_sites, df_out <- df_sim_sites - if ("pval" %in% names(df_out)) { - # nolint start - if (anyNA(df_out$pval)) { - warning_messages <- df_out %>% - filter(is.na(.data$pval)) %>% - mutate( - warning = paste0("\nstudy_id: ", .data$study_id, ", site_number: ", .data$site_number), - warning = paste0(.data$warning, ". pval == NA") - ) %>% - pull(.data$warning) + if ("pval" %in% colnames(df_out)) { - warning(warning_messages) - } - # nolint end + warning_na(df_out, "pval") df_out <- df_out %>% - group_by(.data$study_id) %>% - arrange(.data$study_id, .data$pval) %>% - mutate( - pval_adj = p.adjust(.data$pval, method = method), - pval_prob_ur = 1 - .data$pval_adj - ) + p_adjust("pval", "_prob_ur", method = method) } - if ("prob_low" %in% names(df_out)) { - # nolint start - if (anyNA(df_out$prob_low)) { - warning_messages <- df_out %>% - filter(is.na(.data$prob_low)) %>% - mutate( - warning = paste0("\nstudy_id: ", .data$study_id, ", site_number: ", .data$site_number), - warning = paste0(.data$warning, ", prob_low == NA") - ) %>% - distinct() %>% - pull(.data$warning) %>% - paste(collapse = "\n") + if ("prob_low" %in% colnames(df_out)) { - warning(warning_messages) - } - # nolint end + warning_na(df_out, "prob_low") df_out <- df_out %>% - group_by(.data$study_id) %>% - arrange(.data$study_id, .data$prob_low) %>% - mutate( - prob_low_adj = p.adjust(.data$prob_low, method = method), - prob_low_prob_ur = 1 - .data$prob_low_adj - ) + p_adjust("prob_low", "_prob_ur", method = method) + if (! under_only) { + if (any(str_detect(colnames(df_out), "med75"))) { + df_out <- df_out %>% + mutate(study_site_ae_equal = .data$mean_ae_site_med75 == .data$mean_ae_study_med75) + } else { + df_out <- df_out %>% + mutate(study_site_ae_equal = .data$events_per_visit_site == .data$events_per_visit_study) + } + df_out <- df_out %>% mutate( prob_high = 1 - .data$prob_low, + # when study and site values are equl e.g. both zero, no over-reporting possible prob_high = ifelse( - .data$mean_ae_site_med75 == .data$mean_ae_study_med75, + .data$study_site_ae_equal, 1, .data$prob_high ) ) %>% - arrange(.data$study_id, .data$prob_high) %>% - mutate( - prob_high_adj = p.adjust(.data$prob_high, method = method), - prob_high_prob_or = 1 - .data$prob_high_adj - ) + select(- "study_site_ae_equal") %>% + p_adjust("prob_high", "_prob_or", method = method) } } - return(ungroup(df_out)) + if (inherits(df_out, "data.frame")) { + fun_arrange <- arrange + } else { + fun_arrange <- window_order + } + + df_out <- df_out %>% + fun_arrange(.data$study_id, .data$site_number) + + return(df_out) +} + +#'@keywords internal +p_adjust <- function(df, col, suffix, method = "BH") { + + if (inherits(df, "data.frame")) { + + col_adj <- paste0(col, "_adj") + col_suffix <- paste0(col, suffix) + + df_out <- df %>% + mutate( + !! as.name(col_adj) := p.adjust(.data[[col]], method = method), + !! as.name(col_suffix) := 1 - .data[[col_adj]], + .by = "study_id" + ) + + } else { + df_out <- p_adjust_bh_inframe(df, col, suffix) + } + + return(df_out) + } +#'@keywords internal +warning_na <- function(df, col) { + + any_na <- get_any_na(df, col) + + if (any_na) { + warning_messages <- df %>% + filter(is.na(.data[[col]])) %>% + mutate( + warning = paste0("\nstudy_id: ", .data$study_id, ", site_number: ", .data$site_number), + warning = paste0(.data$warning, ", ", col, "== NA") + ) %>% + distinct() %>% + pull(.data$warning) %>% + paste(collapse = "\n") + + warning(warning_messages) + } +} + +get_any_na <- function(df, col) { + # dbplyr throws a warning for not using na.rm = TRUE + suppressWarnings({ + df %>% + mutate( + any_na = ifelse(is.na(.data[[col]]), 1, 0) + ) %>% + summarise( + any_na = sum(.data$any_na, na.rm = TRUE) + ) %>% + mutate( + any_na = .data$any_na > 0 + ) %>% + pull(.data$any_na) + }) +} #' @title Evaluate sites. #' @description Correct under-reporting probabilities by the expected number of @@ -538,6 +621,8 @@ eval_sites_deprecated <- function(df_sim_sites, return(ungroup(df_out)) } + + #' @title Get empirical cumulative distribution values of pval or prob_lower #' @description Test function, test applicability of poisson test, by calculating #' - the bootstrapped probability of obtaining a specific p-value or lower, use @@ -686,6 +771,11 @@ prob_lower_site_ae_vs_study_ae <- function(site_ae, study_ae, r = 1000, parallel mean_ae_site <- mean(site_ae, na.rm = TRUE) mean_ae_study <- mean(study_ae, na.rm = TRUE) + # this can happen when no patients with site visit_med75 were found in study + if (is.na(mean_ae_study)) { + return(NA) + } + if (under_only) { # we are not interested in cases where site AE is greater study AE if (mean_ae_site > mean_ae_study) { @@ -1229,8 +1319,26 @@ site_aggr <- function(df_visit, # Checks ---------------------------------------------------------- stopifnot( - method %in% c("med75", "med75_adj") + method %in% c("med75", "med75_adj", "max") ) + + if (method == "max") { + + df_site <- df_visit %>% + mutate( + max_visit = max(.data$visit, na.rm = TRUE), + pat_has_max_visit = ifelse(visit == .data$max_visit, patnum, NA), + .by = c("study_id", "site_number") + ) %>% + summarise( + n_pat = n_distinct(.data$patnum), + n_pat_with_max_visit = n_distinct(.data$pat_has_max_visit), + .by = c("study_id", "site_number", "max_visit") + ) + + return(df_site) + } + if (check) { df_visit <- check_df_visit(df_visit) } @@ -1301,6 +1409,11 @@ poiss_test_site_ae_vs_study_ae <- function(site_ae, mean_ae_site <- mean(site_ae, na.rm = TRUE) mean_ae_study <- mean(study_ae, na.rm = TRUE) + # this can happen when no patients with site visit_med75 were found in study + if (is.na(mean_ae_study)) { + return(NA) + } + # we are not interested in cases where site AE is greater study AE if (mean_ae_site > mean_ae_study) { pval <- 1 diff --git a/R/simaerep_plot.R b/R/simaerep_plot.R index ceb5ea9..9b8da05 100644 --- a/R/simaerep_plot.R +++ b/R/simaerep_plot.R @@ -446,11 +446,29 @@ plot_study <- function(df_visit, # TODO: parametrize scores, fix legend + studies <- df_visit %>% + distinct(.data$study_id) %>% + pull(.data$study_id) + + stopifnot(study %in% studies) + stopifnot(study %in% studies) + stopifnot(study %in% studies) + + # filter studies ----------------------------------------------------------- + + df_visit <- df_visit %>% + filter(.data$study_id %in% study) %>% + collect() + df_visit <- check_df_visit(df_visit) - stopifnot(study %in% unique(df_visit$study_id)) - stopifnot(study %in% unique(df_site$study_id)) - stopifnot(study %in% unique(df_eval$study_id)) + df_site <- df_site %>% + filter(.data$study_id %in% study) %>% + collect() + + df_eval <- df_eval %>% + filter(.data$study_id %in% study) %>% + collect() # alert level ------------------------------------------------------------- @@ -467,6 +485,7 @@ plot_study <- function(df_visit, "alert_level_study"))) } + # fill in pvalues when missing -------------------------------------------- if (! "pval_prob_ur" %in% colnames(df_eval)) { @@ -485,16 +504,38 @@ plot_study <- function(df_visit, df_site <- df_site %>% mutate_at(vars(c("study_id", "site_number")), as.character) - # filter studies ----------------------------------------------------------- - df_visit <- df_visit %>% - filter(.data$study_id %in% study) + # adjust to visit_med75 or alternative --------------------------------------- - df_site <- df_site %>% - filter(.data$study_id %in% study) + if (all(c("events_per_visit_study", "events_per_visit_site") %in% colnames(df_eval))) { + col_mean_site <- "events_per_visit_site" + } else { + col_mean_site <- "mean_ae_site_med75" + } - df_eval <- df_eval %>% - filter(.data$study_id %in% study) + if ("visit_med75" %in% colnames(df_site)) { + col_visit <- "visit_med75" + + df_pat <- pat_aggr(df_visit) + + df_visit_med75 <- df_visit %>% + left_join( + df_site %>% + select(c("study_id", "site_number", "visit_med75")), + by = c("study_id", "site_number") + ) %>% + left_join( + df_pat %>% + select(c("study_id", "site_number", "patnum", "max_visit_per_pat")), + by = c("study_id", "site_number", "patnum") + ) %>% + filter(.data$visit <= .data$visit_med75, .data$max_visit_per_pat >= .data$visit_med75) %>% + select(- c("visit_med75", "max_visit_per_pat")) + + } else { + col_visit <- "max_visit" + df_visit_med75 <- df_visit + } # ordered sites ------------------------------------------------------------- @@ -504,28 +545,24 @@ plot_study <- function(df_visit, if (n_site_ur_gr_0p5 > 0) { sites_ordered <- df_eval %>% - arrange(.data$study_id, desc(.data[[prob_col]]), .data$mean_ae_site_med75) %>% + arrange(.data$study_id, desc(.data[[prob_col]]), .data[[col_mean_site]]) %>% filter(.data[[prob_col]] > 0.5) %>% head(n_sites) %>% .$site_number } else { sites_ordered <- df_eval %>% - arrange(.data$study_id, desc(.data[[prob_col]]), .data$mean_ae_site_med75) %>% + arrange(.data$study_id, desc(.data[[prob_col]]), .data[[col_mean_site]]) %>% head(6) %>% .$site_number } # mean AE development ------------------------------------------------------ - df_mean_ae_dev_site <- df_visit %>% - group_by(.data$study_id, .data$site_number, .data$patnum) %>% - mutate(max_visit_per_pat = max(.data$visit)) %>% - ungroup() %>% + df_mean_ae_dev_site <- df_visit_med75 %>% left_join(df_site, by = c("study_id", "site_number")) %>% - filter(.data$visit <= .data$visit_med75, .data$max_visit_per_pat >= .data$visit_med75) %>% group_by(.data$study_id, .data$site_number, - .data$visit_med75, + .data[[col_visit]], .data$n_pat, .data$visit, .data$alert_level_site) %>% @@ -533,17 +570,17 @@ plot_study <- function(df_visit, ungroup() df_mean_ae_dev_study <- df_visit %>% - group_by(.data$study_id, - .data$site_number, - .data$patnum) %>% - mutate(max_visit_per_pat = max(.data$visit)) %>% + left_join( + df_site, + by = c("study_id", "site_number") + ) %>% group_by(.data$study_id) %>% mutate( - visit_med75 = ceiling(median(.data$max_visit_per_pat) * 0.75), + visit_max = median(.data[[col_visit]]), n_pat = n_distinct(.data$patnum) ) %>% + filter(.data$visit <= .data$visit_max) %>% ungroup() %>% - filter(.data$visit <= .data$visit_med75, .data$max_visit_per_pat >= .data$visit_med75) %>% group_by(.data$study_id) %>% mutate(n_pat_with_med75 = n_distinct(.data$patnum)) %>% group_by(.data$study_id, @@ -581,8 +618,11 @@ plot_study <- function(df_visit, ) df_mean_ae_dev_site <- df_mean_ae_dev_site %>% - select(- "visit_med75") %>% - left_join(df_eval, by = c("study_id", "site_number")) + left_join( + df_eval %>% + select(- any_of(col_visit)), + by = c("study_id", "site_number") + ) # we have to split site ae dev up because alert sites get plotted # over if there are many sites @@ -600,8 +640,12 @@ plot_study <- function(df_visit, "alert_level_study")) %>% distinct() + if (! "n_pat_with_med75" %in% colnames(df_site)) { + df_site$n_pat_with_med75 <- df_site$n_pat + } + df_label <- df_mean_ae_dev_site %>% - filter(.data$visit == .data$visit_med75, .data$site_number %in% sites_ordered) %>% + filter(.data$visit == .data[[col_visit]], .data$site_number %in% sites_ordered) %>% select(c("study_id", "site_number", "visit", @@ -614,9 +658,9 @@ plot_study <- function(df_visit, "n_pat", "n_pat_with_med75")) %>% left_join( - select(df_eval, - c( + select(df_eval, - any_of(c( "n_pat", - "n_pat_with_med75")) + "n_pat_with_med75"))) , by = c("study_id", "site_number") ) %>% left_join(df_alert, by = c("study_id", "site_number")) %>% @@ -680,12 +724,6 @@ plot_study <- function(df_visit, data = df_label, color = "grey" ) + - annotate("text", - x = 0.2 * max_visit_study, - y = 0.9 * max_ae_study, - label = paste0(n_pat_with_med75, "/", n_pat), - na.rm = TRUE - ) + annotate("label", x = 0.5 * max_visit_study, y = 0.9 * max_ae_study, @@ -869,6 +907,9 @@ plot_visit_med75 <- function(df_visit, ungroup() df_label <- df_plot %>% + mutate( + n_pat_with_med75 = ifelse(is.na(.data$n_pat_with_med75), .data$n_pat, .data) + ) %>% select(c( "study_id", "site_number", diff --git a/R/simulate_test_data.R b/R/simulate_test_data.R index 8006828..db22c20 100644 --- a/R/simulate_test_data.R +++ b/R/simulate_test_data.R @@ -303,7 +303,8 @@ sim_ur_scenarios <- function(df_portf, parallel = FALSE, progress = TRUE, site_aggr_args = list(), - eval_sites_args = list()) { + eval_sites_args = list(), + check = TRUE) { # checks stopifnot("all site_aggr_args list items must be named" = all(names(site_aggr_args) != "")) @@ -312,19 +313,25 @@ sim_ur_scenarios <- function(df_portf, stopifnot(length(extra_ur_sites) == 1) extra_ur_sites <- as.integer(extra_ur_sites) - if (progress) { message("aggregating site level") } - df_visit <- check_df_visit(df_portf) + if (check) { + df_visit <- check_df_visit(df_portf) + } else { + df_visit <- df_portf + } + # get visit_med75 and number elibible patients df_site <- do.call(site_aggr, c(list(df_visit = df_visit), site_aggr_args)) if (progress) { message("prepping for simulation") } + # for every patient at site add AE count at visit_med75 to site integer vector + # same for study df_sim_prep <- prep_for_sim(df_site = df_site, df_visit = df_visit) if (progress) { @@ -343,6 +350,11 @@ sim_ur_scenarios <- function(df_portf, n_sites = n_distinct(.data$site_number), .groups = "drop") + # free up RAM + remove(df_portf) + remove(df_visit) + gc() + ur_rate <- ur_rate[ur_rate > 0] df_grid_gr0 <- df_site %>% @@ -373,11 +385,12 @@ sim_ur_scenarios <- function(df_portf, df_grid <- bind_rows(df_grid_0, df_grid_gr0) + # we join the parameter grid with site and study ae count vectors df_scen_prep <- df_sim_prep %>% left_join(df_grid, by = c("study_id", "site_number")) # generating scenarios - + # manipulate site and study ae count vectors according to scenario parameter df_scen <- df_scen_prep %>% mutate( scenarios = purrr::pmap( @@ -410,6 +423,8 @@ sim_ur_scenarios <- function(df_portf, ungroup() %>% select(- c("study_id_gr", "site_number_gr")) + + # generate prob_low for every scenario parameter with_progress_cnd( ls_df_sim_sites <- purrr_bar( df_sim_sites$data, @@ -432,17 +447,78 @@ sim_ur_scenarios <- function(df_portf, message("evaluating stats") } + # every row is one scenario in order to apply multiplicity correction + # we need to join each site-level scenario back into the study context + # with regular under-reporting score + df_sim_sites <- bind_rows(ls_df_sim_sites) - df_eval <- do.call(eval_sites, c(list(df_sim_sites = df_sim_sites), eval_sites_args)) %>% + df_sim_sites_ur0 <- df_sim_sites %>% #nolint + filter(.data$extra_ur_sites == 0, + .data$frac_pat_with_ur == 0, + .data$ur_rate == 0) %>% + select(- c("extra_ur_sites", "frac_pat_with_ur", "ur_rate")) + + df_eval_prep <- df_sim_sites %>% + mutate( + study_id_gr = .data$study_id, + site_number_gr = .data$site_number + ) %>% + group_by( + .data$study_id_gr, + .data$site_number_gr, + .data$extra_ur_sites, + .data$frac_pat_with_ur, + .data$ur_rate + ) %>% + nest() %>% + ungroup() %>% + mutate( + # add site scores to study score w/o same site + eval = map2( + .data$study_id_gr, .data$site_number_gr, + ~ filter(df_sim_sites_ur0, study_id == .x, site_number != .y) + ), + eval = map2( + .data$data, .data$eval, + bind_rows + ) + ) %>% + select(- "data") + + # apply eval sites for every scenario + df_eval <- df_eval_prep %>% + mutate( + eval = map( + .data$eval, + ~ do.call(eval_sites, c(list(df_sim_sites = .), eval_sites_args)), + .progess = progress + ), + # filter back to site level + eval = map2(.data$eval, .data$site_number_gr, ~ filter(.x, site_number == .y)) + ) %>% + select(- c("study_id_gr", "site_number_gr")) %>% + unnest(eval) %>% arrange( .data$study_id, .data$site_number, .data$extra_ur_sites, .data$frac_pat_with_ur, .data$ur_rate + ) %>% + select( + c( + "study_id", + "site_number", + "extra_ur_sites", + "frac_pat_with_ur", + "ur_rate" + ), + everything() ) + stopifnot(nrow(df_eval) == nrow(df_grid)) + return(df_eval) } @@ -862,3 +938,62 @@ get_portf_perf <- function(df_scen, stat = "prob_low_prob_ur", fpr = c(0.001, 0. bind_rows(df_prep_0, df_prep_gr0) %>% arrange(.data$fpr, .data$ur_rate) } +#' simulate under-reporting +#'@param df_visit, dataframe +#'@param study_id, character +#'@param site_number, character +#'@param ur_rate, double +#'@description we remove a fraction of AEs from a specific site +#'@details we determine the absolute number of AEs per patient for removal. +#'Then them remove them at the first visit. +#'We intentionally allow fractions +#'@export +#'@examples +#' df_visit <- sim_test_data_study(n_pat = 100, n_sites = 10, +#' frac_site_with_ur = 0.4, ur_rate = 0.6) +#' +#' df_visit$study_id <- "A" +#' +#' df_ur <- sim_ur(df_visit, "A", site_number = "S0001", ur_rate = 0.35) +#' +#' # Example cumulated AE for first patient with 35% under-reporting +#' df_ur[df_ur$site_number == "S0001" & df_ur$patnum == "P000001",]$n_ae +#' +#' # Example cumulated AE for first patient with no under-reporting +#' df_visit[df_visit$site_number == "S0001" & df_visit$patnum == "P000001",]$n_ae +#' +sim_ur <- function(df_visit, study_id, site_number, ur_rate) { + + df_visit <- df_visit %>% + mutate(n_ae = as.numeric(.data$n_ae)) + + df_visit_study <- df_visit %>% + filter(study_id == .env$study_id, site_number != .env$site_number) + + df_visit_site <- df_visit %>% + filter(study_id == .env$study_id, site_number == .env$site_number) + + # determine total AE per patient + # convert cumulative counts to single increments + # first value needs to be AE start value + # from start value substract ae count * ur_rate + # convert back to cumulative count + df_visit_site_rem <- df_visit_site %>% + mutate( + n_ae_pat = max(ifelse(visit == max(visit), n_ae, 0)), + n_ae_rem = .data$n_ae - lag(.data$n_ae), + n_ae_rem = ifelse( + .data$visit == 1, + .data$n_ae - (.data$n_ae_pat * .env$ur_rate), + .data$n_ae_rem), + n_ae = cumsum(.data$n_ae_rem), + .by = "patnum" + ) %>% + mutate( + n_ae = ifelse(n_ae < 0, 0, n_ae) + ) %>% + select(- "n_ae_rem", - "n_ae_pat") + + bind_rows(df_visit_study, df_visit_site_rem) + +} diff --git a/README.Rmd b/README.Rmd index acf0942..ac7736f 100644 --- a/README.Rmd +++ b/README.Rmd @@ -54,21 +54,16 @@ devtools::install_github("openpharma/simaerep") [![IMPALA logo](https://impala-consortium.org/wp-content/uploads/IMPALA-logo-x2.png)](https://impala-consortium.org/) -## Publication +## Publications -Koneswarakantha, B., Barmaz, Y., Ménard, T. et al. Follow-up on the Use of Advanced Analytics for Clinical Quality Assurance: Bootstrap Resampling to Enhance Detection of Adverse Event Under-Reporting. Drug Saf (2020). [https://doi.org/10.1007/s40264-020-01011-5](https://doi.org/10.1007/s40264-020-01011-5) +Koneswarakantha, B., Adyanthaya, R., Emerson, J. et al. An Open-Source R Package for Detection of Adverse Events Under-Reporting in Clinical Trials: Implementation and Validation by the IMPALA (Inter coMPany quALity Analytics) Consortium. Ther Innov Regul Sci 58, 591–599 (2024). [https://doi.org/10.1007/s43441-024-00631-8](https://doi.org/10.1007/s43441-024-00631-8) -## Vignettes/ Articles/ Tutorials +Koneswarakantha, B., Barmaz, Y., Ménard, T. et al. Follow-up on the Use of Advanced Analytics for Clinical Quality Assurance: Bootstrap Resampling to Enhance Detection of Adverse Event Under-Reporting. Drug Saf (2020). [https://doi.org/10.1007/s40264-020-01011-5](https://doi.org/10.1007/s40264-020-01011-5) -[video presentation 15 min](https://vimeo.com/776275791?embedded=true&source=vimeo_logo&owner=189858368) +## Tutorials -- [Introduction](https://openpharma.github.io/simaerep/articles/intro.html) -- [Usability Limits](https://openpharma.github.io/simaerep/articles/usability_limits.html) -- [Check Poisson Test Applicability](https://openpharma.github.io/simaerep/articles/check_poisson.html) -- [SAS Files](https://openpharma.github.io/simaerep/articles/sas_files.html) -- [Adjusted Evaluation Point visit_med75](https://openpharma.github.io/simaerep/articles/visit_med75.html) -- [Aggregate AEs by Days or Visit?](https://openpharma.github.io/simaerep/articles/visits_or_days.html) -- [Portfolio Performance](https://openpharma.github.io/simaerep/articles/portfolio_perf.html) +- [video presentation 15 min](https://vimeo.com/776275791?embedded=true&source=vimeo_logo&owner=189858368) +- [Documentation with Vignettes](https://openpharma.github.io/simaerep) ## Validation Report diff --git a/README.md b/README.md index d3a2eed..b419a18 100644 --- a/README.md +++ b/README.md @@ -59,31 +59,24 @@ testimonials](https://impala-consortium.org/clinical-safety-reporting-work-produ [![IMPALA logo](https://impala-consortium.org/wp-content/uploads/IMPALA-logo-x2.png)](https://impala-consortium.org/) -## Publication +## Publications + +Koneswarakantha, B., Adyanthaya, R., Emerson, J. et al. An Open-Source R +Package for Detection of Adverse Events Under-Reporting in Clinical +Trials: Implementation and Validation by the IMPALA (Inter coMPany +quALity Analytics) Consortium. Ther Innov Regul Sci 58, 591–599 (2024). + Koneswarakantha, B., Barmaz, Y., Ménard, T. et al. Follow-up on the Use of Advanced Analytics for Clinical Quality Assurance: Bootstrap Resampling to Enhance Detection of Adverse Event Under-Reporting. Drug Saf (2020). -## Vignettes/ Articles/ Tutorials - -[video presentation 15 -min](https://vimeo.com/776275791?embedded=true&source=vimeo_logo&owner=189858368) - -- [Introduction](https://openpharma.github.io/simaerep/articles/intro.html) -- [Usability - Limits](https://openpharma.github.io/simaerep/articles/usability_limits.html) -- [Check Poisson Test - Applicability](https://openpharma.github.io/simaerep/articles/check_poisson.html) -- [SAS - Files](https://openpharma.github.io/simaerep/articles/sas_files.html) -- [Adjusted Evaluation Point - visit_med75](https://openpharma.github.io/simaerep/articles/visit_med75.html) -- [Aggregate AEs by Days or - Visit?](https://openpharma.github.io/simaerep/articles/visits_or_days.html) -- [Portfolio - Performance](https://openpharma.github.io/simaerep/articles/portfolio_perf.html) +## Tutorials + +- [video presentation 15 + min](https://vimeo.com/776275791?embedded=true&source=vimeo_logo&owner=189858368) +- [Documentation with Vignettes](https://openpharma.github.io/simaerep) ## Validation Report diff --git a/_pkgdown.yml b/_pkgdown.yml index 6fd5d48..2ee10f3 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -20,6 +20,7 @@ navbar: href: reference/index.html template: + bootstrap: 5 params: ganalytics: UA-123997499-2 @@ -30,14 +31,16 @@ articles: navbar: ~ contents: - intro + - inframe + - over + - performance + - funnel_perf - sas_files - portfolio_perf - visit_med75 - usability_limits - check_poisson - visits_or_days - - gsm_perf - - over reference: - title: S3 Interface @@ -50,7 +53,9 @@ reference: contents: - site_aggr - sim_sites + - sim_inframe - eval_sites + - prune_to_visit_med75_inframe - plot_study - check_df_visit - title: Simulate Data @@ -64,6 +69,7 @@ reference: contents: - sim_studies - sim_ur_scenarios + - sim_ur - get_portf_perf - title: Helper Functions desc: These functions are intended to be called by other functions. @@ -87,6 +93,7 @@ reference: - orivisit - is_simaerep - is_orivisit + - max_rank - title: Deprecated contents: - eval_sites_deprecated diff --git a/docs/404.html b/docs/404.html index c942fa5..e474e67 100644 --- a/docs/404.html +++ b/docs/404.html @@ -4,7 +4,7 @@ - + Page not found (404) • simaerep @@ -12,17 +12,13 @@ - - - - - - + + + + + - - - - -
-
-
- +
+
+
Content not found. Please use links in the navbar. -
- - - +
- -
- - + diff --git a/docs/LICENSE-text.html b/docs/LICENSE-text.html index de9f7cd..5040402 100644 --- a/docs/LICENSE-text.html +++ b/docs/LICENSE-text.html @@ -1,128 +1,76 @@ -License • simaerep - - -
-
-
- + + +
+
+
+
YEAR: 2020
 COPYRIGHT HOLDER: F. Hoffmann-La Roche Ltd and simaerep authors
 
-
+
- +
- - -
-
- - diff --git a/docs/LICENSE.html b/docs/LICENSE.html index 8bbd87b..27ddb70 100644 --- a/docs/LICENSE.html +++ b/docs/LICENSE.html @@ -1,98 +1,54 @@ -MIT License • simaerep - - -
-
-
- + + +
+
+
+
@@ -103,30 +59,22 @@

MIT License

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.

-
+
- +
- - -
-
- - diff --git a/docs/articles/check_poisson.html b/docs/articles/check_poisson.html index 0c5864e..d7078d5 100644 --- a/docs/articles/check_poisson.html +++ b/docs/articles/check_poisson.html @@ -4,7 +4,7 @@ - + Check Poisson Test Applicability • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
-
## # A tibble: 30,000 × 8
 ##        r study_id     site_number visit_med75 n_pat_with_med75 n_pat_study  pval
-##    <dbl> <chr>        <chr>             <dbl>            <int>       <dbl> <dbl>
-##  1     1 ae_per_visi… S0001                15                9         893 1    
-##  2     1 ae_per_visi… S0002                15                7         895 1    
-##  3     1 ae_per_visi… S0003                16                9         840 1    
-##  4     1 ae_per_visi… S0004                14               10         930 1    
-##  5     1 ae_per_visi… S0005                17               10         751 0.731
-##  6     1 ae_per_visi… S0006                16               10         839 0.372
-##  7     1 ae_per_visi… S0007                14               10         930 1    
-##  8     1 ae_per_visi… S0008                15                9         893 1    
-##  9     1 ae_per_visi… S0009                16                9         840 1    
-## 10     1 ae_per_visi… S0010                18                8         668 1    
+##    <dbl> <chr>        <chr>             <dbl>            <dbl>       <dbl> <dbl>
+##  1     1 ae_per_visi… S0001                15               10         905 1    
+##  2     1 ae_per_visi… S0002                15                9         906 0.673
+##  3     1 ae_per_visi… S0003                17                8         760 1    
+##  4     1 ae_per_visi… S0004                15               10         905 1    
+##  5     1 ae_per_visi… S0005                18                9         678 0.850
+##  6     1 ae_per_visi… S0006                16                9         828 1    
+##  7     1 ae_per_visi… S0007                17                9         759 1    
+##  8     1 ae_per_visi… S0008                16                9         828 1    
+##  9     1 ae_per_visi… S0009                14               10         942 1    
+## 10     1 ae_per_visi… S0010                17               10         758 0.714
 ## # ℹ 29,990 more rows
 ## # ℹ 1 more variable: prob_low <dbl>
@@ -306,17 +258,17 @@

Check p-value Probabilitiesdf_check_pval

## # A tibble: 300 × 11
 ##    study_id    site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75
-##    <chr>       <chr>       <int>            <int>       <dbl>              <dbl>
-##  1 ae_per_vis… S0001          10                9          15              0.444
-##  2 ae_per_vis… S0002          10                7          15              0.429
-##  3 ae_per_vis… S0003          10                9          16              0.222
-##  4 ae_per_vis… S0004          10               10          14              0.6  
-##  5 ae_per_vis… S0005          10               10          17              0.5  
-##  6 ae_per_vis… S0006          10               10          16              0.7  
-##  7 ae_per_vis… S0007          10               10          14              0.4  
-##  8 ae_per_vis… S0008          10                9          15              0.444
-##  9 ae_per_vis… S0009          10                9          16              0.444
-## 10 ae_per_vis… S0010          10                8          18              0.625
+##    <chr>       <chr>       <int>            <dbl>       <dbl>              <dbl>
+##  1 ae_per_vis… S0001          10               10          15              0.3  
+##  2 ae_per_vis… S0002          10                9          15              0.667
+##  3 ae_per_vis… S0003          10                8          17              0.375
+##  4 ae_per_vis… S0004          10               10          15              0.3  
+##  5 ae_per_vis… S0005          10                9          18              0.222
+##  6 ae_per_vis… S0006          10                9          16              0.556
+##  7 ae_per_vis… S0007          10                9          17              0.667
+##  8 ae_per_vis… S0008          10                9          16              0.333
+##  9 ae_per_vis… S0009          10               10          14              0.3  
+## 10 ae_per_vis… S0010          10               10          17              0.2  
 ## # ℹ 290 more rows
 ## # ℹ 5 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>,
 ## #   pval <dbl>, prob_low <dbl>, pval_ecd <dbl>
@@ -345,17 +297,17 @@

Perform Same Check on df_check_prob

## # A tibble: 300 × 11
 ##    study_id    site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75
-##    <chr>       <chr>       <int>            <int>       <dbl>              <dbl>
-##  1 ae_per_vis… S0001          10                9          15              0.444
-##  2 ae_per_vis… S0002          10                7          15              0.429
-##  3 ae_per_vis… S0003          10                9          16              0.222
-##  4 ae_per_vis… S0004          10               10          14              0.6  
-##  5 ae_per_vis… S0005          10               10          17              0.5  
-##  6 ae_per_vis… S0006          10               10          16              0.7  
-##  7 ae_per_vis… S0007          10               10          14              0.4  
-##  8 ae_per_vis… S0008          10                9          15              0.444
-##  9 ae_per_vis… S0009          10                9          16              0.444
-## 10 ae_per_vis… S0010          10                8          18              0.625
+##    <chr>       <chr>       <int>            <dbl>       <dbl>              <dbl>
+##  1 ae_per_vis… S0001          10               10          15              0.3  
+##  2 ae_per_vis… S0002          10                9          15              0.667
+##  3 ae_per_vis… S0003          10                8          17              0.375
+##  4 ae_per_vis… S0004          10               10          15              0.3  
+##  5 ae_per_vis… S0005          10                9          18              0.222
+##  6 ae_per_vis… S0006          10                9          16              0.556
+##  7 ae_per_vis… S0007          10                9          17              0.667
+##  8 ae_per_vis… S0008          10                9          16              0.333
+##  9 ae_per_vis… S0009          10               10          14              0.3  
+## 10 ae_per_vis… S0010          10               10          17              0.2  
 ## # ℹ 290 more rows
 ## # ℹ 5 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>,
 ## #   pval <dbl>, prob_low <dbl>, prob_low_ecd <dbl>
@@ -387,35 +339,26 @@

Conclusionplan(sequential)

- - - - + -
- - + diff --git a/docs/articles/check_poisson_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/check_poisson_files/figure-html/unnamed-chunk-4-1.png index ef70787..daaa523 100644 Binary files a/docs/articles/check_poisson_files/figure-html/unnamed-chunk-4-1.png and b/docs/articles/check_poisson_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/articles/check_poisson_files/figure-html/unnamed-chunk-5-1.png b/docs/articles/check_poisson_files/figure-html/unnamed-chunk-5-1.png index 12ca24a..2b9316f 100644 Binary files a/docs/articles/check_poisson_files/figure-html/unnamed-chunk-5-1.png and b/docs/articles/check_poisson_files/figure-html/unnamed-chunk-5-1.png differ diff --git a/docs/articles/funnel_perf.html b/docs/articles/funnel_perf.html new file mode 100644 index 0000000..95cc4d6 --- /dev/null +++ b/docs/articles/funnel_perf.html @@ -0,0 +1,2030 @@ + + + + + + + +Comparing {simaerep} and Funnel Plot Performance • simaerep + + + + + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + + +
+

Install {gsm} +

+
+devtools::install_github("Gilead-BioStats/gsm@v1.9.2", ref = "main")
+
+
+

Introduction +

+

The {gsm} R +package provides a standardized Risk Based Quality Monitoring (RBQM) +framework for clinical trials that pairs a flexible data pipeline with +robust reports. It also uses Funnel Plots to flag outliers which provide +broader tolerance limits for sites with low exposure and narrower limits +for sites with higher exposure. This method is different to the event +rate based limits we have used in previous heuristics to measure +{simaerep} performance. Funnel plots are discussed in greater detail by +Zink et +al. 2018

+

One of the draw backs of using funnel plots for flagging is that they +assume that the AE rate remains constant over the course of the +study.

+
+
+

Prepare Data +

+
+

Load Portfolio Configurations +

+

We have prepared a snapshot of the AE reporting configuration of our +current portfolio. For each study we have also measured a visit-specific +AE rate which allows us to generate a synthetic portfolio with flexible +AE rates across a study.

+
+df_config <- readr::read_csv("ae_conf_20240220.csv")
+df_ae_rates <- readr::read_csv("ae_rates_20240220.csv")
+
+df_config %>%
+  head(25) %>%
+  knitr::kable()
+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
study_idae_per_visit_meansite_numbermax_visit_sdmax_visit_meann_pat
00010.297380647463.535533916.500002
00010.297380647470.707106827.500002
00010.297380647500.500000019.250004
00010.297380648158.376554622.166676
00010.297380648160.000000027.000001
00010.297380648170.000000031.000001
00010.297380648180.000000030.000001
00010.297380648912.380476122.000007
00010.297380648930.000000020.000002
00010.297380649321.095445126.800005
00010.297380649411.732050827.000003
00010.297380649421.414213625.000002
00010.297380649430.000000025.000001
00010.297380649660.000000018.000001
00010.297380649670.816496618.000004
00010.297380649680.000000024.000001
00010.297380649691.527525221.333333
00010.297380649841.707825130.750004
00010.297380649858.908797221.166676
00010.297380649860.577350320.333333
00010.297380649881.414213619.000002
00010.297380650792.160246923.000004
00010.297380650800.000000023.000001
00010.297380650810.000000023.000001
00010.297380650821.414213622.000002
+
+df_ae_rates %>%
+  head(25) %>%
+  knitr::kable()
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
study_idcum_visitae_raten_pat
000110.017699113
000120.088496113
000130.230088113
000140.223214112
000150.187500112
000160.196429112
000170.396396111
000180.207207111
000190.216216111
0001100.315315111
0001110.263636110
0001120.318182110
0001130.345455110
0001140.379630108
0001150.401869107
0001160.485981107
0001170.383178107
0001180.396226106
0001190.370000100
0001200.43956091
0001210.36986373
0001220.37096862
0001230.34545555
0001240.44680947
0001250.29268341
+
+
+

Simulate Portfolio +

+

We generate two synthetic portfolios with no AE under-reporting +sites. One portfolio with a fixed AE rate for all visits and another one +with a flexible visit-specific AE rate.

+
+df_portf_fix <- sim_test_data_portfolio(df_config, parallel = TRUE, progress = TRUE)
+
+df_portf_fix %>%
+  head(25) %>%
+  knitr::kable()
+ ++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
study_idae_per_visit_meansite_numbermax_visit_sdmax_visit_meanpatnumvisitn_ae
00010.297380647463.53553416.5000111
00010.297380647463.53553416.5000121
00010.297380647463.53553416.5000132
00010.297380647463.53553416.5000143
00010.297380647463.53553416.5000154
00010.297380647463.53553416.5000164
00010.297380647463.53553416.5000175
00010.297380647463.53553416.5000185
00010.297380647463.53553416.5000195
00010.297380647463.53553416.50001105
00010.297380647463.53553416.50001115
00010.297380647463.53553416.50001125
00010.297380647463.53553416.50001135
00010.297380647463.53553416.50001145
00010.297380647463.53553416.50001155
00010.297380647463.53553416.50001165
00010.297380647463.53553416.5000211
00010.297380647463.53553416.5000221
00010.297380647463.53553416.5000231
00010.297380647463.53553416.5000241
00010.297380647463.53553416.5000251
00010.297380647463.53553416.5000261
00010.297380647463.53553416.5000272
00010.297380647463.53553416.5000282
00010.297380647463.53553416.5000293
+
+df_portf_flex <- sim_test_data_portfolio(df_config, df_ae_rates = df_ae_rates, parallel = TRUE, progress = TRUE)
+
+
+

Compare AE rates +

+

Next we confirm the different AE rates in our two synthetic +portfolios.

+
+df_rate_fix <- df_portf_fix %>%
+  mutate(ae_rate = coalesce(n_ae - lag(n_ae), n_ae), .by = c("study_id", "patnum")) %>%
+  summarise(ae_rate = mean(ae_rate), .by = c("study_id", "visit")) %>%
+  mutate(rate = "fix")
+
+
+df_rate_flex <- df_portf_flex %>%
+  mutate(ae_rate = coalesce(n_ae - lag(n_ae), n_ae), .by = c("study_id", "patnum")) %>%
+  summarise(ae_rate = mean(ae_rate), .by = c("study_id", "visit")) %>%
+  mutate(rate = "flex")
+
+bind_rows(df_rate_flex, df_rate_fix) %>%
+  ggplot(aes(visit, ae_rate)) +
+    geom_line(aes(group = study_id), alpha = 0.2) +
+    geom_smooth() +
+    facet_wrap(~ rate) +
+    labs(title = "Average AE rates per Study")
+

+
+bind_rows(df_rate_flex, df_rate_fix) %>%
+  filter(dense_rank(study_id) <= 16) %>%
+  ggplot(aes(visit, ae_rate)) +
+    geom_line(aes(group = rate, color = rate)) +
+    facet_wrap(~ study_id, scales = "free") +
+    labs(title = "Average AE rates for Selected Studies")
+

+

We can confirm that the AE rates in the “flexible” portfolio are not +constant. Moreover we see that the AE rate profile is very unique for +each study.

+
+
+
+

Funnel Plots {gsm} +

+
+

Example +

+

Here we demonstrate how to use the {gsm} package on our simulated +portfolios so that we can get a good visualisation of the funnel +plot.

+
+get_SUBJ <- function(df_portf) {
+  df_portf %>%
+    select(study_id, siteid = site_number, subjid = patnum, timeonstudy = visit) %>%
+    summarise(timeonstudy = max(timeonstudy), .by = c(study_id, siteid, subjid)) %>%
+    group_by(study_id) %>%
+    nest()
+}
+
+
+get_AE <- function(df_portf) {
+  df_portf %>%
+    select(study_id, subjid = patnum, n_ae) %>%
+    summarise(n_ae = max(n_ae), .by = c(study_id, subjid)) %>%
+    filter(n_ae > 0) %>%
+    mutate(n_ae = map(n_ae, ~ tibble(n = seq(1, .)), .progress = TRUE)) %>%
+    unnest(n_ae) %>%
+    select(- n) %>%
+    group_by(study_id) %>%
+    nest()
+}
+
+dfSUBJ_fix <- get_SUBJ(df_portf_fix)
+dfAE_fix <- get_AE(df_portf_fix)
+
+dfInput <- gsm::AE_Map_Raw(list(dfSUBJ = dfSUBJ_fix$data[[1]], dfAE = dfAE_fix$data[[1]]))
+dfInput
+
## # A tibble: 113 × 5
+##    SubjectID SiteID Exposure Count  Rate
+##    <chr>     <chr>     <int> <int> <dbl>
+##  1 0001      4746         16     5 0.312
+##  2 0002      4746         14     3 0.214
+##  3 0003      4747         26     6 0.231
+##  4 0004      4747         26    11 0.423
+##  5 0005      4750         19     5 0.263
+##  6 0006      4750         19     5 0.263
+##  7 0007      4750         20     6 0.3  
+##  8 0008      4750         18     3 0.167
+##  9 0009      4815         25     3 0.12 
+## 10 0010      4815         28     8 0.286
+## # ℹ 103 more rows
+
+dfTransformed <- gsm::Transform_Rate(
+  dfInput,
+  strNumeratorCol = "Count",
+  strDenominatorCol = "Exposure"
+  )
+dfTransformed
+
## # A tibble: 44 × 4
+##    GroupID Numerator Denominator Metric
+##    <chr>       <int>       <int>  <dbl>
+##  1 4746            8          30  0.267
+##  2 4747           17          52  0.327
+##  3 4750           19          76  0.25 
+##  4 4815           40         153  0.261
+##  5 4816            9          27  0.333
+##  6 4817            5          31  0.161
+##  7 4818            9          30  0.3  
+##  8 4891           48         151  0.318
+##  9 4893           15          40  0.375
+## 10 4932           33         137  0.241
+## # ℹ 34 more rows
+
+dfAnalyzed <- gsm::Analyze_NormalApprox(dfTransformed)
+dfAnalyzed
+
## # A tibble: 44 × 7
+##    GroupID Numerator Denominator Metric OverallMetric Factor  Score
+##    <chr>       <int>       <int>  <dbl>         <dbl>  <dbl>  <dbl>
+##  1 4942            6          47  0.128         0.288   1.08 -2.34 
+##  2 5166           17          95  0.179         0.288   1.08 -2.27 
+##  3 4817            5          31  0.161         0.288   1.08 -1.51 
+##  4 5229           12          58  0.207         0.288   1.08 -1.32 
+##  5 4932           33         137  0.241         0.288   1.08 -1.18 
+##  6 4986           13          58  0.224         0.288   1.08 -1.04 
+##  7 4985           28         114  0.246         0.288   1.08 -0.972
+##  8 5084            4          21  0.190         0.288   1.08 -0.954
+##  9 5082            9          41  0.220         0.288   1.08 -0.938
+## 10 4969           14          60  0.233         0.288   1.08 -0.907
+## # ℹ 34 more rows
+
+dfFlagged <- gsm::Flag_NormalApprox(dfAnalyzed, vThreshold = c(-3, -2, 2, 3))
+dfFlagged
+
## # A tibble: 44 × 8
+##    GroupID Numerator Denominator Metric OverallMetric Factor  Score  Flag
+##    <chr>       <int>       <int>  <dbl>         <dbl>  <dbl>  <dbl> <dbl>
+##  1 5194           66         182  0.363         0.288   1.08  2.13      1
+##  2 4942            6          47  0.128         0.288   1.08 -2.34     -1
+##  3 5166           17          95  0.179         0.288   1.08 -2.27     -1
+##  4 4817            5          31  0.161         0.288   1.08 -1.51      0
+##  5 5229           12          58  0.207         0.288   1.08 -1.32      0
+##  6 4932           33         137  0.241         0.288   1.08 -1.18      0
+##  7 4986           13          58  0.224         0.288   1.08 -1.04      0
+##  8 4985           28         114  0.246         0.288   1.08 -0.972     0
+##  9 5084            4          21  0.190         0.288   1.08 -0.954     0
+## 10 5082            9          41  0.220         0.288   1.08 -0.938     0
+## # ℹ 34 more rows
+
+dfSummary <- gsm::Summarize(dfFlagged)
+dfSummary
+
## # A tibble: 44 × 6
+##    GroupID Numerator Denominator Metric Score  Flag
+##    <chr>       <int>       <int>  <dbl> <dbl> <dbl>
+##  1 5194           66         182  0.363  2.13     1
+##  2 5166           17          95  0.179 -2.27    -1
+##  3 4942            6          47  0.128 -2.34    -1
+##  4 4968           10          24  0.417  1.34     0
+##  5 4988           16          39  0.410  1.62     0
+##  6 5168           11          27  0.407  1.31     0
+##  7 5311            8          20  0.4    1.06     0
+##  8 5273           16          40  0.4    1.50     0
+##  9 4893           15          40  0.375  1.16     0
+## 10 5083           16          44  0.364  1.06     0
+## # ℹ 34 more rows
+
+dfBounds <- gsm::Analyze_NormalApprox_PredictBounds(dfTransformed, vThreshold = c(-3, -2, 2, 3))
+dfBounds
+
## # A tibble: 1,254 × 5
+##    Threshold Denominator LogDenominator Numerator  Metric
+##        <dbl>       <dbl>          <dbl>     <dbl>   <dbl>
+##  1        -3        24.6           3.20    0.0927 0.00377
+##  2        -3        25.2           3.23    0.189  0.00750
+##  3        -3        25.9           3.25    0.287  0.0111 
+##  4        -3        26.5           3.28    0.386  0.0145 
+##  5        -3        27.2           3.30    0.485  0.0179 
+##  6        -3        27.8           3.33    0.586  0.0211 
+##  7        -3        28.5           3.35    0.688  0.0242 
+##  8        -3        29.2           3.37    0.792  0.0272 
+##  9        -3        29.8           3.39    0.895  0.0300 
+## 10        -3        30.5           3.42    1.00   0.0328 
+## # ℹ 1,244 more rows
+
+chart <- gsm::Visualize_Scatter(dfFlagged, dfBounds)
+chart
+

+
+
+

Simulate UR +

+
+
+
+

UR Funnel +

+

We write out own funnel function as adapted from {gsm}

+
+funnel_ur <- function(df, site, ur_rate) {
+  df %>%
+    filter(visit == max(visit), .by = patnum) %>%
+    summarise(
+      Metric = sum(.data$n_ae) / sum(.data$visit),
+      n_ae = sum(n_ae),
+      visit = sum(visit),
+      .by = "site_number"
+    ) %>%
+    mutate(
+      n_ae = ifelse(site_number == site, n_ae * (1 - ur_rate), n_ae),
+      Metric = n_ae / visit
+    ) %>%
+    mutate(
+          vMu = sum(.data$n_ae) / sum(.data$visit),
+          z_0 = ifelse(.data$vMu == 0,
+            0,
+            (.data$Metric - .data$vMu) /
+              sqrt(.data$vMu / .data$visit)
+          ),
+          phi = mean(.data$z_0^2),
+          z_i = ifelse(.data$vMu == 0 | .data$phi == 0,
+            0,
+            (.data$Metric - .data$vMu) /
+              sqrt(.data$phi * .data$vMu / .data$visit)
+          )
+    ) %>%
+    filter(site_number == site) %>%
+    pull(z_i)
+}
+
+sim_ur_funnel <- function(df) {
+  df %>%
+    group_by(study_id) %>%
+    nest() %>%
+    ungroup() %>%
+    mutate(
+      sites = map(data, ~ distinct(., site_number))
+    ) %>%
+    unnest(sites) %>%
+    mutate(ur = list(tibble(ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1)))) %>%
+    unnest(ur) %>%
+    mutate(
+      score = pmap_dbl(list(data, site_number, ur_rate), funnel_ur, .progress = TRUE)
+    )
+}
+
+df_sim_ur_funnel_flex <- sim_ur_funnel(df_portf_flex)
+
+df_sim_ur_funnel_fix <- sim_ur_funnel(df_portf_fix)
+
+
+

UR {simaerep} +

+

We simulate under-reporting for both portfolios using {simaerep} +using sim_ur_scenarios().

+
+df_sim_simaerep_fix <- sim_ur_scenarios(
+   df_portf_fix,
+   extra_ur_sites = 0,
+   ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1),
+   parallel = TRUE,
+   poisson = TRUE,
+   prob_lower = TRUE,
+   progress = TRUE
+)
+
+df_sim_simaerep_flex <- sim_ur_scenarios(
+   df_portf_flex,
+   extra_ur_sites = 0,
+   ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1),
+   parallel = TRUE,
+   poisson = TRUE,
+   prob_lower = TRUE,
+   progress = TRUE
+)
+
+
+

Evaluate +

+
+

Combine Results +

+

As the funnel plot score does not use multiplicity correction, we +also compare the funnel plot score against the {simaerep} score w/o +multiplicity correction.

+
+df_sim_simaerep_fix$ae_rate <- "AE rate: fix"
+df_sim_simaerep_flex$ae_rate <- "AE rate: flexible"
+df_sim_ur_funnel_fix$ae_rate <- "AE rate: fix"
+df_sim_ur_funnel_flex$ae_rate <- "AE rate: flexible"
+
+
+
+df_sim_fun_thresh2 <- bind_rows(df_sim_ur_funnel_fix, df_sim_ur_funnel_flex) %>%
+  mutate(
+    type = "funnel",
+  ) %>%
+  select(type, ae_rate, study_id, site_number, ur_rate, score)
+
+
+df_sim_simaerep_threshp95 <-  bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>%
+  mutate(
+    type = "{simaerep}"
+  ) %>%
+  select(type, ae_rate, study_id, site_number, ur_rate, score = prob_low_prob_ur)
+
+df_sim_simaerep_threshp95_no_mult <-  bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>%
+  mutate(
+    type = "{simaerep} no mult"
+  ) %>%
+  mutate(
+    score = 1 - prob_low
+  ) %>%
+  select(type, ae_rate, study_id, site_number, ur_rate, score)
+
+
+df_eval <- bind_rows(
+  df_sim_simaerep_threshp95,
+  df_sim_simaerep_threshp95_no_mult,
+  df_sim_fun_thresh2
+)
+
+
+

AUC +

+

We continue by comparing the ROC-AUC.

+
+get_roc <- function(df_ur, df_nr) {
+  df <- bind_rows(df_ur, df_nr) 
+  pROC::roc(df, response = "is_ur", predictor = "score", quiet = TRUE)
+}
+
+# use 0 scenario to mix with ur scenario and calculate auc from scores
+df_nr <- df_eval %>%
+  filter(ur_rate == 0) %>%
+  mutate(is_ur = "no") %>%
+  select(- ur_rate) %>%
+  group_by(type, study_id, ae_rate) %>%
+  nest() %>%
+  ungroup() %>%
+  rename(data_nr = data)
+  
+
+df_ur <- df_eval %>%
+  filter(ur_rate > 0) %>%
+  mutate(is_ur = "yes") %>%
+  group_by(type, study_id, ur_rate, ae_rate) %>%
+  nest() %>%
+  ungroup() %>%
+  rename(data_ur = data)
+
+
+df_auc <- df_ur %>%
+  left_join(
+    df_nr,
+    by = c("type", "study_id", "ae_rate")
+  ) %>%
+  mutate(
+    roc = map2(data_ur, data_nr, get_roc, .progress = TRUE),
+    auc = map_dbl(roc, pROC::auc, .progress = TRUE)
+  ) %>%
+  select(type, study_id, ur_rate, ae_rate, roc, auc)
+
+

Table +

+
+df_auc %>%
+  summarise(
+    sd_auc = sd(.data$auc),
+    auc = mean(.data$auc),
+    .by = c(type, ur_rate, ae_rate)
+  ) %>%
+  knitr::kable(digit = 3)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
typeur_rateae_ratesd_aucauc
{simaerep}0.10AE rate: fix0.1080.630
{simaerep}0.25AE rate: fix0.1630.754
{simaerep}0.50AE rate: fix0.1610.863
{simaerep}0.75AE rate: fix0.1300.920
{simaerep}1.00AE rate: fix0.1110.938
{simaerep}0.10AE rate: flexible0.1140.627
{simaerep}0.25AE rate: flexible0.1670.752
{simaerep}0.50AE rate: flexible0.1610.860
{simaerep}0.75AE rate: flexible0.1270.923
{simaerep}1.00AE rate: flexible0.1080.943
{simaerep} no mult0.10AE rate: fix0.0620.662
{simaerep} no mult0.25AE rate: fix0.0900.814
{simaerep} no mult0.50AE rate: fix0.0710.930
{simaerep} no mult0.75AE rate: fix0.0430.973
{simaerep} no mult1.00AE rate: fix0.0400.980
{simaerep} no mult0.10AE rate: flexible0.0680.666
{simaerep} no mult0.25AE rate: flexible0.0960.819
{simaerep} no mult0.50AE rate: flexible0.0740.932
{simaerep} no mult0.75AE rate: flexible0.0460.972
{simaerep} no mult1.00AE rate: flexible0.0420.978
funnel0.10AE rate: fix0.0820.672
funnel0.25AE rate: fix0.0970.827
funnel0.50AE rate: fix0.0690.937
funnel0.75AE rate: fix0.0420.975
funnel1.00AE rate: fix0.0280.988
funnel0.10AE rate: flexible0.0580.631
funnel0.25AE rate: flexible0.0840.775
funnel0.50AE rate: flexible0.0720.908
funnel0.75AE rate: flexible0.0440.962
funnel1.00AE rate: flexible0.0290.983
+
+
+

Plot +

+
+df_auc %>%
+  ggplot(aes(type, auc)) +
+    geom_boxplot(aes(fill = type)) +
+    facet_grid(ae_rate ~ ur_rate)  +
+    scale_fill_brewer(palette = "Dark2") +
+    theme(axis.text.x = element_blank())
+

+
+
+
+

Metrics +

+
+

Thresholds +

+

We set the thresholds for funnel and simaerep w/o multiplicity +correction so that we get the same fpr as for simaerep with multiplicity +correction and the established threshold of 0.95

+
+thresh_default <- 0.95
+
+target_fpr <- df_eval %>%
+  filter(ur_rate == 0, type == "{simaerep}") %>%
+  summarise(fpr = sum(score >= thresh_default) / n()) %>%
+  pull(fpr)
+
+thresh_no_mult <-  df_eval %>%
+  filter(ur_rate == 0, type == "{simaerep} no mult") %>%
+  pull(score) %>%
+  quantile(1 - target_fpr)
+
+thresh_funnel <- df_eval %>%
+  filter(ur_rate == 0, type == "funnel") %>%
+  pull(score) %>%
+  quantile(target_fpr)
+
+target_fpr
+
## [1] 0.0005309081
+
+thresh_no_mult
+
## 99.94691% 
+##     0.999
+
+thresh_funnel
+
## 0.05309081% 
+##   -2.979981
+
+
+

Aggregate +

+
+get_prop_test_ci95 <- function(..., ix) {
+  
+  stopifnot(ix %in% c(1, 2))
+  
+  tryCatch(
+    prop.test(...)$conf.int[ix],
+    error = function(cnd) c(NA, NA)[ix]
+  )
+}
+
+aggr_results <- function(df_eval) {
+
+  df_perf <- df_eval %>%
+    mutate(
+      is_ur = case_when(
+        type == "{simaerep}" ~ score >= thresh_default,
+        type == "{simaerep} no mult" ~ score >= thresh_no_mult,
+        type == "funnel" ~ score <= thresh_funnel
+      )
+    ) %>%
+    summarise(
+      n = n(),
+      .by = c(type, ae_rate, ur_rate, is_ur)
+    ) %>%
+    pivot_wider(
+      names_from = is_ur,
+      values_from = n,
+      names_prefix = "is_ur_",
+      values_fill = 0
+    ) %>%
+    mutate(
+      n_sites = is_ur_TRUE + is_ur_FALSE,
+      ratio = is_ur_TRUE / n_sites,
+      ratio_type = ifelse(ur_rate == 0, "fpr", "tpr"),
+      ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)),
+      ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2))
+    )
+  }
+
+df_perf <- aggr_results(df_eval)
+
+
+

Table +

+
+df_perf %>%
+  knitr::kable(digits = 4)
+ ++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
typeae_rateur_rateis_ur_FALSEis_ur_TRUEn_sitesratioratio_typeci95_lowci95_high
{simaerep}AE rate: fix0.002165110216610.0005fpr0.00020.0009
{simaerep}AE rate: fix0.102158477216610.0036tpr0.00280.0045
{simaerep}AE rate: fix0.25205811080216610.0499tpr0.04700.0529
{simaerep}AE rate: fix0.50159565705216610.2634tpr0.25750.2693
{simaerep}AE rate: fix0.751101810643216610.4913tpr0.48470.4980
{simaerep}AE rate: fix1.00849213169216610.6080tpr0.60140.6145
{simaerep}AE rate: flexible0.002164813216610.0006fpr0.00030.0011
{simaerep}AE rate: flexible0.1021535126216610.0058tpr0.00490.0069
{simaerep}AE rate: flexible0.25203481313216610.0606tpr0.05750.0639
{simaerep}AE rate: flexible0.50154356226216610.2874tpr0.28140.2935
{simaerep}AE rate: flexible0.751051111150216610.5148tpr0.50810.5214
{simaerep}AE rate: flexible1.00808513576216610.6267tpr0.62030.6332
{simaerep} no multAE rate: fix0.002164120216610.0009fpr0.00060.0015
{simaerep} no multAE rate: fix0.1021531130216610.0060tpr0.00500.0071
{simaerep} no multAE rate: fix0.25202551406216610.0649tpr0.06170.0683
{simaerep} no multAE rate: fix0.50151306531216610.3015tpr0.29540.3077
{simaerep} no multAE rate: fix0.75992311738216610.5419tpr0.53520.5485
{simaerep} no multAE rate: fix1.00747114190216610.6551tpr0.64870.6614
{simaerep} no multAE rate: flexible0.002163526216610.0012fpr0.00080.0018
{simaerep} no multAE rate: flexible0.1021479182216610.0084tpr0.00720.0097
{simaerep} no multAE rate: flexible0.25200151646216610.0760tpr0.07250.0796
{simaerep} no multAE rate: flexible0.50144907171216610.3311tpr0.32480.3374
{simaerep} no multAE rate: flexible0.75948812173216610.5620tpr0.55530.5686
{simaerep} no multAE rate: flexible1.00712314538216610.6712tpr0.66490.6774
funnelAE rate: fix0.00216547216610.0003fpr0.00010.0007
funnelAE rate: fix0.1021561100216610.0046tpr0.00380.0056
funnelAE rate: fix0.25199081753216610.0809tpr0.07730.0847
funnelAE rate: fix0.50144327229216610.3337tpr0.32750.3401
funnelAE rate: fix0.75974211919216610.5503tpr0.54360.5569
funnelAE rate: fix1.00659815063216610.6954tpr0.68920.7015
funnelAE rate: flexible0.002164516216610.0007fpr0.00040.0012
funnelAE rate: flexible0.102159269216610.0032tpr0.00250.0041
funnelAE rate: flexible0.2521076585216610.0270tpr0.02490.0293
funnelAE rate: flexible0.50179083753216610.1733tpr0.16830.1784
funnelAE rate: flexible0.75135068155216610.3765tpr0.37000.3830
funnelAE rate: flexible1.00966511996216610.5538tpr0.54720.5604
+
+
+

Plot +

+
+plot_perf <- function(df_perf) {
+
+  df_perf %>%
+    mutate(ur_rate = paste0("under-reporting rate: ",  ur_rate, " - ", ratio_type),
+           ur_rate = ifelse(str_detect(ur_rate, "fpr"), "fpr", ur_rate),
+           ae_rate = forcats::fct_rev(factor(ae_rate))) %>%
+    group_by(ur_rate) %>%
+    ggplot(aes(type, ratio)) +
+      geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type, linetype = ae_rate), linewidth = 1) +
+      facet_wrap(~ ur_rate, ncol = 1) +
+      coord_flip() +
+      theme(legend.position = "bottom") +
+      labs(
+        x = "",
+        y = "CI95 Performance Ratio", 
+        title = "{simaerep} vs Funnel-Plot Performance"
+      ) +
+      scale_color_brewer(palette = "Dark2")
+}
+
+plot_perf(df_perf)
+

+
+
+
+
+

Summary +

+
    +
  • Funnel plot expects constant event rates over time. Performance +decrease when event rates are flexible.
  • +
  • {simaerep} performance is uneffected by flexible event rates.
  • +
  • {simaerep} detection rates with comparable false positive rates are +greater than funnel plot detection rates when event rates are +flexible.
  • +
+
+plan(sequential)
+
+
+
+ + + +
+ + + +
+
+ + + + + + + diff --git a/docs/articles/funnel_perf_files/figure-html/plot-1.png b/docs/articles/funnel_perf_files/figure-html/plot-1.png new file mode 100644 index 0000000..da4a7f8 Binary files /dev/null and b/docs/articles/funnel_perf_files/figure-html/plot-1.png differ diff --git a/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-14-1.png b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-14-1.png new file mode 100644 index 0000000..78cd59c Binary files /dev/null and b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-14-1.png differ diff --git a/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-3-1.png b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-3-1.png new file mode 100644 index 0000000..c026730 Binary files /dev/null and b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-3-1.png differ diff --git a/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-4-1.png new file mode 100644 index 0000000..46d4e85 Binary files /dev/null and b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 0000000..6b01dc3 Binary files /dev/null and b/docs/articles/funnel_perf_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/index.html b/docs/articles/index.html index a1aa34f..37b54ec 100644 --- a/docs/articles/index.html +++ b/docs/articles/index.html @@ -1,109 +1,72 @@ -Articles • simaerep - - -
-
-
+
+ - - diff --git a/docs/articles/inframe.html b/docs/articles/inframe.html new file mode 100644 index 0000000..2401278 --- /dev/null +++ b/docs/articles/inframe.html @@ -0,0 +1,368 @@ + + + + + + + +Inframe Simulation Using Table Operations • simaerep + + + + + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + + +
+

Introduction +

+

With the latest 0.6.0 release we have added an +alternative version of the {simaerep} algorithm that was coded using +solely dbplyr compatible table operations.

+
    +
  • expand the patients for each site r times
  • +
  • join each patient with a random eligible patient from same +study
  • +
  • for each replicate calculate event per visit rate per site
  • +
  • calculate the ratio of having a lower event per visit rate than +actually observed
  • +
+

This comes with the following advantages and disadvantages:

+
    +
  • Patients are individually matched with patients that have reached +the same visit in the study. No need to pick visit_med75 as an +evaluation point.
  • +
  • +dbplyr compatibility means that code execution can be +done in a database back-end as opposed to in-memory.
  • +
  • Matching patients individually is more costly, this increases +in-memory computation time
  • +
  • Limited patient sample pool for patients that have more visits than +other patients in study.
  • +
+
+
+

Sample Data +

+
+set.seed(1)
+
+df_visit <- sim_test_data_study(
+  n_pat = 1000, # number of patients in study
+  n_sites = 100, # number of sites in study
+  frac_site_with_ur = 0.05, # fraction of sites under-reporting
+  ur_rate = 0.4, # rate of under-reporting
+  ae_per_visit_mean = 0.5 # mean AE per patient visit
+)
+
+df_visit$study_id <- "A"
+
+
+

Patient-Level Matching +

+

Here we use the standard version of the algorithm.

+
+aerep_trad <- simaerep(df_visit)
+plot(aerep_trad)
+

+

To use the patient-level matching algorithm we set +inframe=TRUE and visit_med75=FALSE.

+

The original algorithm uses fixed seeds before sampling while the +inframe method does not. In order to obtain consistent results we need +to manually set a seed.

+
+set.seed(1)
+
+aerep_inframe <- simaerep(
+  df_visit,
+  inframe = TRUE,
+  visit_med75 = FALSE
+)
+

The plot shows that for all sites 10/10 patients were used and none +were excluded. We also observe that the site average has become more +noisy as less patients are used to calculate the averages for the higher +visit numbers.

+
+plot(aerep_inframe)
+

+

The inframe method includes this noisier data but does not compare +average event counts but event per visit rates. We can find +events_per_visit_site and +events_per_visit_study in df_eval. The latter +is the average event rate obtained in the simulation in which each +patient has been resampled according to its maximum visit.

+
+aerep_inframe$df_eval
+
## # A tibble: 100 × 10
+##    study_id site_number events_per_visit_site events visits n_pat prob_low
+##    <chr>    <chr>                       <dbl>  <dbl>  <dbl> <int>    <dbl>
+##  1 A        S0001                       0.279     50    179    10    0    
+##  2 A        S0002                       0.281     59    210    10    0    
+##  3 A        S0003                       0.291     55    189    10    0    
+##  4 A        S0004                       0.312     59    189    10    0    
+##  5 A        S0005                       0.297     58    195    10    0    
+##  6 A        S0006                       0.493    100    203    10    0.569
+##  7 A        S0007                       0.408     89    218    10    0.044
+##  8 A        S0008                       0.559     95    170    10    0.922
+##  9 A        S0009                       0.518    101    195    10    0.761
+## 10 A        S0010                       0.470     87    185    10    0.385
+## # ℹ 90 more rows
+## # ℹ 3 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
+## #   prob_low_prob_ur <dbl>
+

We can also force the inframe method to use the visit_med75 this will +prefilter df_visit, which adds an extra step and decreases +performance.

+
+set.seed(1)
+
+aerep_inframe_visit_med75 <- simaerep(
+  df_visit,
+  inframe = TRUE,
+  visit_med75 = TRUE
+)
+
+plot(aerep_inframe_visit_med75)
+

+
+
+

DB +

+

We can demonstrate the database-backend compatibility by using a +connection to a in memory duckdb database. In order to set +the number of replications we need to create a new table in our back-end +that has one column with as many rows as the desired replications.

+

A lazy reference to this table can then be passed to the +r parameter.

+
+con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:")
+df_r <- tibble(rep = seq(1, 1000))
+
+dplyr::copy_to(con, df_visit, "visit")
+dplyr::copy_to(con, df_r, "r")
+
+tbl_visit <- tbl(con, "visit")
+tbl_r <- tbl(con, "r")
+
+
+aerep <- simaerep(tbl_visit, r = tbl_r, visit_med75 = FALSE)
+

When inspecting df_eval we see that it is still a lazy +table object.

+
+aerep$df_eval
+
## # Source:     SQL [?? x 10]
+## # Database:   DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:]
+## # Ordered by: study_id, site_number
+##    study_id site_number events_per_visit_site events visits n_pat prob_low
+##    <chr>    <chr>                       <dbl>  <dbl>  <dbl> <dbl>    <dbl>
+##  1 A        S0040                       0.477     95    199    10    0.43 
+##  2 A        S0082                       0.470     87    185    10    0.382
+##  3 A        S0084                       0.513    101    197    10    0.698
+##  4 A        S0030                       0.538    106    197    10    0.828
+##  5 A        S0037                       0.429     85    198    10    0.143
+##  6 A        S0034                       0.432     79    183    10    0.148
+##  7 A        S0095                       0.452     80    177    10    0.255
+##  8 A        S0089                       0.481     91    189    10    0.467
+##  9 A        S0031                       0.49      98    200    10    0.527
+## 10 A        S0069                       0.503     88    175    10    0.644
+## # ℹ more rows
+## # ℹ 3 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
+## #   prob_low_prob_ur <dbl>
+

We can convert it to sql code. The cte option makes the +sql code more readable.

+
+sql_eval <- dbplyr::sql_render(aerep$df_eval, sql_options = dbplyr::sql_options(cte = TRUE))
+stringr::str_trunc(sql_eval, 500)
+
## <SQL> WITH q01 AS (
+##   SELECT
+##     patnum,
+##     site_number,
+##     is_ur,
+##     max_visit_mean,
+##     max_visit_sd,
+##     ae_per_visit_mean,
+##     CAST(visit AS NUMERIC) AS visit,
+##     CAST(n_ae AS NUMERIC) AS n_ae,
+##     study_id
+##   FROM visit
+## ),
+## q02 AS (
+##   SELECT DISTINCT study_id, site_number
+##   FROM q01
+##   GROUP BY study_id, site_number, patnum
+## ),
+## q03 AS (
+##   SELECT study_id, site_number, patnum, MAX(visit) AS max_visit_per_pat
+##   FROM q01
+##   GROUP BY study_id, site_number, patnum
+## ),
+## q04 AS (
+##   SELECT DISTINCT study...
+

We can take that code and wrap it in a CREATE TABLE +statement

+
+sql_create <- glue::glue("CREATE TABLE eval AS ({sql_eval})")
+DBI::dbExecute(con, sql_create)
+
## [1] 100
+

Retrieve the new table from the database.

+
+tbl_eval <- tbl(con, "eval")
+tbl_eval
+
## # Source:   table<eval> [?? x 10]
+## # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:]
+##    study_id site_number events_per_visit_site events visits n_pat prob_low
+##    <chr>    <chr>                       <dbl>  <dbl>  <dbl> <dbl>    <dbl>
+##  1 A        S0039                       0.472     92    195    10    0.389
+##  2 A        S0010                       0.470     87    185    10    0.385
+##  3 A        S0024                       0.495     91    184    10    0.579
+##  4 A        S0014                       0.505    103    204    10    0.629
+##  5 A        S0043                       0.503     91    181    10    0.629
+##  6 A        S0036                       0.506     89    176    10    0.666
+##  7 A        S0067                       0.428     83    194    10    0.127
+##  8 A        S0019                       0.448     74    165    10    0.256
+##  9 A        S0042                       0.470     85    181    10    0.379
+## 10 A        S0094                       0.469     99    211    10    0.379
+## # ℹ more rows
+## # ℹ 3 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
+## #   prob_low_prob_ur <dbl>
+

We plot the results from the {simaerep} object.

+
+plot(aerep)
+

+

Or more efficiently by using plot_study() when we have +already written the simaerep results into the database. +Here we avoid that the results are being recalculated just for the sake +of creating a plot. However this requires that we save +df_site to the database as well.

+
+sql_site <- dbplyr::sql_render(aerep$df_site)
+DBI::dbExecute(con, glue::glue("CREATE TABLE site AS ({sql_site})"))
+
## [1] 100
+
+tbl_site <- tbl(con, "site")
+
+plot_study(tbl_visit, tbl_site, tbl_eval, study = "A")
+

+
+DBI::dbDisconnect(con)
+
+
+

In Memory Calculation Times +

+

Here we perform some examplary tests to illustrate the increase +in-memory calculation time of the inframe calculation method.

+

This is the calculation time for the default settings

+
+system.time({simaerep(df_visit, inframe = FALSE, visit_med75 = TRUE, under_only = TRUE, progress = FALSE)})
+
##    user  system elapsed 
+##   0.793   0.010   0.802
+

inframe calculation time is higher

+
+system.time({simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = TRUE)})
+
##    user  system elapsed 
+##   1.968   0.044   2.013
+
+
+
+ + + +
+ + + +
+
+ + + + + + + diff --git a/docs/articles/inframe_files/figure-html/unnamed-chunk-12-1.png b/docs/articles/inframe_files/figure-html/unnamed-chunk-12-1.png new file mode 100644 index 0000000..cb59d08 Binary files /dev/null and b/docs/articles/inframe_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/articles/inframe_files/figure-html/unnamed-chunk-13-1.png b/docs/articles/inframe_files/figure-html/unnamed-chunk-13-1.png new file mode 100644 index 0000000..41f3a3c Binary files /dev/null and b/docs/articles/inframe_files/figure-html/unnamed-chunk-13-1.png differ diff --git a/docs/articles/inframe_files/figure-html/unnamed-chunk-2-1.png b/docs/articles/inframe_files/figure-html/unnamed-chunk-2-1.png new file mode 100644 index 0000000..c651eca Binary files /dev/null and b/docs/articles/inframe_files/figure-html/unnamed-chunk-2-1.png differ diff --git a/docs/articles/inframe_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/inframe_files/figure-html/unnamed-chunk-4-1.png new file mode 100644 index 0000000..73854d6 Binary files /dev/null and b/docs/articles/inframe_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/articles/inframe_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/inframe_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 0000000..b9c84a4 Binary files /dev/null and b/docs/articles/inframe_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/intro.html b/docs/articles/intro.html index 3b18283..89b2bba 100644 --- a/docs/articles/intro.html +++ b/docs/articles/intro.html @@ -4,7 +4,7 @@ - + Introduction • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
-
@@ -907,11 +859,11 @@

Applying {simaerep}aerep <- simaerep(df_visit) aerep

## simaerep object:
-## Check aerep$df_eval prob_low_prob_ur column for AE under-reporting probabilites.
+## Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty.
 ## Plot results using plot() generic.
 str(aerep)
-
## List of 7
+
## List of 11
 ##  $ visit           :List of 3
 ##   ..$ dim       : int [1:2] 2336 9
 ##   ..$ df_summary: tibble [1 × 5] (S3: tbl_df/tbl/data.frame)
@@ -926,7 +878,7 @@ 

Applying {simaerep}## ..$ study_id : chr [1:6] "A" "A" "A" "A" ... ## ..$ site_number : chr [1:6] "S0001" "S0002" "S0003" "S0004" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 -## ..$ n_pat_with_med75 : int [1:6] 17 17 16 17 18 20 +## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, "names")= chr [1:6] "80%" "80%" "80%" "80%" ... ## ..$ mean_ae_site_med75: num [1:6] 3 3.35 8.5 8.06 7.56 ... @@ -934,7 +886,7 @@

Applying {simaerep}## ..$ study_id : chr [1:6] "A" "A" "A" "A" ... ## ..$ site_number : chr [1:6] "S0001" "S0002" "S0003" "S0004" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 -## ..$ n_pat_with_med75 : int [1:6] 17 17 16 17 18 20 +## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, "names")= chr [1:6] "80%" "80%" "80%" "80%" ... ## ..$ mean_ae_site_med75 : num [1:6] 3 3.35 8.5 8.06 7.56 ... @@ -945,7 +897,7 @@

Applying {simaerep}## ..$ study_id : chr [1:6] "A" "A" "A" "A" ... ## ..$ site_number : chr [1:6] "S0001" "S0002" "S0003" "S0004" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 -## ..$ n_pat_with_med75 : int [1:6] 17 17 16 17 18 20 +## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, "names")= chr [1:6] "80%" "80%" "80%" "80%" ... ## ..$ mean_ae_site_med75 : num [1:6] 3 3.35 8.5 8.06 7.56 ... @@ -954,6 +906,10 @@

Applying {simaerep}## ..$ prob_low : num [1:6] 0 0 1 1 1 1 ## ..$ prob_low_adj : num [1:6] 0 0 1 1 1 1 ## ..$ prob_low_prob_ur : num [1:6] 1 1 0 0 0 0 +## $ r : num 1000 +## $ visit_med75 : logi TRUE +## $ inframe : logi FALSE +## $ under_only : logi TRUE ## $ param_site_aggr :List of 2 ## ..$ method : chr "med75_adj" ## ..$ min_pat_pool: num 0.2 @@ -973,35 +929,26 @@

Applying {simaerep}simaerep() documentation.

-
- - - +
-
- - + diff --git a/docs/articles/intro_files/figure-html/unnamed-chunk-10-1.png b/docs/articles/intro_files/figure-html/unnamed-chunk-10-1.png index d075c33..ccd793c 100644 Binary files a/docs/articles/intro_files/figure-html/unnamed-chunk-10-1.png and b/docs/articles/intro_files/figure-html/unnamed-chunk-10-1.png differ diff --git a/docs/articles/intro_files/figure-html/unnamed-chunk-5-1.png b/docs/articles/intro_files/figure-html/unnamed-chunk-5-1.png index db586a4..37ac59b 100644 Binary files a/docs/articles/intro_files/figure-html/unnamed-chunk-5-1.png and b/docs/articles/intro_files/figure-html/unnamed-chunk-5-1.png differ diff --git a/docs/articles/intro_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/intro_files/figure-html/unnamed-chunk-6-1.png index ac82ad0..8c37432 100644 Binary files a/docs/articles/intro_files/figure-html/unnamed-chunk-6-1.png and b/docs/articles/intro_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/intro_files/figure-html/unnamed-chunk-9-1.png b/docs/articles/intro_files/figure-html/unnamed-chunk-9-1.png index fe82a63..c8fec31 100644 Binary files a/docs/articles/intro_files/figure-html/unnamed-chunk-9-1.png and b/docs/articles/intro_files/figure-html/unnamed-chunk-9-1.png differ diff --git a/docs/articles/over.html b/docs/articles/over.html index b1500b9..702f561 100644 --- a/docs/articles/over.html +++ b/docs/articles/over.html @@ -4,7 +4,7 @@ - + Over-Reporting Probability • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
-

@@ -269,11 +221,11 @@

Over-Reporting ) %>% mutate(type = "default under-reporting") ) %>% - ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + - geom_point(alpha = 0.25) + - theme(legend.position = "bottom") + - scale_color_manual(values = c("gold", "purple", "blue")) + - labs(y = "Probability of getting a lower or equal mean site AE in a 1000x simulation") + ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + + geom_point(alpha = 0.25) + + theme(legend.position = "bottom") + + scale_color_manual(values = c("gold", "purple", "blue")) + + labs(y = "Probability of getting a lower or equal mean site AE in a 1000x simulation") ggExtra::ggMarginal(p, groupColour = TRUE, type = "density")

@@ -307,11 +259,11 @@

Multiplicity Correction ) %>% mutate(type = "default under-reporting") ) %>% - ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + - geom_point(alpha = 0.25) + - theme(legend.position = "bottom") + - scale_color_manual(values = c("gold", "purple", "blue")) + - labs(y = "Probability of getting a lower or equal mean site AE in a 1000x simulation") + ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + + geom_point(alpha = 0.25) + + theme(legend.position = "bottom") + + scale_color_manual(values = c("gold", "purple", "blue")) + + labs(y = "Probability of getting a lower or equal mean site AE in a 1000x simulation") ggExtra::ggMarginal(p, groupColour = TRUE, type = "density")

@@ -366,25 +318,25 @@

Simulating Over-Reporting## site_number mean_ae_site_med75 mean_ae_study_med75 prob_low_prob_ur ## <chr> <dbl> <dbl> <dbl> ## 1 S0001 11.7 7.48 0 -## 2 S0006 8.67 8.22 0.118 -## 3 S0013 7.6 7.19 0.118 -## 4 S0005 7.98 7.66 0.118 -## 5 S0015 7.98 7.66 0.118 -## 6 S0018 7.91 7.67 0.118 -## 7 S0012 7.9 7.67 0.118 -## 8 S0009 7.78 7.67 0.118 -## 9 S0010 7.59 7.68 0.248 -## 10 S0007 7.58 7.68 0.276 -## 11 S0019 7.46 7.69 0.364 -## 12 S0004 7.40 7.69 0.364 -## 13 S0017 7.36 7.69 0.445 -## 14 S0002 7.72 8.27 0.58 -## 15 S0011 7.17 7.71 0.587 -## 16 S0014 7.05 7.71 0.644 -## 17 S0020 7.09 7.71 0.644 -## 18 S0003 6.93 7.72 0.687 -## 19 S0016 6.96 7.72 0.687 -## 20 S0008 6.85 7.72 0.687 +## 2 S0002 7.72 8.27 0.58 +## 3 S0003 6.93 7.72 0.687 +## 4 S0004 7.40 7.69 0.364 +## 5 S0005 7.98 7.66 0.118 +## 6 S0006 8.67 8.22 0.118 +## 7 S0007 7.58 7.68 0.276 +## 8 S0008 6.85 7.72 0.687 +## 9 S0009 7.78 7.67 0.118 +## 10 S0010 7.59 7.68 0.248 +## 11 S0011 7.17 7.71 0.587 +## 12 S0012 7.9 7.67 0.118 +## 13 S0013 7.6 7.19 0.118 +## 14 S0014 7.05 7.71 0.644 +## 15 S0015 7.98 7.66 0.118 +## 16 S0016 6.96 7.72 0.687 +## 17 S0017 7.36 7.69 0.445 +## 18 S0018 7.91 7.67 0.118 +## 19 S0019 7.46 7.69 0.364 +## 20 S0020 7.09 7.71 0.644 ## # ℹ 1 more variable: prob_high_prob_or <dbl>

We can plot over-reporting by changing setting prob_col = "prob_high_prob_or".

@@ -397,35 +349,26 @@

Simulating Over-Reporting## study = NULL, defaulting to study:A

- - - - + - - - + diff --git a/docs/articles/over_files/figure-html/unnamed-chunk-12-1.png b/docs/articles/over_files/figure-html/unnamed-chunk-12-1.png index ed08a9e..7fe8cb1 100644 Binary files a/docs/articles/over_files/figure-html/unnamed-chunk-12-1.png and b/docs/articles/over_files/figure-html/unnamed-chunk-12-1.png differ diff --git a/docs/articles/over_files/figure-html/unnamed-chunk-12-2.png b/docs/articles/over_files/figure-html/unnamed-chunk-12-2.png index cd386fe..05f60ac 100644 Binary files a/docs/articles/over_files/figure-html/unnamed-chunk-12-2.png and b/docs/articles/over_files/figure-html/unnamed-chunk-12-2.png differ diff --git a/docs/articles/over_files/figure-html/unnamed-chunk-6-1.png b/docs/articles/over_files/figure-html/unnamed-chunk-6-1.png index 8c88a52..3bdd070 100644 Binary files a/docs/articles/over_files/figure-html/unnamed-chunk-6-1.png and b/docs/articles/over_files/figure-html/unnamed-chunk-6-1.png differ diff --git a/docs/articles/over_files/figure-html/unnamed-chunk-7-1.png b/docs/articles/over_files/figure-html/unnamed-chunk-7-1.png index fc08260..7802001 100644 Binary files a/docs/articles/over_files/figure-html/unnamed-chunk-7-1.png and b/docs/articles/over_files/figure-html/unnamed-chunk-7-1.png differ diff --git a/docs/articles/over_files/figure-html/unnamed-chunk-8-1.png b/docs/articles/over_files/figure-html/unnamed-chunk-8-1.png index 017394b..1f0ac66 100644 Binary files a/docs/articles/over_files/figure-html/unnamed-chunk-8-1.png and b/docs/articles/over_files/figure-html/unnamed-chunk-8-1.png differ diff --git a/docs/articles/performance.html b/docs/articles/performance.html new file mode 100644 index 0000000..a8d9816 --- /dev/null +++ b/docs/articles/performance.html @@ -0,0 +1,1260 @@ + + + + + + + +Statistical Performance • simaerep + + + + + + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + + +
+

Introduction +

+
+
+

Performance (Statistical) +

+ +
+

Load Portfolio +

+
+df_config <- readr::read_csv("ae_conf_20240220.csv")
+df_ae_rates <- readr::read_csv("ae_rates_20240220.csv")
+
+df_portf_flex <- sim_test_data_portfolio(df_config, df_ae_rates = df_ae_rates, parallel = TRUE, progress = TRUE)
+
+df_portf_flex %>%
+  readr::write_csv("portf.csv")
+
+df_portf_flex <- readr::read_csv("portf.csv")
+
+
+

Simulating Under Reporting +

+

Here we reanalyze performance of {simaerep} integrating the learnings +from previous versions. We will apply the following.

+
    +
  • generate portfolio using flexible AE rates
  • +
  • remove AEs directly from the data set and not from an aggregated +metric using sim_ur() +
  • +
  • set threshold for confusion matrix so that all methods have similar +fpr
  • +
  • test the following {simaerep} parameters with and without +correcting for multiplicity: +
      +
    • default algorithm
    • +
    • default algorithm with active over-reporting scoring
    • +
    • inframe algorithm with visit_med75
    • +
    • inframe algorithm
    • +
    +
  • +
  • test the following outlier detection methods +
      +
    • box-plot
    • +
    • funnel-plot
    • +
    +
  • +
+
+
+

Functions +

+
+funnel <- function(df) {
+  df %>%
+  filter(visit == max(visit), .by = patnum) %>%
+  summarise(
+    Metric = sum(.data$n_ae) / sum(.data$visit),
+    n_ae = sum(n_ae),
+    visit = sum(visit),
+    .by = "site_number"
+  ) %>%
+  mutate(
+        vMu = sum(.data$n_ae) / sum(.data$visit),
+        z_0 = ifelse(.data$vMu == 0,
+          0,
+          (.data$Metric - .data$vMu) /
+            sqrt(.data$vMu / .data$visit)
+        ),
+        phi = mean(.data$z_0^2),
+        z_i = ifelse(.data$vMu == 0 | .data$phi == 0,
+          0,
+          (.data$Metric - .data$vMu) /
+            sqrt(.data$phi * .data$vMu / .data$visit)
+        )
+  )
+}
+
+box <- function(df) {
+  df <- df %>%
+    filter(visit == max(visit), .by = patnum) %>%
+    summarise(
+      event_per_visit = sum(.data$n_ae) / sum(.data$visit),
+      .by = "site_number"
+    )
+  
+  bx <- boxplot.stats(df$event_per_visit)
+  
+  df <- df %>%
+    mutate(
+      box_out = event_per_visit < bx$stats[1]
+    )
+  
+}
+
+
+perf <- function(df_visit, study_id, site_number, ur_rate) {
+  df_vs_study <- df_visit %>%
+    sim_ur(study_id, site_number, ur_rate)
+  
+  df_visit_med75 <- df_vs_study %>%
+    simaerep(under_only = TRUE, progress = FALSE, check = FALSE) %>%
+    .$df_eval %>%
+    filter(.data$site_number == .env$site_number)
+  
+  df_visit_med75_over <- df_vs_study %>%
+    simaerep(under_only = FALSE, progress = FALSE, check = FALSE) %>%
+    .$df_eval %>%
+    filter(.data$site_number == .env$site_number)
+
+  df_inframe <- df_vs_study %>%
+    simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = FALSE) %>%
+    .$df_eval %>%
+    filter(.data$site_number == .env$site_number)
+  
+  df_inframe_visit_med75 <- df_vs_study %>%
+    simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = TRUE) %>%
+    .$df_eval %>%
+    filter(.data$site_number == .env$site_number)
+  
+  funnel_zi <- funnel(df_vs_study) %>%
+    filter(.data$site_number == .env$site_number) %>%
+    pull(z_i)
+  
+  box_out <- box(df_vs_study) %>%
+    filter(.data$site_number == .env$site_number) %>%
+    pull(box_out)
+
+  tibble(
+    score_visit_med75 = df_visit_med75$prob_low_prob_ur,
+    score_visit_med75_no_mult = 1 - df_visit_med75$prob_low,
+    score_visit_med75_over = df_visit_med75_over$prob_low_prob_ur,
+    score_visit_med75_over_no_mult = 1 - df_visit_med75_over$prob_low,
+    score_inframe = df_inframe$prob_low_prob_ur,
+    score_inframe_no_mult = 1 - df_inframe$prob_low,
+    score_inframe_visit_med75 = df_inframe_visit_med75$prob_low_prob_ur,
+    score_inframe_visit_med75_no_mult = 1 - df_inframe_visit_med75$prob_low,
+    score_funnel_zi = funnel_zi,
+    score_box_out = as.integer(box_out),
+    stat_visit_med75_visit_med75 = df_visit_med75$visit_med75,
+    stat_visit_med75_n_pat_with_med75 = df_visit_med75$n_pat_with_med75,
+    stat_visit_med75_mean_ae_site_med75 = df_visit_med75$mean_ae_site_med75,
+    stat_visit_med75_mean_ae_study_med75 = df_visit_med75$mean_ae_study_med75,
+    stat_inframe_visit_med75 = df_inframe_visit_med75$visit_med75,
+    stat_inframe_visit_med75_n_pat_with_med75 = df_inframe_visit_med75$n_pat_with_med75,
+    stat_inframe_visit_med75_events_per_visit_site = df_inframe_visit_med75$events_per_visit_site,
+    stat_inframe_visit_med75_events_per_visit_study = df_inframe_visit_med75$events_per_visit_study,
+    stat_inframe_n_pat = df_inframe$n_pat,
+    stat_inframe_events_per_visit_site = df_inframe$events_per_visit_site,
+    stat_inframe_events_per_visit_study = df_inframe$events_per_visit_study,
+  )
+}
+
+# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0) %>% unlist()
+# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0.5) %>% unlist()
+# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0.75) %>% unlist()
+# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 1) %>% unlist()
+
+
+

Grid +

+
+df_grid <- df_portf_flex %>%
+  distinct(study_id, site_number) %>%
+  # to reduce calculation time we only take every xth study
+  filter(dense_rank(study_id)%%5 == 0) %>%
+  mutate(ur_rate = list(c(0, 0.1, 0.25, 0.5, 0.75, 1))) %>%
+  unnest(ur_rate)
+
+df_grid
+
## # A tibble: 27,120 × 3
+##    study_id site_number ur_rate
+##    <chr>    <chr>         <dbl>
+##  1 0005     4480           0   
+##  2 0005     4480           0.1 
+##  3 0005     4480           0.25
+##  4 0005     4480           0.5 
+##  5 0005     4480           0.75
+##  6 0005     4480           1   
+##  7 0005     4481           0   
+##  8 0005     4481           0.1 
+##  9 0005     4481           0.25
+## 10 0005     4481           0.5 
+## # ℹ 27,110 more rows
+
+
+

Apply +

+
+with_progress_cnd(
+  df_perf <- df_grid %>%
+    mutate(
+      perf = purrr_bar(
+        list(study_id, site_number, ur_rate),
+        .purrr = furrr::future_pmap,
+        .f = function(x, y, z) perf(df_portf_flex, x, y, z),
+        .purrr_args = list(.options = furrr_options(seed = TRUE)),
+        .steps = nrow(.)
+      )
+    )
+)
+
+df_perf %>%
+  unnest(perf) %>%
+  readr::write_csv("perf.csv")
+
+df_perf <- readr::read_csv("perf.csv", show_col_types = FALSE)
+
+df_perf_long <- df_perf %>%
+  pivot_longer(cols = - c(study_id, site_number, ur_rate), names_to = "type", values_to = "score") %>%
+  filter(startsWith(type, "score_")) %>%
+  mutate(type = stringr::str_replace(type, "score_", ""))
+
+
+
+

Evaluation +

+
+

Thresholds +

+

We set the thresholds so that we get a fpr of 0.01.

+

Note that this results in probability thresholds ~ 0.99 for scores +w/o multiplicity correction and in the recommended funne plot score +threshold of -2.

+
+target_fpr <- 0.01
+
+df_thresh <- df_perf_long %>%
+  group_by(type) %>%
+  nest() %>%
+  ungroup() %>%
+  mutate(
+    data = map(data, ~ filter(., ur_rate == 0)),
+    thresh1 = map_dbl(data, ~ quantile(pull(., score), 1 - target_fpr)),
+    thresh2 = map_dbl(data, ~ quantile(pull(., score), target_fpr)),
+    thresh = ifelse(type == "funnel_zi", thresh2, thresh1)
+  ) %>%
+  select(type, thresh)
+  
+df_thresh
+
## # A tibble: 10 × 2
+##    type                        thresh
+##    <chr>                        <dbl>
+##  1 visit_med75                  0.455
+##  2 visit_med75_no_mult          0.989
+##  3 visit_med75_over             0.455
+##  4 visit_med75_over_no_mult     0.989
+##  5 inframe                      0.384
+##  6 inframe_no_mult              0.982
+##  7 inframe_visit_med75          0.412
+##  8 inframe_visit_med75_no_mult  0.987
+##  9 funnel_zi                   -2.03 
+## 10 box_out                      1
+
+
+

Aggregate +

+
+get_prop_test_ci95 <- function(..., ix) {
+  
+  stopifnot(ix %in% c(1, 2))
+  
+  tryCatch(
+    prop.test(...)$conf.int[ix],
+    error = function(cnd) c(NA, NA)[ix]
+  )
+}
+
+df_aggr <- df_perf_long %>%
+  left_join(df_thresh, by = "type") %>%
+  mutate(
+    is_ur = ifelse(type == "funnel_zi", score <= thresh, score >= thresh),
+    is_ur = ifelse(type == "box_out", score == 1, is_ur)
+  ) %>%
+  summarise(
+    n = n(),
+    .by = c(type, ur_rate, is_ur)
+  ) %>%
+  pivot_wider(
+    names_from = is_ur,
+    values_from = n,
+    names_prefix = "is_ur_",
+    values_fill = 0
+  ) %>%
+  mutate(
+    n_sites = is_ur_TRUE + is_ur_FALSE,
+    ratio = is_ur_TRUE / n_sites,
+    ratio_type = ifelse(ur_rate == 0, "fpr", "tpr"),
+    ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)),
+    ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)),
+    type_strip = str_replace(type, "_no_mult", ""),
+    has_mult = ! str_detect(type, "no_mult") & ! type %in% c("funnel_zi", "box_out")
+  )
+
+
+

Table +

+

Methods:

+
    +
  • +visit_med75: default algorithm
  • +
  • +visit_med75_over: default algorithm including +over-reporting score
  • +
  • +inframe: new algorithm using table operations and +no visit_med75
  • +
  • +inframe visit_med75: new aldorithm using table +operations and visit_med75
  • +
  • +funnel_zi: funnel plot derived outlier +detection
  • +
  • +box_out: box plot derived outlier detection
  • +
+

FN: false negatives TP: true positives

+
+df_aggr %>%
+  select(method = type_strip, has_mult, ur_rate, FN = is_ur_FALSE, TP = is_ur_TRUE, n_sites, ratio_type, ratio, ci95_low, ci95_high) %>%
+  knitr::kable(digits = 4)
+ ++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
methodhas_multur_rateFNTPn_sitesratio_typeratioci95_lowci95_high
visit_med75TRUE0.004474464520fpr0.01020.00750.0137
visit_med75FALSE0.004474464520fpr0.01020.00750.0137
visit_med75_overTRUE0.004474464520fpr0.01020.00750.0137
visit_med75_overFALSE0.004474464520fpr0.01020.00750.0137
inframeTRUE0.004470504520fpr0.01110.00830.0147
inframeFALSE0.004473474520fpr0.01040.00770.0139
inframe_visit_med75TRUE0.004468524520fpr0.01150.00870.0152
inframe_visit_med75FALSE0.004471494520fpr0.01080.00810.0144
funnel_ziFALSE0.004474464520fpr0.01020.00750.0137
box_outFALSE0.004463574520fpr0.01260.00970.0164
visit_med75TRUE0.1042642564520tpr0.05660.05020.0639
visit_med75FALSE0.1042812394520tpr0.05290.04660.0599
visit_med75_overTRUE0.1042642564520tpr0.05660.05020.0639
visit_med75_overFALSE0.1042812394520tpr0.05290.04660.0599
inframeTRUE0.1042612594520tpr0.05730.05080.0646
inframeFALSE0.1042033174520tpr0.07010.06290.0781
inframe_visit_med75TRUE0.1042282924520tpr0.06460.05770.0723
inframe_visit_med75FALSE0.1042532674520tpr0.05910.05250.0664
funnel_ziFALSE0.1043591614520tpr0.03560.03050.0415
box_outFALSE0.104445754520tpr0.01660.01320.0209
visit_med75TRUE0.25342510954520tpr0.24230.22990.2551
visit_med75FALSE0.25337811424520tpr0.25270.24010.2656
visit_med75_overTRUE0.25342510954520tpr0.24230.22990.2551
visit_med75_overFALSE0.25337811424520tpr0.25270.24010.2656
inframeTRUE0.25345810624520tpr0.23500.22270.2477
inframeFALSE0.25323312874520tpr0.28470.27170.2982
inframe_visit_med75TRUE0.25334411764520tpr0.26020.24750.2733
inframe_visit_med75FALSE0.25330412164520tpr0.26900.25620.2823
funnel_ziFALSE0.2538776434520tpr0.14230.13230.1529
box_outFALSE0.2543571634520tpr0.03610.03090.0420
visit_med75TRUE0.50216123594520tpr0.52190.50720.5366
visit_med75FALSE0.50197725434520tpr0.56260.54800.5771
visit_med75_overTRUE0.50216123594520tpr0.52190.50720.5366
visit_med75_overFALSE0.50197725434520tpr0.56260.54800.5771
inframeTRUE0.50214623744520tpr0.52520.51050.5399
inframeFALSE0.50183626844520tpr0.59380.57930.6081
inframe_visit_med75TRUE0.50208124394520tpr0.53960.52490.5542
inframe_visit_med75FALSE0.50192725934520tpr0.57370.55910.5881
funnel_ziFALSE0.50260719134520tpr0.42320.40880.4378
box_outFALSE0.50348110394520tpr0.22990.21770.2425
visit_med75TRUE0.75126932514520tpr0.71920.70590.7323
visit_med75FALSE0.75109534254520tpr0.75770.74490.7701
visit_med75_overTRUE0.75126932514520tpr0.71920.70590.7323
visit_med75_overFALSE0.75109534254520tpr0.75770.74490.7701
inframeTRUE0.75120033204520tpr0.73450.72130.7473
inframeFALSE0.7593235884520tpr0.79380.78170.8055
inframe_visit_med75TRUE0.75124932714520tpr0.72370.71030.7366
inframe_visit_med75FALSE0.75105534654520tpr0.76660.75390.7788
funnel_ziFALSE0.75162728934520tpr0.64000.62580.6540
box_outFALSE0.75224222784520tpr0.50400.48930.5187
visit_med75TRUE1.0096935514520tpr0.78560.77330.7974
visit_med75FALSE1.0079137294520tpr0.82500.81350.8359
visit_med75_overTRUE1.0096935514520tpr0.78560.77330.7974
visit_med75_overFALSE1.0079137294520tpr0.82500.81350.8359
inframeTRUE1.0090136194520tpr0.80070.78870.8122
inframeFALSE1.0062338974520tpr0.86220.85170.8720
inframe_visit_med75TRUE1.00100235184520tpr0.77830.76590.7903
inframe_visit_med75FALSE1.0077437464520tpr0.82880.81740.8396
funnel_ziFALSE1.0091636044520tpr0.79730.78530.8089
box_outFALSE1.0077637444520tpr0.82830.81690.8391
+
+
+

Plot +

+
+df_aggr %>%
+  mutate(ur_rate = paste0("under-reporting rate: ",  ur_rate, " - ", ratio_type)
+  ) %>%
+  group_by(ur_rate) %>%
+  ggplot(aes(type, ratio)) +
+    geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type_strip, alpha = has_mult), linewidth = 1) +
+    facet_wrap(~ ur_rate, ncol = 1) +
+    coord_flip() +
+    theme(
+      legend.position = "right",
+      axis.text.y = element_blank(),
+      axis.ticks.y = element_blank()
+    ) +
+    labs(
+      x = "",
+      y = "Ratio (CI95)", 
+      title = "{simaerep} Performance",
+      color = "Method",
+      alpha = "Multiplicity Correction"
+    ) +
+    scale_color_manual(values = rev(RColorBrewer::brewer.pal(n = 6, name = "Dark2"))) +
+    scale_alpha_manual(values = c(1, 0.5))
+

+
+
+

Summary +

+
    +
  • new inframe method has slightly better performance than original +algorithm
  • +
+
+

The new inframe methods compares event per visit rates without +dropping any patients or AEs from the analysis. This is likely to +explain the performance increase. We observed a similar increase when we +optimized visit_med75 in past experiments.

+
+
    +
  • multiplicity correction imposes a penalty on the true positive +rate
  • +
+
+

This observation was already made by the Boeringer Ingelheim Team +during the evaluation of {simaerep}. We can now reporducibly confirm +this. The unaltered probability score as returned by the bootstrap +algorithm already provides very realistic under-reporting +probabilities.

+
+
    +
  • {simaerep} outperforms simpler methods such as funnel plot and box +plot outlier detection.
  • +
+
+

These controly confirm previous observations that were made during +the {simaerep} validation.

+
+
+
+
+
+ + + +
+ + + +
+
+ + + + + + + diff --git a/docs/articles/performance_files/figure-html/plot-1.png b/docs/articles/performance_files/figure-html/plot-1.png new file mode 100644 index 0000000..f40ccdf Binary files /dev/null and b/docs/articles/performance_files/figure-html/plot-1.png differ diff --git a/docs/articles/performance_files/figure-html/unnamed-chunk-10-1.png b/docs/articles/performance_files/figure-html/unnamed-chunk-10-1.png new file mode 100644 index 0000000..0eda94a Binary files /dev/null and b/docs/articles/performance_files/figure-html/unnamed-chunk-10-1.png differ diff --git a/docs/articles/performance_files/figure-html/unnamed-chunk-13-1.png b/docs/articles/performance_files/figure-html/unnamed-chunk-13-1.png new file mode 100644 index 0000000..0503b28 Binary files /dev/null and b/docs/articles/performance_files/figure-html/unnamed-chunk-13-1.png differ diff --git a/docs/articles/portfolio_perf.html b/docs/articles/portfolio_perf.html index 1f727c2..997f917 100644 --- a/docs/articles/portfolio_perf.html +++ b/docs/articles/portfolio_perf.html @@ -4,26 +4,20 @@ - -simaerep Portfolio Performance • simaerep + +(superseeded) simaerep Portfolio Performance • simaerep - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
-

Portfolio Configuration @@ -249,162 +201,162 @@

Portfolio Configuration 0001 -0.4019710 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 10 0001 -0.4019710 +0.3997929 0002 -4.012480 -20.1 +5.108816 +18.1 10 0001 -0.4019710 +0.3997929 0003 -3.573047 -19.9 +3.011091 +18.8 10 0001 -0.4019710 +0.3997929 0004 -4.357624 -18.9 +3.366502 +18.0 10 0001 -0.4019710 +0.3997929 0005 -4.357624 -17.9 +3.831159 +18.7 10 0001 -0.4019710 +0.3997929 0006 -3.695342 -18.9 +2.708013 +19.0 10 0001 -0.4019710 +0.3997929 0007 -3.348300 -17.1 +1.885618 +20.0 10 0001 -0.4019710 +0.3997929 0008 -3.521363 -19.2 +3.333333 +20.0 10 0001 -0.4019710 +0.3997929 0009 -4.372896 -21.3 +3.765339 +22.2 10 0001 -0.4019710 +0.3997929 0010 -3.771236 -19.0 +2.898275 +19.2 10 0002 -0.5097038 +0.4793602 0001 -4.871687 -19.8 +4.237400 +17.8 10 0002 -0.5097038 +0.4793602 0002 -3.414023 -19.9 +3.622461 +19.3 10 0002 -0.5097038 +0.4793602 0003 -5.308274 -19.8 +3.272783 +18.6 10 0002 -0.5097038 +0.4793602 0004 -4.326918 -19.5 +4.648775 +20.5 10 0002 -0.5097038 +0.4793602 0005 -3.171050 -20.5 +3.368152 +20.3 10 0002 -0.5097038 +0.4793602 0006 -3.091206 -19.0 +4.083844 +20.3 10 0002 -0.5097038 +0.4793602 0007 -5.082650 -20.5 +4.900113 +19.3 10 0002 -0.5097038 +0.4793602 0008 -4.191261 -18.3 +3.622461 +20.3 10 0002 -0.5097038 +0.4793602 0009 -4.062019 -18.5 +3.604010 +17.9 10 0002 -0.5097038 +0.4793602 0010 -2.905933 -20.0 +3.308239 +19.5 10 @@ -445,253 +397,253 @@

Simulate Portfolio from Configura 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 1 -0 +1 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 2 1 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 3 -1 +2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 4 -1 +2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 5 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 6 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 7 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 8 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 9 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 10 2 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 11 -4 +3 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 12 -4 +3 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 13 -5 +4 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 14 -6 +4 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 15 -7 +5 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 16 -8 +6 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 17 -10 +7 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 +4.175324 +19.1 0001 18 -10 +7 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -19 -10 +4.175324 +19.1 +0002 +1 +1 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -20 -10 +4.175324 +19.1 +0002 +2 +1 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -21 -10 +4.175324 +19.1 +0002 +3 +1 0001 -0.401971 +0.3997929 0001 -4.600725 -20.5 -0001 -22 -10 +4.175324 +19.1 +0002 +4 +2 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -23 -10 +4.175324 +19.1 +0002 +5 +2 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -24 -10 +4.175324 +19.1 +0002 +6 +2 0001 -0.401971 -0001 -4.600725 -20.5 +0.3997929 0001 -25 -11 +4.175324 +19.1 +0002 +7 +3 @@ -1099,7 +1051,7 @@

Simulate Portfolio622 0001 4 -0 +1 0001 @@ -1112,7 +1064,7 @@

Simulate Portfolio622 0001 5 -0 +1 0001 @@ -1125,7 +1077,7 @@

Simulate Portfolio622 0001 6 -0 +1 0001 @@ -1138,7 +1090,7 @@

Simulate Portfolio622 0001 7 -0 +1 0001 @@ -1151,7 +1103,7 @@

Simulate Portfolio622 0001 8 -0 +1 0001 @@ -1164,7 +1116,7 @@

Simulate Portfolio622 0001 9 -0 +1 0001 @@ -1177,7 +1129,7 @@

Simulate Portfolio622 0001 10 -0 +2 0001 @@ -1190,7 +1142,7 @@

Simulate Portfolio622 0001 11 -0 +2 0001 @@ -1203,7 +1155,7 @@

Simulate Portfolio622 0001 12 -1 +2 0001 @@ -1216,7 +1168,7 @@

Simulate Portfolio622 0001 13 -1 +2 0001 @@ -1229,7 +1181,7 @@

Simulate Portfolio622 0001 14 -1 +2 0001 @@ -1294,7 +1246,7 @@

Simulate Portfolio622 0001 19 -3 +5 0001 @@ -1307,7 +1259,7 @@

Simulate Portfolio622 0001 20 -3 +5 0001 @@ -1320,7 +1272,7 @@

Simulate Portfolio622 0001 21 -3 +5 0001 @@ -1333,7 +1285,7 @@

Simulate Portfolio622 0001 22 -3 +6 0001 @@ -1372,7 +1324,7 @@

Simulate Portfolio622 0002 3 -0 +1 @@ -1494,19 +1446,19 @@

df_scen %>% head(25) %>% knitr::kable()

- +
+++---@@ -1517,15 +1469,15 @@

+ + + - - - @@ -1537,245 +1489,245 @@

- - - - - - - - - - + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + - - + + + + + + + + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + - + + + + + + + - - + + - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + - + + + - - - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + - + + + + + + + - - - - + + + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + @@ -1784,91 +1736,91 @@

+ + + - - - - - - - - + + + - + + + + + + - - - - - - - - + + + + + - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + - - + + + + + - - - - - - + + + @@ -1879,135 +1831,135 @@

- - - - - - - - + + + + + + + + - - + + - - - - - - - + - - + + + + + + + + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + - - - + + + + + + + + + + - - + - - - - - - - + + + + + + + - + + + - - + + + - - - - + + + + + + + + - - - - - - -
study_id site_numberextra_ur_sitesfrac_pat_with_urur_rate n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 mean_ae_study_med75 n_pat_with_med75_studyextra_ur_sitesfrac_pat_with_urur_rate pval prob_low pval_adj0001 000122218.0005.38961077 0 0.0000000 0.001.00000001.0001.00000000.000000022223.5000005.794520730.22891040.119 1.0000000 0.00000000.80457140.1954286
0001 000122217.2005.38961077 00.02531650.0266667 0.1022223.1500005.79452073 1.00000001.0001.00000000.00000000.052 1.0000000 0.00000000.73920000.2608000
0001 000122216.0005.38961077 00.02531650.0266667 0.2522222.6250005.79452073 1.00000001.0001.00000000.00000000.020 1.0000000 0.00000000.44000000.5600000
0001 000122214.0005.38961077 00.02531650.0266667 0.500.53366740.25122221.7500005.794520731.00000000.006 1.0000000 0.00000000.37226970.62773030.26400000.7360000
0001 000122212.0005.38961077 00.02531650.0266667 0.750.04086790.0210.15413030.84586970.06446510.935534922220.8750005.794520731.00000000.0021.00000000.00000000.08800000.9120000
0001 000122210.0005.38961077 00.02531650.0266667 1.000.000053522220.0000005.794520730.0000248 0.0000.00052890.99947110.00109080.9989092 0.0000000 1.0000000
0001 000222296.0007.03571428 0 0.0000000 0.000.67871790.34122259.0000006.454546551.00000001.0001.00000000.0000000 1.0000000 0.00000000.46887500.5311250
0001 000222295.4007.03571428 00.06666670.0350877 0.1022258.1000006.45454655 1.00000000.1761.0001.00000000.0000000 1.0000000 0.00000000.28480000.7152000
0001 000222294.5007.03571428 00.06666670.0350877 0.250.20988220.0960.64428960.35571040.18499270.815007322256.7500006.454546551.00000001.0001.00000000.00000001.00000000.0000000
0001 000222293.0007.03571428 00.06666670.0350877 0.500.03338590.0240.13155030.86844970.07282760.927172422254.5000006.454546550.38981650.1921.00000000.00000000.80800000.1920000
0001 000222291.5007.03571428 00.06666670.0350877 0.750.001001822252.2500006.454546551.0000000 0.0060.00715610.99284390.02640000.97360001.00000000.00000000.26400000.7360000
0001 000222290.0007.03571428 00.06666670.0350877 1.000.00000220.0060.00003300.99996700.02640000.973600022250.0000006.454546550.00000730.0020.00032250.99967750.08800000.9120000
0001 000344186.0004.801980101 0 0.0000000 0.001.00000001.00044184.0000004.8431371020.56002870.277 1.0000000 0.0000000 1.00000000001 000300.03773580.10 4 4 185.4004.80198010100.03809520.101.00000001.0003.6000004.843137102 1.00000000.00000000.133 1.0000000 0.00000000.73150000.2685000
0001 000300.03773580.25 4 4 184.5004.80198010100.03809520.250.90717230.4443.0000004.8431371020.10238890.049 1.0000000 0.00000000.57178540.42821460.70400000.2960000
0001 000300.03773580.50 4 4 183.0004.80198010100.03809520.500.12585340.0630.42596530.57403470.13632790.86367212.0000004.8431371020.00672750.0070.29600870.70399130.30800000.6920000
0001 000300.03773580.75 4 4 181.5004.80198010100.03809520.750.00126691.0000004.8431371020.0000794 0.0000.00857570.99142430.00349280.9965072 0.0000000 1.0000000
0001 000300.03773581.00 4 4 180.0004.80198010100.03809521.000.0000004.843137102 0.0000000 0.000 0.00000040001 000464317.5007.85000020 0 0.0000000 0.000.92181030.44466224.6666675.826087690.28609840.169 1.0000000 0.00000000.57178540.42821460.82622220.1737778
0001 000464316.7507.85000020 00.16666670.0800000 0.100.55252450.23166224.2000005.826087691.00000000.054 1.0000000 0.00000000.35048280.64951720.70400000.2960000
0001 000464315.6257.85000020 00.16666670.0800000 0.251.00000000.0701.00000000.00000000.14437500.855625066223.5000005.826087690.01945000.0070.49130960.50869040.30800000.6920000
0001 000464313.7507.85000020 00.16666670.0800000 0.500.00394820.0060.02127220.97872780.02640000.973600066222.3333335.826087690.00018540.0000.00815660.99184340.00000001.0000000
0001 000464311.8757.85000020 00.16666670.0800000 0.751.00000000.0011.000000066221.1666675.826087690.00000000.0000.00000200.9999980 0.00000000.00643900.99356101.0000000
0001 000464310.0007.85000020 00.16666670.0800000 1.0066220.0000005.82608769 0.00000000.0010.0000.00000001.0000000 0.0000000 1.00000000.00643900.9935610
0001 000500.00000000.00 1 1 302.0007.5555562707.0000008.035714280.85794450.5271.00000000.00000001.0000000 0.00000000.000.03777610.0310.14453450.85546550.08184000.9181600
@@ -2035,31 +1987,31 @@

Portfolio Performance0001 0001 0.00 -0.0000000 +0.1954286 0001 0001 0.10 -0.0000000 +0.2608000 0001 0001 0.25 -0.0000000 +0.5600000 0001 0001 0.50 -0.6277303 +0.7360000 0001 0001 0.75 -0.9355349 +0.9120000 0001 @@ -2071,37 +2023,37 @@

Portfolio Performance0001 0002 0.00 -0.5311250 +0.0000000 0001 0002 0.10 -0.7152000 +0.0000000 0001 0002 0.25 -0.8150073 +0.0000000 0001 0002 0.50 -0.9271724 +0.1920000 0001 0002 0.75 -0.9736000 +0.7360000 0001 0002 1.00 -0.9736000 +0.9120000 0001 @@ -2113,19 +2065,19 @@

Portfolio Performance0001 0003 0.10 -0.0000000 +0.2685000 0001 0003 0.25 -0.4282146 +0.2960000 0001 0003 0.50 -0.8636721 +0.6920000 0001 @@ -2143,43 +2095,43 @@

Portfolio Performance0001 0004 0.00 -0.4282146 +0.1737778 0001 0004 0.10 -0.6495172 +0.2960000 0001 0004 0.25 -0.8556250 +0.6920000 0001 0004 0.50 -0.9736000 +1.0000000 0001 0004 0.75 -0.9935610 +1.0000000 0001 0004 1.00 -0.9935610 +1.0000000 0001 0005 0.00 -0.9181600 +0.0000000 @@ -2235,63 +2187,63 @@

Portfolio Performance 0.00 -15459 -215 +15662 +12 15674 -0.014 +0.001 fpr -0.012 -0.016 +0.000 +0.001 0.10 -14709 -965 +15601 +73 15674 -0.062 +0.005 tpr -0.058 -0.065 +0.004 +0.006 0.25 -12338 -3336 +14874 +800 15674 -0.213 +0.051 tpr -0.206 -0.219 +0.048 +0.055 0.50 -7944 -7730 +11496 +4178 15674 -0.493 +0.267 tpr -0.485 -0.501 +0.260 +0.274 0.75 -4783 -10891 +8172 +7502 15674 -0.695 +0.479 tpr -0.688 -0.702 +0.471 +0.486 1.00 -3567 -12107 +6467 +9207 15674 -0.772 +0.587 tpr -0.766 -0.779 +0.580 +0.595 @@ -3069,19 +3021,19 @@

Effect of Adjusting visit_med75df_scen_old_visit_med75 %>% head(25) %>% knitr::kable()

- +
+++---@@ -3092,15 +3044,15 @@

Effect of Adjusting visit_med75

+ + + - - - @@ -3112,477 +3064,477 @@

Effect of Adjusting visit_med75

- - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + - + - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + - - + + + + + - - - - - - - + + + + - - + + + + + - - - - - - + + + - + - - + + + + + - - - - - - + + + - + - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + - + - - + + + + + - - - - - - - + + + + - - - - + + + + + + - - - - - - + + + + - + - - + + + + - - - - - - + + + + - + - - + + + + - - - - - - + + + + - + - - + + + + - - - - - - + + + + - + - - + + + + - - - - - - - + + + + + - - - - + + + + + + + - - - - - - + + + - + - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + - - - - + + + + + + + - - - - - - + + + - + - - + + + + + - - - - - - + + + - + - - + + + + + - - - - - - + + + - + - - + + + + + - - - - - - + + + - + - - + +
study_id site_numberextra_ur_sitesfrac_pat_with_urur_rate n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 mean_ae_study_med75 n_pat_with_med75_studyextra_ur_sitesfrac_pat_with_urur_rate pval prob_low pval_adj0001 000122175.004.707547106 0 0.0000000 0.001.00000001.0001.00000000.000000022172.04.5700931070.09337040.032 1.0000000 0.00000000.35200000.6480000
0001 000100.01834860.10 2 2 174.504.70754710600.01851850.101.84.570093107 1.00000000.5630.013 1.0000000 0.00000000.62996880.37003120.19066670.8093333
0001 000100.01834860.25 2 2 173.754.70754710600.01851850.251.00000000.3181.00000000.00000000.40651550.59348451.54.5700931070.04150230.0130.60870050.39129950.19066670.8093333
0001 000100.01834860.50 2 2 172.504.70754710600.01851850.500.18470700.1030.74982230.25017770.19539920.80460081.04.5700931070.01082380.0030.29664660.70335340.08800000.9120000
0001 000100.01834860.75 2 2 171.254.70754710600.01851850.751.00000000.54.5700931070.0020871 0.0001.00000000.00000000.09183330.9081667 0.0000000 1.0000000
0001 000100.01834861.00 2 2 170.004.70754710600.01851851.000.00017000.04.5700931070.0002580 0.0000.00177030.99822970.01135190.9886481 0.0000000 1.0000000
0001 000110.04107590.10 2 2 174.504.70188710610.04124580.101.84.561682107 1.00000000.5630.013 1.0000000 0.00000000.62996880.37003120.19066670.8093333
0001 000110.04107590.25 2 2 173.754.69339610610.04124580.251.54.549065107 1.00000000.3250.013 1.0000000 0.00000000.41352610.58647390.19066670.8093333
0001 000110.04107590.50 2 2 172.504.67924510610.04124580.500.18444860.1140.74982230.25017770.20622810.79377191.04.5280371071.00000000.0031.00000000.00000000.08800000.9120000
0001 000110.04107590.75 2 2 171.254.66509410610.04124580.750.54.507009107 1.00000000.0040.000 1.0000000 0.00000000.02200000.97800000.00000001.0000000
0001 000110.04107591.00 2 2 170.004.65094310610.04124581.000.00016730.04.4859811070.0002466 0.0010.00177030.99822970.00762010.99237990.01085020.98914980.04400000.9560000
0001 0001 20.06380320.10 2174.504.693396106 20.06397310.10171.84.550467107 1.00000000.5630.014 1.0000000 0.00000000.62996880.37003120.20533330.7946667
0001 0001 20.06380320.25 2173.754.672170106 20.06397310.25171.54.521028107 1.00000000.3350.014 1.0000000 0.00000000.42427110.57572890.20533330.7946667
0001 0001 20.06380320.50 2172.504.636793106 20.06397310.50171.04.471963107 1.00000000.1270.004 1.0000000 0.00000000.22039190.77960810.08800000.9120000
0001 0001 20.06380320.75 2171.254.601415106 20.06397310.75170.54.422897107 1.00000000.0110.001 1.0000000 0.00000000.04202800.95797200.04400000.9560000
0001 0001 20.06380321.00 2170.004.566038106 20.06397311.000.0002567170.04.3738321070.0003766 0.0050.00255590.99744410.02544780.97455220.01657060.98342940.11000000.8900000
0001 000130.08653040.10 2 2 174.504.68679310630.08670030.101.84.543925107 1.00000000.5630.014 1.0000000 0.00000000.62996880.37003120.20533330.7946667
0001 000130.08653040.25 2 2 173.754.65566010630.08670030.251.00000000.3391.00000000.00000000.42854120.57145881.54.5046731070.04105640.0140.60215980.39784020.20533330.7946667
0001 000130.08653040.50 2 2 172.504.60377410630.08670030.500.23766360.1340.92094660.07905340.22509360.77490641.04.4392521070.01528600.0040.29664660.70335340.08800000.9120000
0001 000130.08653040.75 2 2 171.254.55188710630.08670030.751.00000000.0141.00000000.00000000.04972920.95027080.54.3738321070.00297910.0010.13108010.86891990.04400000.9560000
0001 000130.08653041.00 2 2 170.004.50000010630.08670031.000.00024740.04.3084111070.0003629 0.0070.00249930.99750070.03236610.96763390.01596890.98403110.15400000.8460000
0001 000140.10925770.10 2 2 174.504.67452810640.10942760.101.84.533645107 1.00000000.5650.014 1.0000000 0.00000000.63013900.36986100.20533330.7946667
0001 000140.10925770.25 2 2 173.754.62500010640.10942760.251.54.478972107 1.00000000.3580.014 1.0000000 0.00000000.44922910.55077090.20533330.7946667
0001 000140.10925770.50 2 2 172.504.54245310640.10942760.501.04.387850107 1.00000000.1490.004 1.0000000 0.00000000.24137290.75862710.08800000.9120000
0001 000140.10925770.75 2 2 171.254.45990610640.10942760.750.54.296729107 1.00000000.0190.001 1.0000000 0.00000000.06214870.93785130.04400000.9560000
@@ -3608,19 +3560,21 @@

Effect of Adjusting visit_med75 knitr::kable(digits = 3)

--++---++++ + @@ -3630,63 +3584,69 @@

Effect of Adjusting visit_med75

- - - - + + + + + - - + + - - - - + + + + + - - + + - - - - + + + + + - - + + - - - - + + + + + - - + + - - - - + + + + + - - + + - - - - + + + + + - - + +
ur_rate is_ur_FALSE is_ur_TRUEis_ur_NA n_sites ratio ratio_type
0.0015483191156740.0121566284156700.001 fpr0.0110.0140.0000.001
0.10894344610940440.0499368034024940200.004 tpr0.0480.0500.0030.004
0.257814615898940440.16990404361624940200.038 tpr0.1670.1710.0370.040
0.505490639138940440.416747041931624940200.205 tpr0.4130.4190.2030.208
0.753764256402940440.600573403668024940200.390 tpr0.5970.6030.3870.393
1.003363160413940440.642490504497024940200.478 tpr0.6390.6450.4750.482
@@ -3754,19 +3714,19 @@

Days vs. Visitsdf_scen_days %>% head(25) %>% knitr::kable() - +
+++---@@ -3777,15 +3737,15 @@

Days vs. Visits

+ + + - - - @@ -3797,17 +3757,17 @@

Days vs. Visits

- - - - - - + + + + + + - + @@ -3816,458 +3776,458 @@

Days vs. Visits

- - - - - - - + - - - - + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + - - + + + + + + + + - - + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + - - + + + + + + + + - - + + - - - - - - - + + + + + + + - + - - + + - - - - - - - + - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - + - - + + + + + + + + - - + + - - - - - - - + + + + + + + - + - - + +
study_id site_numberextra_ur_sitesfrac_pat_with_urur_rate n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 mean_ae_study_med75 n_pat_with_med75_studyextra_ur_sitesfrac_pat_with_urur_rate pval prob_low pval_adj0001 0001216807.005.05797169 0 0.0000000 0.00227275.005.36363666 1.00000001.0000.517 1.0000000 0.0000000 1.00000000001 0001216806.305.05797169 00.01428570.0294118 0.101.00000001.0001.00000000.0000000227274.505.363636660.75533630.397 1.0000000 0.00000000.97044440.0295556
0001 0001216805.255.05797169 00.01428570.0294118 0.25227273.755.36363666 1.00000001.0001.00000000.00000000.174 1.0000000 0.00000000.76560000.2344000
0001 0001216803.505.05797169 00.01428570.0294118 0.501.00000000.314227272.505.363636660.08530880.041 1.0000000 0.00000000.49229430.50770570.31533330.6846667
0001 0001216801.755.05797169 00.01428570.0294118 0.75227271.255.36363666 1.00000000.0860.005 1.0000000 0.00000000.22258820.77741180.15400000.8460000
0001 0001216800.005.05797169 00.01428570.0294118 1.000.01158640.0120.07634680.92365320.07989960.9201004227270.005.363636660.00004990.0000.00219500.99780500.00000001.0000000
0001 0001216806.305.04202969 10.04458870.0571895 0.10227274.505.34090966 1.00000001.0001.00000000.00000000.399 1.0000000 0.00000000.97533330.0246667
0001 0001216805.255.01811669 10.04458870.0571895 0.25227273.755.30681866 1.00000001.0001.00000000.00000000.180 1.0000000 0.00000000.79200000.2080000
0001 0001216803.504.97826169 10.04458870.0571895 0.50227272.505.25000066 1.00000000.3450.047 1.0000000 0.00000000.53233030.46766970.34466670.6553333
0001 00012 16801.754.9384066910.04458870.0571895 0.75227271.255.19318266 1.00000000.1170.008 1.0000000 0.00000000.26334650.73665350.17600000.8240000
0001 0001216800.004.89855169 10.04458870.0571895 1.000.01801130.0430.11388190.88611810.14133010.8586699227270.005.136364660.00007230.0030.00317920.99682080.13200000.8680000
0001 0001 216806.305.0202906920.07489180.0849673 0.10227274.505.31969766 1.00000001.0001.00000000.00000000.399 1.0000000 0.00000000.97533330.0246667
0001 0001 216805.254.9637686920.07489180.0849673 0.25227273.755.25378866 1.00000001.0001.00000000.00000000.181 1.0000000 0.00000000.79640000.2036000
0001 0001 216803.504.8695656920.07489180.0849673 0.50227272.505.14393966 1.00000000.3600.050 1.0000000 0.00000000.54198680.45801320.36666670.6333333
0001 0001 216801.754.7753626920.07489180.0849673 0.75227271.255.03409166 1.00000000.1320.010 1.0000000 0.00000000.28132500.71867500.22000000.7800000
0001 0001 216800.004.6811596920.07489180.0849673 1.000.01707550.0710.11009340.88990660.19804500.8019550227270.004.924242660.00010560.0080.00464810.99535190.17600000.8240000
0001 0001216806.304.99855169 30.10519480.1127451 0.10227274.505.30909166 1.00000001.0001.00000000.00000000.400 1.0000000 0.00000000.97777780.0222222
0001 0001216805.254.90942069 30.10519480.1127451 0.25227273.755.22727366 1.00000001.0001.00000000.00000000.192 1.0000000 0.00000000.82800000.1720000
0001 0001216803.504.76087069 30.10519480.1127451 0.501.00000000.378227272.505.090909660.14510100.058 1.0000000 0.00000000.55499680.44500320.42350000.5765000
0001 0001216801.754.61231969 30.10519480.1127451 0.75227271.254.95454666 1.00000000.1500.014 1.0000000 0.00000000.30628740.69371260.30800000.6920000
0001 0001216800.004.46376869 30.10519480.1127451 1.000.02609960.1000.15964080.84035920.24357140.7564286227270.004.818182660.00016190.0120.00712290.99287710.26400000.7360000
0001 0001216806.304.97971069 40.13549780.1405229 0.10227274.505.29393966 1.00000001.0001.00000000.00000000.401 1.0000000 0.00000000.98022220.0197778
0001 0001216805.254.86231969 40.13549780.1405229 0.25227273.755.18939466 1.00000001.0001.00000000.00000000.207 1.0000000 0.00000000.82800000.1720000
0001 0001216803.504.66666769 40.13549780.1405229 0.501.00000000.403227272.505.015151660.14321600.068 1.0000000 0.00000000.58540150.41459850.42350000.5765000
0001 0001216801.754.47101469 40.13549780.1405229 0.75227271.254.84090966 1.00000000.1750.021 1.0000000 0.00000000.32609290.67390710.31533330.6846667
@@ -4315,63 +4275,63 @@

Days vs. Visits 0.00 -15450 -224 -15674 -0.014 +14441 +7 +14448 +0.000 fpr -0.013 -0.016 +0.000 +0.001 0.10 -87426 -6618 -94044 -0.070 +86141 +547 +86688 +0.006 tpr -0.069 -0.072 +0.006 +0.007 0.25 -72046 -21998 -94044 -0.234 +80821 +5867 +86688 +0.068 tpr -0.231 -0.237 +0.066 +0.069 0.50 -50373 -43671 -94044 -0.464 +66036 +20652 +86688 +0.238 tpr -0.461 -0.468 +0.235 +0.241 0.75 -38042 -56002 -94044 -0.595 +54199 +32489 +86688 +0.375 tpr -0.592 -0.599 +0.372 +0.378 1.00 -37942 -56102 -94044 -0.597 +51520 +35168 +86688 +0.406 tpr -0.593 -0.600 +0.402 +0.409 @@ -4413,16 +4373,16 @@

Heuristic Rank## # A tibble: 15,674 × 6 ## study_id site_number visit n_ae ae_per_visit ls_study_ae_per_visit ## <chr> <chr> <int> <int> <dbl> <list> -## 1 0001 0001 44 9 0.205 <dbl [43]> -## 2 0001 0002 56 16 0.286 <dbl [43]> -## 3 0001 0003 78 20 0.256 <dbl [43]> -## 4 0001 0004 207 58 0.280 <dbl [43]> +## 1 0001 0001 44 13 0.295 <dbl [43]> +## 2 0001 0002 53 15 0.283 <dbl [43]> +## 3 0001 0003 74 19 0.257 <dbl [43]> +## 4 0001 0004 215 57 0.265 <dbl [43]> ## 5 0001 0005 30 9 0.3 <dbl [43]> ## 6 0001 0006 35 11 0.314 <dbl [43]> -## 7 0001 0007 36 11 0.306 <dbl [43]> -## 8 0001 0008 183 38 0.208 <dbl [43]> -## 9 0001 0009 40 8 0.2 <dbl [43]> -## 10 0001 0010 135 37 0.274 <dbl [43]> +## 7 0001 0007 36 14 0.389 <dbl [43]> +## 8 0001 0008 172 37 0.215 <dbl [43]> +## 9 0001 0009 40 19 0.475 <dbl [43]> +## 10 0001 0010 137 32 0.234 <dbl [43]> ## # ℹ 15,664 more rows

We write a function that: - determines how many sites should be flagged in a study - pools ae_per_visit rates and ranks sites (using @@ -4478,11 +4438,11 @@

Heuristic Rank## # A tibble: 6 × 3 ## ur_rate ae_per_visit is_ur ## <dbl> <dbl> <lgl> -## 1 0 0.205 FALSE -## 2 0.1 0.184 FALSE -## 3 0.25 0.153 FALSE -## 4 0.5 0.102 TRUE -## 5 0.75 0.0511 TRUE +## 1 0 0.295 FALSE +## 2 0.1 0.266 FALSE +## 3 0.25 0.222 FALSE +## 4 0.5 0.148 TRUE +## 5 0.75 0.0739 TRUE ## 6 1 0 TRUE

We apply.

@@ -4537,48 +4497,48 @@ 

Heuristic Rank 0.00 -1541 +1560 15674 -0.098 +0.100 fpr -0.094 -0.103 +0.095 +0.104 0.10 -2096 +2134 15674 -0.134 +0.136 tpr -0.128 -0.139 +0.131 +0.142 0.25 -4598 +4716 15674 -0.293 -tpr -0.286 0.301 +tpr +0.294 +0.308 0.50 -11593 +11753 15674 -0.740 +0.750 tpr -0.733 -0.746 +0.743 +0.757 0.75 -15164 +15197 15674 -0.967 -tpr -0.965 0.970 +tpr +0.967 +0.972 1.00 @@ -4673,25 +4633,25 @@

Heuristic Box Plot Outlier 0.00 -412 +405 15674 0.026 fpr -0.024 -0.029 +0.023 +0.028 0.10 -609 +599 15674 -0.039 +0.038 tpr -0.036 -0.042 +0.035 +0.041 0.25 -1609 +1612 15674 0.103 tpr @@ -4700,30 +4660,30 @@

Heuristic Box Plot Outlier 0.50 -6628 +6619 15674 -0.423 +0.422 tpr 0.415 -0.431 +0.430 0.75 -10307 +10754 15674 -0.658 +0.686 tpr -0.650 -0.665 +0.679 +0.693 1.00 -12417 +13408 15674 -0.792 +0.855 tpr -0.786 -0.799 +0.850 +0.861 @@ -4828,16 +4788,16 @@

Plot Performance Metrics fpr {simaerep} unadjusted visit-med75 -0.012 -0.011 -0.014 +0.001 +0.000 +0.001 fpr {simaerep} AE per days on study -0.014 -0.013 -0.016 +0.000 +0.000 +0.001 fpr @@ -4849,37 +4809,37 @@

Plot Performance Metrics fpr {simaerep} default -0.014 -0.012 -0.016 +0.001 +0.000 +0.001 fpr heuristic - rank -0.098 -0.094 -0.103 +0.100 +0.095 +0.104 fpr heuristic - boxplot outlier 0.026 -0.024 -0.029 +0.023 +0.028 under-reporting rate: 0.1 - tpr {simaerep} unadjusted visit-med75 -0.049 -0.048 -0.050 +0.004 +0.003 +0.004 under-reporting rate: 0.1 - tpr {simaerep} AE per days on study -0.070 -0.069 -0.072 +0.006 +0.006 +0.007 under-reporting rate: 0.1 - tpr @@ -4891,37 +4851,37 @@

Plot Performance Metrics under-reporting rate: 0.1 - tpr {simaerep} default -0.062 -0.058 -0.065 +0.005 +0.004 +0.006 under-reporting rate: 0.1 - tpr heuristic - rank -0.134 -0.128 -0.139 +0.136 +0.131 +0.142 under-reporting rate: 0.1 - tpr heuristic - boxplot outlier -0.039 -0.036 -0.042 +0.038 +0.035 +0.041 under-reporting rate: 0.25 - tpr {simaerep} unadjusted visit-med75 -0.169 -0.167 -0.171 +0.038 +0.037 +0.040 under-reporting rate: 0.25 - tpr {simaerep} AE per days on study -0.234 -0.231 -0.237 +0.068 +0.066 +0.069 under-reporting rate: 0.25 - tpr @@ -4933,16 +4893,16 @@

Plot Performance Metrics under-reporting rate: 0.25 - tpr {simaerep} default -0.213 -0.206 -0.219 +0.051 +0.048 +0.055 under-reporting rate: 0.25 - tpr heuristic - rank -0.293 -0.286 0.301 +0.294 +0.308 under-reporting rate: 0.25 - tpr @@ -4954,16 +4914,16 @@

Plot Performance Metrics under-reporting rate: 0.5 - tpr {simaerep} unadjusted visit-med75 -0.416 -0.413 -0.419 +0.205 +0.203 +0.208 under-reporting rate: 0.5 - tpr {simaerep} AE per days on study -0.464 -0.461 -0.468 +0.238 +0.235 +0.241 under-reporting rate: 0.5 - tpr @@ -4975,37 +4935,37 @@

Plot Performance Metrics under-reporting rate: 0.5 - tpr {simaerep} default -0.493 -0.485 -0.501 +0.267 +0.260 +0.274 under-reporting rate: 0.5 - tpr heuristic - rank -0.740 -0.733 -0.746 +0.750 +0.743 +0.757 under-reporting rate: 0.5 - tpr heuristic - boxplot outlier -0.423 +0.422 0.415 -0.431 +0.430 under-reporting rate: 0.75 - tpr {simaerep} unadjusted visit-med75 -0.600 -0.597 -0.603 +0.390 +0.387 +0.393 under-reporting rate: 0.75 - tpr {simaerep} AE per days on study -0.595 -0.592 -0.599 +0.375 +0.372 +0.378 under-reporting rate: 0.75 - tpr @@ -5017,37 +4977,37 @@

Plot Performance Metrics under-reporting rate: 0.75 - tpr {simaerep} default -0.695 -0.688 -0.702 +0.479 +0.471 +0.486 under-reporting rate: 0.75 - tpr heuristic - rank -0.967 -0.965 0.970 +0.967 +0.972 under-reporting rate: 0.75 - tpr heuristic - boxplot outlier -0.658 -0.650 -0.665 +0.686 +0.679 +0.693 under-reporting rate: 1 - tpr {simaerep} unadjusted visit-med75 -0.642 -0.639 -0.645 +0.478 +0.475 +0.482 under-reporting rate: 1 - tpr {simaerep} AE per days on study -0.597 -0.593 -0.600 +0.406 +0.402 +0.409 under-reporting rate: 1 - tpr @@ -5059,9 +5019,9 @@

Plot Performance Metrics under-reporting rate: 1 - tpr {simaerep} default -0.772 -0.766 -0.779 +0.587 +0.580 +0.595 under-reporting rate: 1 - tpr @@ -5073,44 +5033,35 @@

Plot Performance Metrics under-reporting rate: 1 - tpr heuristic - boxplot outlier -0.792 -0.786 -0.799 +0.855 +0.850 +0.861
 plan(sequential)

- - - - + -
- - + diff --git a/docs/articles/portfolio_perf_files/figure-html/check_portf-1.png b/docs/articles/portfolio_perf_files/figure-html/check_portf-1.png index 93e08c3..a8d7971 100644 Binary files a/docs/articles/portfolio_perf_files/figure-html/check_portf-1.png and b/docs/articles/portfolio_perf_files/figure-html/check_portf-1.png differ diff --git a/docs/articles/portfolio_perf_files/figure-html/check_portf-2.png b/docs/articles/portfolio_perf_files/figure-html/check_portf-2.png index 12fb0bc..a816695 100644 Binary files a/docs/articles/portfolio_perf_files/figure-html/check_portf-2.png and b/docs/articles/portfolio_perf_files/figure-html/check_portf-2.png differ diff --git a/docs/articles/portfolio_perf_files/figure-html/check_portf_2-1.png b/docs/articles/portfolio_perf_files/figure-html/check_portf_2-1.png index 952790e..248837b 100644 Binary files a/docs/articles/portfolio_perf_files/figure-html/check_portf_2-1.png and b/docs/articles/portfolio_perf_files/figure-html/check_portf_2-1.png differ diff --git a/docs/articles/portfolio_perf_files/figure-html/check_portf_2-2.png b/docs/articles/portfolio_perf_files/figure-html/check_portf_2-2.png index 6e89eb4..b1ac11a 100644 Binary files a/docs/articles/portfolio_perf_files/figure-html/check_portf_2-2.png and b/docs/articles/portfolio_perf_files/figure-html/check_portf_2-2.png differ diff --git a/docs/articles/portfolio_perf_files/figure-html/plot-1.png b/docs/articles/portfolio_perf_files/figure-html/plot-1.png index bca62db..787c145 100644 Binary files a/docs/articles/portfolio_perf_files/figure-html/plot-1.png and b/docs/articles/portfolio_perf_files/figure-html/plot-1.png differ diff --git a/docs/articles/sas_files.html b/docs/articles/sas_files.html index c020967..17a9546 100644 --- a/docs/articles/sas_files.html +++ b/docs/articles/sas_files.html @@ -4,7 +4,7 @@ - + SAS Files as a Data Source • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
- -
- - + diff --git a/docs/articles/sas_files_files/figure-html/unnamed-chunk-8-1.png b/docs/articles/sas_files_files/figure-html/unnamed-chunk-8-1.png index 5302346..836388c 100644 Binary files a/docs/articles/sas_files_files/figure-html/unnamed-chunk-8-1.png and b/docs/articles/sas_files_files/figure-html/unnamed-chunk-8-1.png differ diff --git a/docs/articles/usability_limits.html b/docs/articles/usability_limits.html index 370044f..5ee9f43 100644 --- a/docs/articles/usability_limits.html +++ b/docs/articles/usability_limits.html @@ -4,7 +4,7 @@ - + Address Usability Limits of Bootstrap Method • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
-
- -
- - + diff --git a/docs/articles/visit_med75.html b/docs/articles/visit_med75.html index 3deb8b1..3ed35a3 100644 --- a/docs/articles/visit_med75.html +++ b/docs/articles/visit_med75.html @@ -4,7 +4,7 @@ - + Adjusted Evaluation Point visit_med75 • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - - -
-
-
- - - -
- -
- - + diff --git a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-2-1.png b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-2-1.png index c2b3bc2..917b20e 100644 Binary files a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-2-1.png and b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-2-1.png differ diff --git a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-3-1.png b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-3-1.png index a285bbb..0879e89 100644 Binary files a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-3-1.png and b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-3-1.png differ diff --git a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-4-1.png b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-4-1.png index 32c9992..a6c7d5b 100644 Binary files a/docs/articles/visit_med75_files/figure-html/unnamed-chunk-4-1.png and b/docs/articles/visit_med75_files/figure-html/unnamed-chunk-4-1.png differ diff --git a/docs/articles/visits_or_days.html b/docs/articles/visits_or_days.html index 3450245..b3b4c93 100644 --- a/docs/articles/visits_or_days.html +++ b/docs/articles/visits_or_days.html @@ -4,7 +4,7 @@ - + Aggregate AEs by Days or Visit? • simaerep @@ -12,18 +12,12 @@ - - - - - - - - - + + + + + - - + + Skip to contents -
-
- - + diff --git a/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-10-1.png b/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-10-1.png index 5302346..836388c 100644 Binary files a/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-10-1.png and b/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-10-1.png differ diff --git a/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-9-1.png b/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-9-1.png index 8df19ce..558e39b 100644 Binary files a/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-9-1.png and b/docs/articles/visits_or_days_files/figure-html/unnamed-chunk-9-1.png differ diff --git a/docs/authors.html b/docs/authors.html index 763bac8..b6fe33a 100644 --- a/docs/authors.html +++ b/docs/authors.html @@ -1,151 +1,102 @@ -Authors and Citation • simaerep - - -
-
-
-
- - - +
+

Authors

+
  • Bjoern Koneswarakantha. Author, maintainer, copyright holder.

  • -

    F. Hoffmann-La Roche Ltd. Copyright holder. +

    F. Hoffmann-La Roche Ltd. Copyright holder.

-
-
-

Citation

- Source: DESCRIPTION -
-
+
+

Citation

+

Source: DESCRIPTION

-

Koneswarakantha B (2024). +

Koneswarakantha B (2024). simaerep: Find Clinical Trial Sites Under-Reporting Adverse Events. -R package version 0.4.3.900, https://github.com/openpharma/simaerep, https://openpharma.github.io/simaerep/. +R package version 0.5.0.900, https://github.com/openpharma/simaerep, https://openpharma.github.io/simaerep/.

-
@Manual{,
+      
@Manual{,
   title = {simaerep: Find Clinical Trial Sites Under-Reporting Adverse Events},
   author = {Bjoern Koneswarakantha},
   year = {2024},
-  note = {R package version 0.4.3.900, https://github.com/openpharma/simaerep},
+  note = {R package version 0.5.0.900, https://github.com/openpharma/simaerep},
   url = {https://openpharma.github.io/simaerep/},
 }
+
-
- -
- +
-
+
+ - - diff --git a/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js b/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js new file mode 100644 index 0000000..e8f21f7 --- /dev/null +++ b/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function M(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function j(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${j(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${j(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=M(t.dataset[n])}return e},getDataAttribute:(t,e)=>M(t.getAttribute(`data-bs-${j(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.1"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return n(e)},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",Mt="collapsing",jt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(Mt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(Mt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Mt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(jt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];me(s)&&ue(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function Me(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const je={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:Me(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,We(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var ze={passive:!0};const Re={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:Me(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==P(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},li={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Vt,qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],M=f?-T[$]/2:0,j=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-M-q-z-O.mainAxis:j-q-z-O.mainAxis,K=v?-E[$]/2+M+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,Mn=`hide${xn}`,jn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,Mn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,jn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,jn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",Ms="Home",js="End",Fs="active",Hs="fade",Ws="show",Bs=":not(.dropdown-toggle)",zs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Rs=`.nav-link${Bs}, .list-group-item${Bs}, [role="tab"]${Bs}, ${zs}`,qs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Vs extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,Ms,js].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([Ms,js].includes(t.key))i=e[t.key===Ms?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Vs.getOrCreateInstance(i).show())}_getChildren(){return z.find(Rs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(".dropdown-toggle",Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(Rs)?t:z.findOne(Rs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Vs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,zs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Vs.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(qs))Vs.getOrCreateInstance(t)})),m(Vs);const Ks=".bs.toast",Qs=`mouseover${Ks}`,Xs=`mouseout${Ks}`,Ys=`focusin${Ks}`,Us=`focusout${Ks}`,Gs=`hide${Ks}`,Js=`hidden${Ks}`,Zs=`show${Ks}`,to=`shown${Ks}`,eo="hide",io="show",no="showing",so={animation:"boolean",autohide:"boolean",delay:"number"},oo={animation:!0,autohide:!0,delay:5e3};class ro extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return oo}static get DefaultType(){return so}static get NAME(){return"toast"}show(){N.trigger(this._element,Zs).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(eo),d(this._element),this._element.classList.add(io,no),this._queueCallback((()=>{this._element.classList.remove(no),N.trigger(this._element,to),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Gs).defaultPrevented||(this._element.classList.add(no),this._queueCallback((()=>{this._element.classList.add(eo),this._element.classList.remove(no,io),N.trigger(this._element,Js)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(io),super.dispose()}isShown(){return this._element.classList.contains(io)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Qs,(t=>this._onInteraction(t,!0))),N.on(this._element,Xs,(t=>this._onInteraction(t,!1))),N.on(this._element,Ys,(t=>this._onInteraction(t,!0))),N.on(this._element,Us,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ro.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ro),m(ro),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Vs,Toast:ro,Tooltip:cs}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js.map b/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js.map new file mode 100644 index 0000000..3863da8 --- /dev/null +++ b/docs/deps/bootstrap-5.3.1/bootstrap.bundle.min.js.map @@ -0,0 +1 @@ +{"version":3,"names":["elementMap","Map","Data","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete","TRANSITION_END","parseSelector","selector","window","CSS","escape","replace","match","id","triggerTransitionEnd","dispatchEvent","Event","isElement","object","jquery","nodeType","getElement","length","document","querySelector","isVisible","getClientRects","elementIsVisible","getComputedStyle","getPropertyValue","closedDetails","closest","summary","parentNode","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","noop","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","isRTL","dir","defineJQueryPlugin","plugin","callback","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","readyState","addEventListener","push","execute","possibleCallback","args","defaultValue","executeAfterTransition","transitionElement","waitForTransition","emulatedDuration","transitionDuration","transitionDelay","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","getTransitionDurationFromElement","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","listLength","index","indexOf","Math","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","nativeEvents","Set","makeEventUid","uid","getElementEvents","findHandler","events","callable","delegationSelector","Object","values","find","event","normalizeParameters","originalTypeEvent","delegationFunction","isDelegated","typeEvent","getTypeEvent","addHandler","oneOff","wrapFunction","relatedTarget","delegateTarget","call","this","handlers","previousFunction","domElements","querySelectorAll","domElement","hydrateObj","EventHandler","off","type","apply","bootstrapDelegationHandler","bootstrapHandler","removeHandler","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","handlerKey","entries","includes","on","one","inNamespace","isNamespace","startsWith","elementEvent","slice","keyHandlers","trigger","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","evt","cancelable","preventDefault","obj","meta","value","_unused","defineProperty","configurable","normalizeData","toString","JSON","parse","decodeURIComponent","normalizeDataKey","chr","toLowerCase","Manipulator","setDataAttribute","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","dataset","filter","pureKey","charAt","getDataAttribute","Config","Default","DefaultType","Error","_getConfig","config","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","jsonConfig","constructor","configTypes","property","expectedTypes","valueType","prototype","RegExp","test","TypeError","toUpperCase","BaseComponent","super","_element","_config","DATA_KEY","dispose","EVENT_KEY","propertyName","getOwnPropertyNames","_queueCallback","isAnimated","getInstance","getOrCreateInstance","VERSION","eventName","getSelector","hrefAttribute","trim","SelectorEngine","concat","Element","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","map","join","el","getSelectorFromElement","getElementFromSelector","getMultipleElementsFromSelector","enableDismissTrigger","component","method","clickEvent","tagName","EVENT_CLOSE","EVENT_CLOSED","Alert","close","_destroyElement","each","data","undefined","SELECTOR_DATA_TOGGLE","Button","toggle","button","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","endCallback","leftCallback","rightCallback","Swipe","isSupported","_deltaX","_supportPointerEvents","PointerEvent","_initEvents","_start","_eventIsPointerPenTouch","clientX","touches","_end","_handleSwipe","_move","absDeltaX","abs","direction","add","pointerType","navigator","maxTouchPoints","DATA_API_KEY","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_DRAG_START","EVENT_LOAD_DATA_API","EVENT_CLICK_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_ACTIVE","SELECTOR_ACTIVE","SELECTOR_ITEM","SELECTOR_ACTIVE_ITEM","KEY_TO_DIRECTION","ArrowLeft","ArrowRight","interval","keyboard","pause","ride","touch","wrap","Carousel","_interval","_activeElement","_isSliding","touchTimeout","_swipeHelper","_indicatorsElement","_addEventListeners","cycle","_slide","nextWhenVisible","hidden","_clearInterval","_updateInterval","setInterval","_maybeEnableCycle","to","items","_getItems","activeIndex","_getItemIndex","_getActive","order","defaultInterval","_keydown","_addTouchEventListeners","img","swipeConfig","_directionToOrder","endCallBack","clearTimeout","_setActiveIndicatorElement","activeIndicator","newActiveIndicator","elementInterval","parseInt","isNext","nextElement","nextElementIndex","triggerEvent","_orderToDirection","isCycling","directionalClassName","orderClassName","completeCallBack","_isAnimated","clearInterval","carousel","slideIndex","carousels","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_DEEPER_CHILDREN","parent","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElement","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activeChildren","_getFirstLevelChildren","activeInstance","dimension","_getDimension","style","scrollSize","complete","getBoundingClientRect","selected","triggerArray","isOpen","top","bottom","right","left","auto","basePlacements","start","end","clippingParents","viewport","popper","reference","variationPlacements","reduce","acc","placement","placements","beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite","modifierPhases","getNodeName","nodeName","getWindow","node","ownerDocument","defaultView","isHTMLElement","HTMLElement","isShadowRoot","applyStyles$1","enabled","phase","_ref","state","elements","forEach","styles","assign","effect","_ref2","initialStyles","position","options","strategy","margin","arrow","hasOwnProperty","attribute","requires","getBasePlacement","round","getUAString","uaData","userAgentData","brands","isArray","item","brand","version","userAgent","isLayoutViewport","includeScale","isFixedStrategy","clientRect","scaleX","scaleY","offsetWidth","width","height","visualViewport","addVisualOffsets","x","offsetLeft","y","offsetTop","getLayoutRect","rootNode","isSameNode","host","isTableElement","getDocumentElement","getParentNode","assignedSlot","getTrueOffsetParent","offsetParent","getOffsetParent","isFirefox","currentNode","css","transform","perspective","contain","willChange","getContainingBlock","getMainAxisFromPlacement","within","mathMax","mathMin","mergePaddingObject","paddingObject","expandToHashMap","hashMap","arrow$1","_state$modifiersData$","arrowElement","popperOffsets","modifiersData","basePlacement","axis","len","padding","rects","toPaddingObject","arrowRect","minProp","maxProp","endDiff","startDiff","arrowOffsetParent","clientSize","clientHeight","clientWidth","centerToReference","center","offset","axisProp","centerOffset","_options$element","requiresIfExists","getVariation","unsetSides","mapToStyles","_Object$assign2","popperRect","variation","offsets","gpuAcceleration","adaptive","roundOffsets","isFixed","_offsets$x","_offsets$y","_ref3","hasX","hasY","sideX","sideY","win","heightProp","widthProp","_Object$assign","commonStyles","_ref4","dpr","devicePixelRatio","roundOffsetsByDPR","computeStyles$1","_ref5","_options$gpuAccelerat","_options$adaptive","_options$roundOffsets","passive","eventListeners","_options$scroll","scroll","_options$resize","resize","scrollParents","scrollParent","update","hash","getOppositePlacement","matched","getOppositeVariationPlacement","getWindowScroll","scrollLeft","pageXOffset","scrollTop","pageYOffset","getWindowScrollBarX","isScrollParent","_getComputedStyle","overflow","overflowX","overflowY","getScrollParent","listScrollParents","_element$ownerDocumen","isBody","updatedList","rectToClientRect","rect","getClientRectFromMixedType","clippingParent","html","layoutViewport","getViewportRect","clientTop","clientLeft","getInnerBoundingClientRect","winScroll","scrollWidth","scrollHeight","getDocumentRect","computeOffsets","commonX","commonY","mainAxis","detectOverflow","_options","_options$placement","_options$strategy","_options$boundary","boundary","_options$rootBoundary","rootBoundary","_options$elementConte","elementContext","_options$altBoundary","altBoundary","_options$padding","altContext","clippingClientRect","mainClippingParents","clipperElement","getClippingParents","firstClippingParent","clippingRect","accRect","getClippingRect","contextElement","referenceClientRect","popperClientRect","elementClientRect","overflowOffsets","offsetData","multiply","computeAutoPlacement","flipVariations","_options$allowedAutoP","allowedAutoPlacements","allPlacements","allowedPlacements","overflows","sort","a","b","flip$1","_skip","_options$mainAxis","checkMainAxis","_options$altAxis","altAxis","checkAltAxis","specifiedFallbackPlacements","fallbackPlacements","_options$flipVariatio","preferredPlacement","oppositePlacement","getExpandedFallbackPlacements","referenceRect","checksMap","makeFallbackChecks","firstFittingPlacement","i","_basePlacement","isStartVariation","isVertical","mainVariationSide","altVariationSide","checks","every","check","_loop","_i","fittingPlacement","reset","getSideOffsets","preventedOffsets","isAnySideFullyClipped","some","side","hide$1","preventOverflow","referenceOverflow","popperAltOverflow","referenceClippingOffsets","popperEscapeOffsets","isReferenceHidden","hasPopperEscaped","offset$1","_options$offset","invertDistance","skidding","distance","distanceAndSkiddingToXY","_data$state$placement","popperOffsets$1","preventOverflow$1","_options$tether","tether","_options$tetherOffset","tetherOffset","isBasePlacement","tetherOffsetValue","normalizedTetherOffsetValue","offsetModifierState","_offsetModifierState$","mainSide","altSide","additive","minLen","maxLen","arrowPaddingObject","arrowPaddingMin","arrowPaddingMax","arrowLen","minOffset","maxOffset","clientOffset","offsetModifierValue","tetherMax","preventedOffset","_offsetModifierState$2","_mainSide","_altSide","_offset","_len","_min","_max","isOriginSide","_offsetModifierValue","_tetherMin","_tetherMax","_preventedOffset","v","withinMaxClamp","getCompositeRect","elementOrVirtualElement","isOffsetParentAnElement","offsetParentIsScaled","isElementScaled","modifiers","visited","result","modifier","dep","depModifier","DEFAULT_OPTIONS","areValidElements","arguments","_key","popperGenerator","generatorOptions","_generatorOptions","_generatorOptions$def","defaultModifiers","_generatorOptions$def2","defaultOptions","pending","orderedModifiers","effectCleanupFns","isDestroyed","setOptions","setOptionsAction","cleanupModifierEffects","merged","orderModifiers","current","existing","m","_ref$options","cleanupFn","forceUpdate","_state$elements","_state$orderedModifie","_state$orderedModifie2","Promise","resolve","then","destroy","onFirstUpdate","createPopper","computeStyles","applyStyles","flip","ARROW_UP_KEY","ARROW_DOWN_KEY","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","SELECTOR_DATA_TOGGLE_SHOWN","SELECTOR_MENU","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","autoClose","display","popperConfig","Dropdown","_popper","_parent","_menu","_inNavbar","_detectNavbar","_createPopper","focus","_completeHide","Popper","referenceElement","_getPopperConfig","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","_selectMenuItem","clearMenus","openToggles","context","composedPath","isMenuTarget","dataApiKeydownHandler","isInput","isEscapeEvent","isUpOrDownEvent","getToggleButton","stopPropagation","EVENT_MOUSEDOWN","className","clickCallback","rootElement","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_NAV_BACKWARD","autofocus","trapElement","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","shiftKey","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","getWidth","documentWidth","innerWidth","_disableOverFlow","_setElementAttributes","calculatedValue","_resetElementAttributes","isOverflowing","_saveInitialAttribute","styleProperty","scrollbarWidth","_applyManipulationCallback","setProperty","actualValue","removeProperty","callBack","sel","EVENT_HIDE_PREVENTED","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","CLASS_NAME_OPEN","CLASS_NAME_STATIC","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_scrollBar","_adjustDialog","_showElement","_hideModal","handleUpdate","modalBody","transitionComplete","_triggerBackdropTransition","event2","_resetAdjustments","isModalOverflowing","initialOverflowY","isBodyOverflowing","paddingLeft","paddingRight","showEvent","alreadyOpen","CLASS_NAME_SHOWING","CLASS_NAME_HIDING","OPEN_SELECTOR","Offcanvas","blur","completeCallback","DefaultAllowlist","area","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","uriAttributes","SAFE_URL_PATTERN","allowedAttribute","allowedAttributeList","attributeName","nodeValue","attributeRegex","regex","allowList","content","extraClass","sanitize","sanitizeFn","template","DefaultContentType","entry","TemplateFactory","getContent","_resolvePossibleFunction","hasContent","changeContent","_checkContent","toHtml","templateWrapper","innerHTML","_maybeSanitize","text","_setContent","arg","templateElement","_putElementInTemplate","textContent","unsafeHtml","sanitizeFunction","createdDocument","DOMParser","parseFromString","elementName","attributeList","allowedAttributes","sanitizeHtml","DISALLOWED_ATTRIBUTES","CLASS_NAME_FADE","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","animation","container","customClass","delay","title","Tooltip","_isEnabled","_timeout","_isHovered","_activeTrigger","_templateFactory","_newContent","tip","_setListeners","_fixTitle","enable","disable","toggleEnabled","click","_leave","_enter","_hideModalHandler","_disposePopper","_isWithContent","isInTheDom","_getTipElement","_isWithActiveTrigger","_getTitle","_createTipElement","_getContentForTemplate","_getTemplateFactory","tipId","prefix","floor","random","getElementById","getUID","setContent","_initializeOnDelegatedTarget","_getDelegateConfig","attachment","triggers","eventIn","eventOut","_setTimeout","timeout","dataAttributes","dataAttribute","Popover","_getContent","EVENT_ACTIVATE","EVENT_CLICK","SELECTOR_TARGET_LINKS","SELECTOR_NAV_LINKS","SELECTOR_LINK_ITEMS","rootMargin","smoothScroll","threshold","ScrollSpy","_targetLinks","_observableSections","_rootElement","_activeTarget","_observer","_previousScrollData","visibleEntryTop","parentScrollTop","refresh","_initializeTargetsAndObservables","_maybeEnableSmoothScroll","disconnect","_getNewObserver","section","observe","observableSection","scrollTo","behavior","IntersectionObserver","_observerCallback","targetElement","_process","userScrollsDown","isIntersecting","_clearActiveClass","entryIsLowerThanPrevious","targetLinks","anchor","decodeURI","_activateParents","listGroup","activeNodes","spy","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","HOME_KEY","END_KEY","NOT_SELECTOR_DROPDOWN_TOGGLE","SELECTOR_INNER_ELEM","SELECTOR_DATA_TOGGLE_ACTIVE","Tab","_setInitialAttributes","_getChildren","innerElem","_elemIsActive","active","_getActiveElem","hideEvent","_deactivate","_activate","relatedElem","_toggleDropDown","nextActiveElement","preventScroll","_setAttributeIfNotExists","_setInitialAttributesOnChild","_getInnerElement","isActive","outerElem","_getOuterElement","_setInitialAttributesOnTargetPanel","open","EVENT_MOUSEOVER","EVENT_MOUSEOUT","EVENT_FOCUSOUT","CLASS_NAME_HIDE","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","isShown","_onInteraction","isInteracting"],"sources":["../../js/src/dom/data.js","../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/manipulator.js","../../js/src/util/config.js","../../js/src/base-component.js","../../js/src/dom/selector-engine.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/util/swipe.js","../../js/src/carousel.js","../../js/src/collapse.js","../../node_modules/@popperjs/core/lib/enums.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js","../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","../../node_modules/@popperjs/core/lib/modifiers/applyStyles.js","../../node_modules/@popperjs/core/lib/utils/getBasePlacement.js","../../node_modules/@popperjs/core/lib/utils/math.js","../../node_modules/@popperjs/core/lib/utils/userAgent.js","../../node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","../../node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","../../node_modules/@popperjs/core/lib/dom-utils/contains.js","../../node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","../../node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","../../node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","../../node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","../../node_modules/@popperjs/core/lib/utils/within.js","../../node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","../../node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","../../node_modules/@popperjs/core/lib/utils/expandToHashMap.js","../../node_modules/@popperjs/core/lib/modifiers/arrow.js","../../node_modules/@popperjs/core/lib/utils/getVariation.js","../../node_modules/@popperjs/core/lib/modifiers/computeStyles.js","../../node_modules/@popperjs/core/lib/modifiers/eventListeners.js","../../node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","../../node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","../../node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","../../node_modules/@popperjs/core/lib/utils/rectToClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","../../node_modules/@popperjs/core/lib/utils/computeOffsets.js","../../node_modules/@popperjs/core/lib/utils/detectOverflow.js","../../node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","../../node_modules/@popperjs/core/lib/modifiers/flip.js","../../node_modules/@popperjs/core/lib/modifiers/hide.js","../../node_modules/@popperjs/core/lib/modifiers/offset.js","../../node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","../../node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","../../node_modules/@popperjs/core/lib/utils/getAltAxis.js","../../node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","../../node_modules/@popperjs/core/lib/utils/orderModifiers.js","../../node_modules/@popperjs/core/lib/createPopper.js","../../node_modules/@popperjs/core/lib/utils/debounce.js","../../node_modules/@popperjs/core/lib/utils/mergeByName.js","../../node_modules/@popperjs/core/lib/popper-lite.js","../../node_modules/@popperjs/core/lib/popper.js","../../js/src/dropdown.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/util/scrollbar.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.js","../../js/src/util/template-factory.js","../../js/src/tooltip.js","../../js/src/popover.js","../../js/src/scrollspy.js","../../js/src/tab.js","../../js/src/toast.js","../../js/index.umd.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1_000_000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`)\n }\n\n return selector\n}\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`\n }\n\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false\n }\n\n if (typeof object.jquery !== 'undefined') {\n object = object[0]\n }\n\n return typeof object.nodeType !== 'undefined'\n}\n\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object\n }\n\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object))\n }\n\n return null\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])')\n\n if (!closedDetails) {\n return elementIsVisible\n }\n\n if (closedDetails !== element) {\n const summary = element.closest('summary')\n if (summary && summary.parentNode !== closedDetails) {\n return false\n }\n\n if (summary === null) {\n return false\n }\n }\n\n return elementIsVisible\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight // eslint-disable-line no-unused-expressions\n}\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback()\n }\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]\n }\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n defineJQueryPlugin,\n execute,\n executeAfterTransition,\n findShadowRoot,\n getElement,\n getjQuery,\n getNextActiveElement,\n getTransitionDurationFromElement,\n getUID,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n onDOMContentLoaded,\n parseSelector,\n reflow,\n triggerTransitionEnd,\n toType\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index.js'\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\n\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getElementEvents(element) {\n const uid = makeEventUid(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, { delegateTarget: element })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue\n }\n\n hydrateObj(event, { delegateTarget: target })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events)\n .find(event => event.callable === callable && event.delegationSelector === delegationSelector)\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string'\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : (handler || delegationFunction)\n let typeEvent = getTypeEvent(originalTypeEvent)\n\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent\n }\n\n return [isDelegated, callable, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n callable = wrapFunction(callable)\n }\n\n const events = getElementEvents(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)\n\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff\n\n return\n }\n\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = isDelegated ?\n bootstrapDelegationHandler(element, handler, callable) :\n bootstrapHandler(element, callable)\n\n fn.delegationSelector = isDelegated ? handler : null\n fn.callable = callable\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, isDelegated)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false)\n },\n\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getElementEvents(element)\n const storeElementEvent = events[typeEvent] || {}\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return\n }\n\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)\n return\n }\n\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n }\n }\n\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n\n let jQueryEvent = null\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value\n } catch {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value\n }\n })\n }\n }\n\n return obj\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport { isElement, toType } from './index.js'\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {}\n }\n\n static get DefaultType() {\n return {}\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n return config\n }\n\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n }\n }\n\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property]\n const valueType = isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n }\n }\n}\n\nexport default Config\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data.js'\nimport EventHandler from './dom/event-handler.js'\nimport Config from './util/config.js'\nimport { executeAfterTransition, getElement } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.1'\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super()\n\n element = getElement(element)\n if (!element) {\n return\n }\n\n this._element = element\n this._config = this._getConfig(config)\n\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null\n }\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`\n }\n}\n\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible, parseSelector } from '../util/index.js'\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`\n }\n\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null\n }\n\n return parseSelector(selector)\n}\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n let ancestor = element.parentNode.closest(selector)\n\n while (ancestor) {\n parents.push(ancestor)\n ancestor = ancestor.parentNode.closest(selector)\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n },\n\n getSelectorFromElement(element) {\n const selector = getSelector(element)\n\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null\n }\n\n return null\n },\n\n getElementFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.findOne(selector) : null\n },\n\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.find(selector) : []\n }\n}\n\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isDisabled } from './index.js'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'swipe'\nconst EVENT_KEY = '.bs.swipe'\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n}\n\nconst DefaultType = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n}\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super()\n this._element = element\n\n if (!element || !Swipe.isSupported()) {\n return\n }\n\n this._config = this._getConfig(config)\n this._deltaX = 0\n this._supportPointerEvents = Boolean(window.PointerEvent)\n this._initEvents()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY)\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX\n\n return\n }\n\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX\n }\n }\n\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX\n }\n\n this._handleSwipe()\n execute(this._config.endCallback)\n }\n\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this._deltaX\n }\n\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX)\n\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltaX / this._deltaX\n\n this._deltaX = 0\n\n if (!direction) {\n return\n }\n\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)\n }\n\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))\n }\n }\n\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n }\n}\n\nexport default Swipe\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getNextActiveElement,\n isRTL,\n isVisible,\n reflow,\n triggerTransitionEnd\n} from './util/index.js'\nimport Swipe from './util/swipe.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)', // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._interval = null\n this._activeElement = null\n this._isSliding = false\n this.touchTimeout = null\n this._swipeHelper = null\n\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._addEventListeners()\n\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element)\n }\n\n this._clearInterval()\n }\n\n cycle() {\n this._clearInterval()\n this._updateInterval()\n\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)\n }\n\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle())\n return\n }\n\n this.cycle()\n }\n\n to(index) {\n const items = this._getItems()\n if (index > items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n const activeIndex = this._getItemIndex(this._getActive())\n if (activeIndex === index) {\n return\n }\n\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV\n\n this._slide(order, items[index])\n }\n\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose()\n }\n\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())\n EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())\n }\n\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())\n }\n\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n }\n\n this._swipeHelper = new Swipe(this._element, swipeConfig)\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(this._directionToOrder(direction))\n }\n }\n\n _getItemIndex(element) {\n return this._getItems().indexOf(element)\n }\n\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return\n }\n\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement)\n\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)\n newActiveIndicator.setAttribute('aria-current', 'true')\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || this._getActive()\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n this._config.interval = elementInterval || this._config.defaultInterval\n }\n\n _slide(order, element = null) {\n if (this._isSliding) {\n return\n }\n\n const activeElement = this._getActive()\n const isNext = order === ORDER_NEXT\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)\n\n if (nextElement === activeElement) {\n return\n }\n\n const nextElementIndex = this._getItemIndex(nextElement)\n\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n })\n }\n\n const slideEvent = triggerEvent(EVENT_SLIDE)\n\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return\n }\n\n const isCycling = Boolean(this._interval)\n this.pause()\n\n this._isSliding = true\n\n this._setActiveIndicatorElement(nextElementIndex)\n this._activeElement = nextElement\n\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n triggerEvent(EVENT_SLID)\n }\n\n this._queueCallback(completeCallBack, activeElement, this._isAnimated())\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE)\n }\n\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n }\n\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element)\n }\n\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n }\n\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config)\n\n if (typeof config === 'number') {\n data.to(config)\n return\n }\n\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n event.preventDefault()\n\n const carousel = Carousel.getOrCreateInstance(target)\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n carousel.to(slideIndex)\n carousel._maybeEnableCycle()\n return\n }\n\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next()\n carousel._maybeEnableCycle()\n return\n }\n\n carousel.prev()\n carousel._maybeEnableCycle()\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getElement,\n reflow\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\nconst Default = {\n parent: null,\n toggle: true\n}\n\nconst DefaultType = {\n parent: '(null|element)',\n toggle: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isTransitioning = false\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElement => foundElement === this._element)\n\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let activeChildren = []\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)\n .filter(element => element !== this._element)\n .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))\n }\n\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n for (const activeInstance of activeChildren) {\n activeInstance.hide()\n }\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger)\n\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)\n\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n }\n }\n\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)\n element.setAttribute('aria-expanded', isOpen)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n execute,\n getElement,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center'\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)'\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR = '.navbar'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\nconst PLACEMENT_TOPCENTER = 'top'\nconst PLACEMENT_BOTTOMCENTER = 'bottom'\n\nconst Default = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n}\n\nconst DefaultType = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n}\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._popper = null\n this._parent = this._element.parentNode // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.findOne(SELECTOR_MENU, this._parent)\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._createPopper()\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = super._getConfig(config)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = this._parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n }\n\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getPlacement() {\n const parentDropdown = this._parent\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {\n return\n }\n\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)\n\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle)\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n const relatedTarget = { relatedTarget: context._element }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName)\n const isEscapeEvent = event.key === ESCAPE_KEY\n const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)\n\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return\n }\n\n if (isInput && !isEscapeEvent) {\n return\n }\n\n event.preventDefault()\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?\n this :\n (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))\n\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (isUpOrDownEvent) {\n event.stopPropagation()\n instance.show()\n instance._selectMenuItem(event)\n return\n }\n\n if (instance._isShown()) { // else is escape and we check if it is shown\n event.stopPropagation()\n instance.hide()\n getToggleButton.focus()\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute, executeAfterTransition, getElement, reflow } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nconst Default = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n}\n\nconst DefaultType = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n}\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n const element = this._getElement()\n if (this._config.isAnimated) {\n reflow(element)\n }\n\n element.classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n const element = this._getElement()\n this._config.rootElement.append(element)\n\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin,\n isDisabled,\n isVisible\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\nconst CLASS_NAME_HIDING = 'hiding'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate()\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.add(CLASS_NAME_HIDING)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n return\n }\n\n this.hide()\n }\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop)\n\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n })\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show()\n }\n})\n\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide()\n }\n }\n})\n\nenableDismissTrigger(Offcanvas)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n// js-docs-end allow-list\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n .some(regex => regex.test(attributeName))\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\nimport { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'\nimport { execute, getElement, isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'TemplateFactory'\n\nconst Default = {\n allowList: DefaultAllowlist,\n content: {}, // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n}\n\nconst DefaultType = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n}\n\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n}\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content)\n .map(config => this._resolvePossibleFunction(config))\n .filter(Boolean)\n }\n\n hasContent() {\n return this.getContent().length > 0\n }\n\n changeContent(content) {\n this._checkContent(content)\n this._config.content = { ...this._config.content, ...content }\n return this\n }\n\n toHtml() {\n const templateWrapper = document.createElement('div')\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template)\n\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector)\n }\n\n const template = templateWrapper.children[0]\n const extraClass = this._resolvePossibleFunction(this._config.extraClass)\n\n if (extraClass) {\n template.classList.add(...extraClass.split(' '))\n }\n\n return template\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config)\n this._checkContent(config.content)\n }\n\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({ selector, entry: content }, DefaultContentType)\n }\n }\n\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!templateElement) {\n return\n }\n\n content = this._resolvePossibleFunction(content)\n\n if (!content) {\n templateElement.remove()\n return\n }\n\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement)\n return\n }\n\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content)\n return\n }\n\n templateElement.textContent = content\n }\n\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this])\n }\n\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = ''\n templateElement.append(element)\n return\n }\n\n templateElement.textContent = element.textContent\n }\n}\n\nexport default TemplateFactory\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index.js'\nimport { DefaultAllowlist } from './util/sanitizer.js'\nimport TemplateFactory from './util/template-factory.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\nconst EVENT_HIDE = 'hide'\nconst EVENT_HIDDEN = 'hidden'\nconst EVENT_SHOW = 'show'\nconst EVENT_SHOWN = 'shown'\nconst EVENT_INSERTED = 'inserted'\nconst EVENT_CLICK = 'click'\nconst EVENT_FOCUSIN = 'focusin'\nconst EVENT_FOCUSOUT = 'focusout'\nconst EVENT_MOUSEENTER = 'mouseenter'\nconst EVENT_MOUSELEAVE = 'mouseleave'\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' +\n '
' +\n '
' +\n '
',\n title: '',\n trigger: 'hover focus'\n}\n\nconst DefaultType = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n}\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)')\n }\n\n super(element, config)\n\n // Private\n this._isEnabled = true\n this._timeout = 0\n this._isHovered = null\n this._activeTrigger = {}\n this._popper = null\n this._templateFactory = null\n this._newContent = null\n\n // Protected\n this.tip = null\n\n this._setListeners()\n\n if (!this._config.selector) {\n this._fixTitle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle() {\n if (!this._isEnabled) {\n return\n }\n\n this._activeTrigger.click = !this._activeTrigger.click\n if (this._isShown()) {\n this._leave()\n return\n }\n\n this._enter()\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this._isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper()\n\n const tip = this._getTipElement()\n\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'))\n\n const { container } = this._config\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))\n }\n\n this._popper = this._createPopper(tip)\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))\n\n if (this._isHovered === false) {\n this._leave()\n }\n\n this._isHovered = false\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n hide() {\n if (!this._isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const tip = this._getTipElement()\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n this._isHovered = null // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (!this._isHovered) {\n this._disposePopper()\n }\n\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n update() {\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle())\n }\n\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())\n }\n\n return this.tip\n }\n\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml()\n\n // TODO: remove this check in v6\n if (!tip) {\n return null\n }\n\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`)\n\n const tipId = getUID(this.constructor.NAME).toString()\n\n tip.setAttribute('id', tipId)\n\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n return tip\n }\n\n setContent(content) {\n this._newContent = content\n if (this._isShown()) {\n this._disposePopper()\n this.show()\n }\n }\n\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content)\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n })\n }\n\n return this._templateFactory\n }\n\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n }\n }\n\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _isAnimated() {\n return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))\n }\n\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)\n }\n\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element])\n const attachment = AttachmentMap[placement.toUpperCase()]\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element])\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement)\n }\n }\n ]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context.toggle()\n })\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSEENTER) :\n this.constructor.eventName(EVENT_FOCUSIN)\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSELEAVE) :\n this.constructor.eventName(EVENT_FOCUSOUT)\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true\n context._enter()\n })\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =\n context._element.contains(event.relatedTarget)\n\n context._leave()\n })\n }\n }\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n\n if (!title) {\n return\n }\n\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title')\n }\n\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true\n return\n }\n\n this._isHovered = true\n\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show()\n }\n }, this._config.delay.show)\n }\n\n _leave() {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n this._isHovered = false\n\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide()\n }\n }, this._config.delay.hide)\n }\n\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout)\n this._timeout = setTimeout(handler, timeout)\n }\n\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true)\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute]\n }\n }\n\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value\n }\n }\n\n config.selector = false\n config.trigger = 'manual'\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n\n if (this.tip) {\n this.tip.remove()\n this.tip = null\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Tooltip from './tooltip.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'popover'\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\nconst Default = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' +\n '
' +\n '

' +\n '
' +\n '
',\n trigger: 'click'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n}\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent()\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n }\n }\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_CLICK = `click${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_TARGET_LINKS = '[href]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst Default = {\n offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n}\n\nconst DefaultType = {\n offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n}\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map()\n this._observableSections = new Map()\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element\n this._activeTarget = null\n this._observer = null\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n }\n this.refresh() // initialize\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables()\n this._maybeEnableSmoothScroll()\n\n if (this._observer) {\n this._observer.disconnect()\n } else {\n this._observer = this._getNewObserver()\n }\n\n for (const section of this._observableSections.values()) {\n this._observer.observe(section)\n }\n }\n\n dispose() {\n this._observer.disconnect()\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin\n\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))\n }\n\n return config\n }\n\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK)\n\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash)\n if (observableSection) {\n event.preventDefault()\n const root = this._rootElement || window\n const height = observableSection.offsetTop - this._element.offsetTop\n if (root.scrollTo) {\n root.scrollTo({ top: height, behavior: 'smooth' })\n return\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height\n }\n })\n }\n\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n }\n\n return new IntersectionObserver(entries => this._observerCallback(entries), options)\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop\n this._process(targetElement(entry))\n }\n\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop\n this._previousScrollData.parentScrollTop = parentScrollTop\n\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null\n this._clearActiveClass(targetElement(entry))\n\n continue\n }\n\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry)\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return\n }\n\n continue\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry)\n }\n }\n }\n\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map()\n this._observableSections = new Map()\n\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)\n\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue\n }\n\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor)\n this._observableSections.set(anchor.hash, observableSection)\n }\n }\n }\n\n _process(target) {\n if (this._activeTarget === target) {\n return\n }\n\n this._clearActiveClass(this._config.target)\n this._activeTarget = target\n target.classList.add(CLASS_NAME_ACTIVE)\n this._activateParents(target)\n\n EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })\n }\n\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n return\n }\n\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both
    and
')},createChildNavList:function(e){var t=this.createNavList();return e.append(t),t},generateNavEl:function(e,t){var n=a('
');n.attr("href","#"+e),n.text(t);var r=a("
  • ");return r.append(n),r},generateNavItem:function(e){var t=this.generateAnchor(e),n=a(e),r=n.data("toc-text")||n.text();return this.generateNavEl(t,r)},getTopLevel:function(e){for(var t=1;t<=6;t++){if(1 + + + + + + + + + + + + diff --git a/docs/deps/font-awesome-6.4.2/css/all.css b/docs/deps/font-awesome-6.4.2/css/all.css new file mode 100644 index 0000000..bdb6e3a --- /dev/null +++ b/docs/deps/font-awesome-6.4.2/css/all.css @@ -0,0 +1,7968 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa { + font-family: var(--fa-style-family, "Font Awesome 6 Free"); + font-weight: var(--fa-style, 900); } + +.fa, +.fa-classic, +.fa-sharp, +.fas, +.fa-solid, +.far, +.fa-regular, +.fab, +.fa-brands { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: var(--fa-display, inline-block); + font-style: normal; + font-variant: normal; + line-height: 1; + text-rendering: auto; } + +.fas, +.fa-classic, +.fa-solid, +.far, +.fa-regular { + font-family: 'Font Awesome 6 Free'; } + +.fab, +.fa-brands { + font-family: 'Font Awesome 6 Brands'; } + +.fa-1x { + font-size: 1em; } + +.fa-2x { + font-size: 2em; } + +.fa-3x { + font-size: 3em; } + +.fa-4x { + font-size: 4em; } + +.fa-5x { + font-size: 5em; } + +.fa-6x { + font-size: 6em; } + +.fa-7x { + font-size: 7em; } + +.fa-8x { + font-size: 8em; } + +.fa-9x { + font-size: 9em; } + +.fa-10x { + font-size: 10em; } + +.fa-2xs { + font-size: 0.625em; + line-height: 0.1em; + vertical-align: 0.225em; } + +.fa-xs { + font-size: 0.75em; + line-height: 0.08333em; + vertical-align: 0.125em; } + +.fa-sm { + font-size: 0.875em; + line-height: 0.07143em; + vertical-align: 0.05357em; } + +.fa-lg { + font-size: 1.25em; + line-height: 0.05em; + vertical-align: -0.075em; } + +.fa-xl { + font-size: 1.5em; + line-height: 0.04167em; + vertical-align: -0.125em; } + +.fa-2xl { + font-size: 2em; + line-height: 0.03125em; + vertical-align: -0.1875em; } + +.fa-fw { + text-align: center; + width: 1.25em; } + +.fa-ul { + list-style-type: none; + margin-left: var(--fa-li-margin, 2.5em); + padding-left: 0; } + .fa-ul > li { + position: relative; } + +.fa-li { + left: calc(var(--fa-li-width, 2em) * -1); + position: absolute; + text-align: center; + width: var(--fa-li-width, 2em); + line-height: inherit; } + +.fa-border { + border-color: var(--fa-border-color, #eee); + border-radius: var(--fa-border-radius, 0.1em); + border-style: var(--fa-border-style, solid); + border-width: var(--fa-border-width, 0.08em); + padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } + +.fa-pull-left { + float: left; + margin-right: var(--fa-pull-margin, 0.3em); } + +.fa-pull-right { + float: right; + margin-left: var(--fa-pull-margin, 0.3em); } + +.fa-beat { + -webkit-animation-name: fa-beat; + animation-name: fa-beat; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); } + +.fa-bounce { + -webkit-animation-name: fa-bounce; + animation-name: fa-bounce; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } + +.fa-fade { + -webkit-animation-name: fa-fade; + animation-name: fa-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } + +.fa-beat-fade { + -webkit-animation-name: fa-beat-fade; + animation-name: fa-beat-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } + +.fa-flip { + -webkit-animation-name: fa-flip; + animation-name: fa-flip; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); } + +.fa-shake { + -webkit-animation-name: fa-shake; + animation-name: fa-shake; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); } + +.fa-spin { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 2s); + animation-duration: var(--fa-animation-duration, 2s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); } + +.fa-spin-reverse { + --fa-animation-direction: reverse; } + +.fa-pulse, +.fa-spin-pulse { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); + animation-timing-function: var(--fa-animation-timing, steps(8)); } + +@media (prefers-reduced-motion: reduce) { + .fa-beat, + .fa-bounce, + .fa-fade, + .fa-beat-fade, + .fa-flip, + .fa-pulse, + .fa-shake, + .fa-spin, + .fa-spin-pulse { + -webkit-animation-delay: -1ms; + animation-delay: -1ms; + -webkit-animation-duration: 1ms; + animation-duration: 1ms; + -webkit-animation-iteration-count: 1; + animation-iteration-count: 1; + -webkit-transition-delay: 0s; + transition-delay: 0s; + -webkit-transition-duration: 0s; + transition-duration: 0s; } } + +@-webkit-keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); } } + +@keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); } } + +@-webkit-keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } } + +@keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); } } + +@-webkit-keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); } } + +@keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); } } + +@-webkit-keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); } } + +@keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); } } + +@-webkit-keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } + +@keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } + +@-webkit-keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } } + +@keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } } + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +.fa-rotate-90 { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + +.fa-rotate-180 { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + +.fa-rotate-270 { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + +.fa-flip-horizontal { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); } + +.fa-flip-vertical { + -webkit-transform: scale(1, -1); + transform: scale(1, -1); } + +.fa-flip-both, +.fa-flip-horizontal.fa-flip-vertical { + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); } + +.fa-rotate-by { + -webkit-transform: rotate(var(--fa-rotate-angle, none)); + transform: rotate(var(--fa-rotate-angle, none)); } + +.fa-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2.5em; } + +.fa-stack-1x, +.fa-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; + z-index: var(--fa-stack-z-index, auto); } + +.fa-stack-1x { + line-height: inherit; } + +.fa-stack-2x { + font-size: 2em; } + +.fa-inverse { + color: var(--fa-inverse, #fff); } + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +.fa-0::before { + content: "\30"; } + +.fa-1::before { + content: "\31"; } + +.fa-2::before { + content: "\32"; } + +.fa-3::before { + content: "\33"; } + +.fa-4::before { + content: "\34"; } + +.fa-5::before { + content: "\35"; } + +.fa-6::before { + content: "\36"; } + +.fa-7::before { + content: "\37"; } + +.fa-8::before { + content: "\38"; } + +.fa-9::before { + content: "\39"; } + +.fa-fill-drip::before { + content: "\f576"; } + +.fa-arrows-to-circle::before { + content: "\e4bd"; } + +.fa-circle-chevron-right::before { + content: "\f138"; } + +.fa-chevron-circle-right::before { + content: "\f138"; } + +.fa-at::before { + content: "\40"; } + +.fa-trash-can::before { + content: "\f2ed"; } + +.fa-trash-alt::before { + content: "\f2ed"; } + +.fa-text-height::before { + content: "\f034"; } + +.fa-user-xmark::before { + content: "\f235"; } + +.fa-user-times::before { + content: "\f235"; } + +.fa-stethoscope::before { + content: "\f0f1"; } + +.fa-message::before { + content: "\f27a"; } + +.fa-comment-alt::before { + content: "\f27a"; } + +.fa-info::before { + content: "\f129"; } + +.fa-down-left-and-up-right-to-center::before { + content: "\f422"; } + +.fa-compress-alt::before { + content: "\f422"; } + +.fa-explosion::before { + content: "\e4e9"; } + +.fa-file-lines::before { + content: "\f15c"; } + +.fa-file-alt::before { + content: "\f15c"; } + +.fa-file-text::before { + content: "\f15c"; } + +.fa-wave-square::before { + content: "\f83e"; } + +.fa-ring::before { + content: "\f70b"; } + +.fa-building-un::before { + content: "\e4d9"; } + +.fa-dice-three::before { + content: "\f527"; } + +.fa-calendar-days::before { + content: "\f073"; } + +.fa-calendar-alt::before { + content: "\f073"; } + +.fa-anchor-circle-check::before { + content: "\e4aa"; } + +.fa-building-circle-arrow-right::before { + content: "\e4d1"; } + +.fa-volleyball::before { + content: "\f45f"; } + +.fa-volleyball-ball::before { + content: "\f45f"; } + +.fa-arrows-up-to-line::before { + content: "\e4c2"; } + +.fa-sort-down::before { + content: "\f0dd"; } + +.fa-sort-desc::before { + content: "\f0dd"; } + +.fa-circle-minus::before { + content: "\f056"; } + +.fa-minus-circle::before { + content: "\f056"; } + +.fa-door-open::before { + content: "\f52b"; } + +.fa-right-from-bracket::before { + content: "\f2f5"; } + +.fa-sign-out-alt::before { + content: "\f2f5"; } + +.fa-atom::before { + content: "\f5d2"; } + +.fa-soap::before { + content: "\e06e"; } + +.fa-icons::before { + content: "\f86d"; } + +.fa-heart-music-camera-bolt::before { + content: "\f86d"; } + +.fa-microphone-lines-slash::before { + content: "\f539"; } + +.fa-microphone-alt-slash::before { + content: "\f539"; } + +.fa-bridge-circle-check::before { + content: "\e4c9"; } + +.fa-pump-medical::before { + content: "\e06a"; } + +.fa-fingerprint::before { + content: "\f577"; } + +.fa-hand-point-right::before { + content: "\f0a4"; } + +.fa-magnifying-glass-location::before { + content: "\f689"; } + +.fa-search-location::before { + content: "\f689"; } + +.fa-forward-step::before { + content: "\f051"; } + +.fa-step-forward::before { + content: "\f051"; } + +.fa-face-smile-beam::before { + content: "\f5b8"; } + +.fa-smile-beam::before { + content: "\f5b8"; } + +.fa-flag-checkered::before { + content: "\f11e"; } + +.fa-football::before { + content: "\f44e"; } + +.fa-football-ball::before { + content: "\f44e"; } + +.fa-school-circle-exclamation::before { + content: "\e56c"; } + +.fa-crop::before { + content: "\f125"; } + +.fa-angles-down::before { + content: "\f103"; } + +.fa-angle-double-down::before { + content: "\f103"; } + +.fa-users-rectangle::before { + content: "\e594"; } + +.fa-people-roof::before { + content: "\e537"; } + +.fa-people-line::before { + content: "\e534"; } + +.fa-beer-mug-empty::before { + content: "\f0fc"; } + +.fa-beer::before { + content: "\f0fc"; } + +.fa-diagram-predecessor::before { + content: "\e477"; } + +.fa-arrow-up-long::before { + content: "\f176"; } + +.fa-long-arrow-up::before { + content: "\f176"; } + +.fa-fire-flame-simple::before { + content: "\f46a"; } + +.fa-burn::before { + content: "\f46a"; } + +.fa-person::before { + content: "\f183"; } + +.fa-male::before { + content: "\f183"; } + +.fa-laptop::before { + content: "\f109"; } + +.fa-file-csv::before { + content: "\f6dd"; } + +.fa-menorah::before { + content: "\f676"; } + +.fa-truck-plane::before { + content: "\e58f"; } + +.fa-record-vinyl::before { + content: "\f8d9"; } + +.fa-face-grin-stars::before { + content: "\f587"; } + +.fa-grin-stars::before { + content: "\f587"; } + +.fa-bong::before { + content: "\f55c"; } + +.fa-spaghetti-monster-flying::before { + content: "\f67b"; } + +.fa-pastafarianism::before { + content: "\f67b"; } + +.fa-arrow-down-up-across-line::before { + content: "\e4af"; } + +.fa-spoon::before { + content: "\f2e5"; } + +.fa-utensil-spoon::before { + content: "\f2e5"; } + +.fa-jar-wheat::before { + content: "\e517"; } + +.fa-envelopes-bulk::before { + content: "\f674"; } + +.fa-mail-bulk::before { + content: "\f674"; } + +.fa-file-circle-exclamation::before { + content: "\e4eb"; } + +.fa-circle-h::before { + content: "\f47e"; } + +.fa-hospital-symbol::before { + content: "\f47e"; } + +.fa-pager::before { + content: "\f815"; } + +.fa-address-book::before { + content: "\f2b9"; } + +.fa-contact-book::before { + content: "\f2b9"; } + +.fa-strikethrough::before { + content: "\f0cc"; } + +.fa-k::before { + content: "\4b"; } + +.fa-landmark-flag::before { + content: "\e51c"; } + +.fa-pencil::before { + content: "\f303"; } + +.fa-pencil-alt::before { + content: "\f303"; } + +.fa-backward::before { + content: "\f04a"; } + +.fa-caret-right::before { + content: "\f0da"; } + +.fa-comments::before { + content: "\f086"; } + +.fa-paste::before { + content: "\f0ea"; } + +.fa-file-clipboard::before { + content: "\f0ea"; } + +.fa-code-pull-request::before { + content: "\e13c"; } + +.fa-clipboard-list::before { + content: "\f46d"; } + +.fa-truck-ramp-box::before { + content: "\f4de"; } + +.fa-truck-loading::before { + content: "\f4de"; } + +.fa-user-check::before { + content: "\f4fc"; } + +.fa-vial-virus::before { + content: "\e597"; } + +.fa-sheet-plastic::before { + content: "\e571"; } + +.fa-blog::before { + content: "\f781"; } + +.fa-user-ninja::before { + content: "\f504"; } + +.fa-person-arrow-up-from-line::before { + content: "\e539"; } + +.fa-scroll-torah::before { + content: "\f6a0"; } + +.fa-torah::before { + content: "\f6a0"; } + +.fa-broom-ball::before { + content: "\f458"; } + +.fa-quidditch::before { + content: "\f458"; } + +.fa-quidditch-broom-ball::before { + content: "\f458"; } + +.fa-toggle-off::before { + content: "\f204"; } + +.fa-box-archive::before { + content: "\f187"; } + +.fa-archive::before { + content: "\f187"; } + +.fa-person-drowning::before { + content: "\e545"; } + +.fa-arrow-down-9-1::before { + content: "\f886"; } + +.fa-sort-numeric-desc::before { + content: "\f886"; } + +.fa-sort-numeric-down-alt::before { + content: "\f886"; } + +.fa-face-grin-tongue-squint::before { + content: "\f58a"; } + +.fa-grin-tongue-squint::before { + content: "\f58a"; } + +.fa-spray-can::before { + content: "\f5bd"; } + +.fa-truck-monster::before { + content: "\f63b"; } + +.fa-w::before { + content: "\57"; } + +.fa-earth-africa::before { + content: "\f57c"; } + +.fa-globe-africa::before { + content: "\f57c"; } + +.fa-rainbow::before { + content: "\f75b"; } + +.fa-circle-notch::before { + content: "\f1ce"; } + +.fa-tablet-screen-button::before { + content: "\f3fa"; } + +.fa-tablet-alt::before { + content: "\f3fa"; } + +.fa-paw::before { + content: "\f1b0"; } + +.fa-cloud::before { + content: "\f0c2"; } + +.fa-trowel-bricks::before { + content: "\e58a"; } + +.fa-face-flushed::before { + content: "\f579"; } + +.fa-flushed::before { + content: "\f579"; } + +.fa-hospital-user::before { + content: "\f80d"; } + +.fa-tent-arrow-left-right::before { + content: "\e57f"; } + +.fa-gavel::before { + content: "\f0e3"; } + +.fa-legal::before { + content: "\f0e3"; } + +.fa-binoculars::before { + content: "\f1e5"; } + +.fa-microphone-slash::before { + content: "\f131"; } + +.fa-box-tissue::before { + content: "\e05b"; } + +.fa-motorcycle::before { + content: "\f21c"; } + +.fa-bell-concierge::before { + content: "\f562"; } + +.fa-concierge-bell::before { + content: "\f562"; } + +.fa-pen-ruler::before { + content: "\f5ae"; } + +.fa-pencil-ruler::before { + content: "\f5ae"; } + +.fa-people-arrows::before { + content: "\e068"; } + +.fa-people-arrows-left-right::before { + content: "\e068"; } + +.fa-mars-and-venus-burst::before { + content: "\e523"; } + +.fa-square-caret-right::before { + content: "\f152"; } + +.fa-caret-square-right::before { + content: "\f152"; } + +.fa-scissors::before { + content: "\f0c4"; } + +.fa-cut::before { + content: "\f0c4"; } + +.fa-sun-plant-wilt::before { + content: "\e57a"; } + +.fa-toilets-portable::before { + content: "\e584"; } + +.fa-hockey-puck::before { + content: "\f453"; } + +.fa-table::before { + content: "\f0ce"; } + +.fa-magnifying-glass-arrow-right::before { + content: "\e521"; } + +.fa-tachograph-digital::before { + content: "\f566"; } + +.fa-digital-tachograph::before { + content: "\f566"; } + +.fa-users-slash::before { + content: "\e073"; } + +.fa-clover::before { + content: "\e139"; } + +.fa-reply::before { + content: "\f3e5"; } + +.fa-mail-reply::before { + content: "\f3e5"; } + +.fa-star-and-crescent::before { + content: "\f699"; } + +.fa-house-fire::before { + content: "\e50c"; } + +.fa-square-minus::before { + content: "\f146"; } + +.fa-minus-square::before { + content: "\f146"; } + +.fa-helicopter::before { + content: "\f533"; } + +.fa-compass::before { + content: "\f14e"; } + +.fa-square-caret-down::before { + content: "\f150"; } + +.fa-caret-square-down::before { + content: "\f150"; } + +.fa-file-circle-question::before { + content: "\e4ef"; } + +.fa-laptop-code::before { + content: "\f5fc"; } + +.fa-swatchbook::before { + content: "\f5c3"; } + +.fa-prescription-bottle::before { + content: "\f485"; } + +.fa-bars::before { + content: "\f0c9"; } + +.fa-navicon::before { + content: "\f0c9"; } + +.fa-people-group::before { + content: "\e533"; } + +.fa-hourglass-end::before { + content: "\f253"; } + +.fa-hourglass-3::before { + content: "\f253"; } + +.fa-heart-crack::before { + content: "\f7a9"; } + +.fa-heart-broken::before { + content: "\f7a9"; } + +.fa-square-up-right::before { + content: "\f360"; } + +.fa-external-link-square-alt::before { + content: "\f360"; } + +.fa-face-kiss-beam::before { + content: "\f597"; } + +.fa-kiss-beam::before { + content: "\f597"; } + +.fa-film::before { + content: "\f008"; } + +.fa-ruler-horizontal::before { + content: "\f547"; } + +.fa-people-robbery::before { + content: "\e536"; } + +.fa-lightbulb::before { + content: "\f0eb"; } + +.fa-caret-left::before { + content: "\f0d9"; } + +.fa-circle-exclamation::before { + content: "\f06a"; } + +.fa-exclamation-circle::before { + content: "\f06a"; } + +.fa-school-circle-xmark::before { + content: "\e56d"; } + +.fa-arrow-right-from-bracket::before { + content: "\f08b"; } + +.fa-sign-out::before { + content: "\f08b"; } + +.fa-circle-chevron-down::before { + content: "\f13a"; } + +.fa-chevron-circle-down::before { + content: "\f13a"; } + +.fa-unlock-keyhole::before { + content: "\f13e"; } + +.fa-unlock-alt::before { + content: "\f13e"; } + +.fa-cloud-showers-heavy::before { + content: "\f740"; } + +.fa-headphones-simple::before { + content: "\f58f"; } + +.fa-headphones-alt::before { + content: "\f58f"; } + +.fa-sitemap::before { + content: "\f0e8"; } + +.fa-circle-dollar-to-slot::before { + content: "\f4b9"; } + +.fa-donate::before { + content: "\f4b9"; } + +.fa-memory::before { + content: "\f538"; } + +.fa-road-spikes::before { + content: "\e568"; } + +.fa-fire-burner::before { + content: "\e4f1"; } + +.fa-flag::before { + content: "\f024"; } + +.fa-hanukiah::before { + content: "\f6e6"; } + +.fa-feather::before { + content: "\f52d"; } + +.fa-volume-low::before { + content: "\f027"; } + +.fa-volume-down::before { + content: "\f027"; } + +.fa-comment-slash::before { + content: "\f4b3"; } + +.fa-cloud-sun-rain::before { + content: "\f743"; } + +.fa-compress::before { + content: "\f066"; } + +.fa-wheat-awn::before { + content: "\e2cd"; } + +.fa-wheat-alt::before { + content: "\e2cd"; } + +.fa-ankh::before { + content: "\f644"; } + +.fa-hands-holding-child::before { + content: "\e4fa"; } + +.fa-asterisk::before { + content: "\2a"; } + +.fa-square-check::before { + content: "\f14a"; } + +.fa-check-square::before { + content: "\f14a"; } + +.fa-peseta-sign::before { + content: "\e221"; } + +.fa-heading::before { + content: "\f1dc"; } + +.fa-header::before { + content: "\f1dc"; } + +.fa-ghost::before { + content: "\f6e2"; } + +.fa-list::before { + content: "\f03a"; } + +.fa-list-squares::before { + content: "\f03a"; } + +.fa-square-phone-flip::before { + content: "\f87b"; } + +.fa-phone-square-alt::before { + content: "\f87b"; } + +.fa-cart-plus::before { + content: "\f217"; } + +.fa-gamepad::before { + content: "\f11b"; } + +.fa-circle-dot::before { + content: "\f192"; } + +.fa-dot-circle::before { + content: "\f192"; } + +.fa-face-dizzy::before { + content: "\f567"; } + +.fa-dizzy::before { + content: "\f567"; } + +.fa-egg::before { + content: "\f7fb"; } + +.fa-house-medical-circle-xmark::before { + content: "\e513"; } + +.fa-campground::before { + content: "\f6bb"; } + +.fa-folder-plus::before { + content: "\f65e"; } + +.fa-futbol::before { + content: "\f1e3"; } + +.fa-futbol-ball::before { + content: "\f1e3"; } + +.fa-soccer-ball::before { + content: "\f1e3"; } + +.fa-paintbrush::before { + content: "\f1fc"; } + +.fa-paint-brush::before { + content: "\f1fc"; } + +.fa-lock::before { + content: "\f023"; } + +.fa-gas-pump::before { + content: "\f52f"; } + +.fa-hot-tub-person::before { + content: "\f593"; } + +.fa-hot-tub::before { + content: "\f593"; } + +.fa-map-location::before { + content: "\f59f"; } + +.fa-map-marked::before { + content: "\f59f"; } + +.fa-house-flood-water::before { + content: "\e50e"; } + +.fa-tree::before { + content: "\f1bb"; } + +.fa-bridge-lock::before { + content: "\e4cc"; } + +.fa-sack-dollar::before { + content: "\f81d"; } + +.fa-pen-to-square::before { + content: "\f044"; } + +.fa-edit::before { + content: "\f044"; } + +.fa-car-side::before { + content: "\f5e4"; } + +.fa-share-nodes::before { + content: "\f1e0"; } + +.fa-share-alt::before { + content: "\f1e0"; } + +.fa-heart-circle-minus::before { + content: "\e4ff"; } + +.fa-hourglass-half::before { + content: "\f252"; } + +.fa-hourglass-2::before { + content: "\f252"; } + +.fa-microscope::before { + content: "\f610"; } + +.fa-sink::before { + content: "\e06d"; } + +.fa-bag-shopping::before { + content: "\f290"; } + +.fa-shopping-bag::before { + content: "\f290"; } + +.fa-arrow-down-z-a::before { + content: "\f881"; } + +.fa-sort-alpha-desc::before { + content: "\f881"; } + +.fa-sort-alpha-down-alt::before { + content: "\f881"; } + +.fa-mitten::before { + content: "\f7b5"; } + +.fa-person-rays::before { + content: "\e54d"; } + +.fa-users::before { + content: "\f0c0"; } + +.fa-eye-slash::before { + content: "\f070"; } + +.fa-flask-vial::before { + content: "\e4f3"; } + +.fa-hand::before { + content: "\f256"; } + +.fa-hand-paper::before { + content: "\f256"; } + +.fa-om::before { + content: "\f679"; } + +.fa-worm::before { + content: "\e599"; } + +.fa-house-circle-xmark::before { + content: "\e50b"; } + +.fa-plug::before { + content: "\f1e6"; } + +.fa-chevron-up::before { + content: "\f077"; } + +.fa-hand-spock::before { + content: "\f259"; } + +.fa-stopwatch::before { + content: "\f2f2"; } + +.fa-face-kiss::before { + content: "\f596"; } + +.fa-kiss::before { + content: "\f596"; } + +.fa-bridge-circle-xmark::before { + content: "\e4cb"; } + +.fa-face-grin-tongue::before { + content: "\f589"; } + +.fa-grin-tongue::before { + content: "\f589"; } + +.fa-chess-bishop::before { + content: "\f43a"; } + +.fa-face-grin-wink::before { + content: "\f58c"; } + +.fa-grin-wink::before { + content: "\f58c"; } + +.fa-ear-deaf::before { + content: "\f2a4"; } + +.fa-deaf::before { + content: "\f2a4"; } + +.fa-deafness::before { + content: "\f2a4"; } + +.fa-hard-of-hearing::before { + content: "\f2a4"; } + +.fa-road-circle-check::before { + content: "\e564"; } + +.fa-dice-five::before { + content: "\f523"; } + +.fa-square-rss::before { + content: "\f143"; } + +.fa-rss-square::before { + content: "\f143"; } + +.fa-land-mine-on::before { + content: "\e51b"; } + +.fa-i-cursor::before { + content: "\f246"; } + +.fa-stamp::before { + content: "\f5bf"; } + +.fa-stairs::before { + content: "\e289"; } + +.fa-i::before { + content: "\49"; } + +.fa-hryvnia-sign::before { + content: "\f6f2"; } + +.fa-hryvnia::before { + content: "\f6f2"; } + +.fa-pills::before { + content: "\f484"; } + +.fa-face-grin-wide::before { + content: "\f581"; } + +.fa-grin-alt::before { + content: "\f581"; } + +.fa-tooth::before { + content: "\f5c9"; } + +.fa-v::before { + content: "\56"; } + +.fa-bangladeshi-taka-sign::before { + content: "\e2e6"; } + +.fa-bicycle::before { + content: "\f206"; } + +.fa-staff-snake::before { + content: "\e579"; } + +.fa-rod-asclepius::before { + content: "\e579"; } + +.fa-rod-snake::before { + content: "\e579"; } + +.fa-staff-aesculapius::before { + content: "\e579"; } + +.fa-head-side-cough-slash::before { + content: "\e062"; } + +.fa-truck-medical::before { + content: "\f0f9"; } + +.fa-ambulance::before { + content: "\f0f9"; } + +.fa-wheat-awn-circle-exclamation::before { + content: "\e598"; } + +.fa-snowman::before { + content: "\f7d0"; } + +.fa-mortar-pestle::before { + content: "\f5a7"; } + +.fa-road-barrier::before { + content: "\e562"; } + +.fa-school::before { + content: "\f549"; } + +.fa-igloo::before { + content: "\f7ae"; } + +.fa-joint::before { + content: "\f595"; } + +.fa-angle-right::before { + content: "\f105"; } + +.fa-horse::before { + content: "\f6f0"; } + +.fa-q::before { + content: "\51"; } + +.fa-g::before { + content: "\47"; } + +.fa-notes-medical::before { + content: "\f481"; } + +.fa-temperature-half::before { + content: "\f2c9"; } + +.fa-temperature-2::before { + content: "\f2c9"; } + +.fa-thermometer-2::before { + content: "\f2c9"; } + +.fa-thermometer-half::before { + content: "\f2c9"; } + +.fa-dong-sign::before { + content: "\e169"; } + +.fa-capsules::before { + content: "\f46b"; } + +.fa-poo-storm::before { + content: "\f75a"; } + +.fa-poo-bolt::before { + content: "\f75a"; } + +.fa-face-frown-open::before { + content: "\f57a"; } + +.fa-frown-open::before { + content: "\f57a"; } + +.fa-hand-point-up::before { + content: "\f0a6"; } + +.fa-money-bill::before { + content: "\f0d6"; } + +.fa-bookmark::before { + content: "\f02e"; } + +.fa-align-justify::before { + content: "\f039"; } + +.fa-umbrella-beach::before { + content: "\f5ca"; } + +.fa-helmet-un::before { + content: "\e503"; } + +.fa-bullseye::before { + content: "\f140"; } + +.fa-bacon::before { + content: "\f7e5"; } + +.fa-hand-point-down::before { + content: "\f0a7"; } + +.fa-arrow-up-from-bracket::before { + content: "\e09a"; } + +.fa-folder::before { + content: "\f07b"; } + +.fa-folder-blank::before { + content: "\f07b"; } + +.fa-file-waveform::before { + content: "\f478"; } + +.fa-file-medical-alt::before { + content: "\f478"; } + +.fa-radiation::before { + content: "\f7b9"; } + +.fa-chart-simple::before { + content: "\e473"; } + +.fa-mars-stroke::before { + content: "\f229"; } + +.fa-vial::before { + content: "\f492"; } + +.fa-gauge::before { + content: "\f624"; } + +.fa-dashboard::before { + content: "\f624"; } + +.fa-gauge-med::before { + content: "\f624"; } + +.fa-tachometer-alt-average::before { + content: "\f624"; } + +.fa-wand-magic-sparkles::before { + content: "\e2ca"; } + +.fa-magic-wand-sparkles::before { + content: "\e2ca"; } + +.fa-e::before { + content: "\45"; } + +.fa-pen-clip::before { + content: "\f305"; } + +.fa-pen-alt::before { + content: "\f305"; } + +.fa-bridge-circle-exclamation::before { + content: "\e4ca"; } + +.fa-user::before { + content: "\f007"; } + +.fa-school-circle-check::before { + content: "\e56b"; } + +.fa-dumpster::before { + content: "\f793"; } + +.fa-van-shuttle::before { + content: "\f5b6"; } + +.fa-shuttle-van::before { + content: "\f5b6"; } + +.fa-building-user::before { + content: "\e4da"; } + +.fa-square-caret-left::before { + content: "\f191"; } + +.fa-caret-square-left::before { + content: "\f191"; } + +.fa-highlighter::before { + content: "\f591"; } + +.fa-key::before { + content: "\f084"; } + +.fa-bullhorn::before { + content: "\f0a1"; } + +.fa-globe::before { + content: "\f0ac"; } + +.fa-synagogue::before { + content: "\f69b"; } + +.fa-person-half-dress::before { + content: "\e548"; } + +.fa-road-bridge::before { + content: "\e563"; } + +.fa-location-arrow::before { + content: "\f124"; } + +.fa-c::before { + content: "\43"; } + +.fa-tablet-button::before { + content: "\f10a"; } + +.fa-building-lock::before { + content: "\e4d6"; } + +.fa-pizza-slice::before { + content: "\f818"; } + +.fa-money-bill-wave::before { + content: "\f53a"; } + +.fa-chart-area::before { + content: "\f1fe"; } + +.fa-area-chart::before { + content: "\f1fe"; } + +.fa-house-flag::before { + content: "\e50d"; } + +.fa-person-circle-minus::before { + content: "\e540"; } + +.fa-ban::before { + content: "\f05e"; } + +.fa-cancel::before { + content: "\f05e"; } + +.fa-camera-rotate::before { + content: "\e0d8"; } + +.fa-spray-can-sparkles::before { + content: "\f5d0"; } + +.fa-air-freshener::before { + content: "\f5d0"; } + +.fa-star::before { + content: "\f005"; } + +.fa-repeat::before { + content: "\f363"; } + +.fa-cross::before { + content: "\f654"; } + +.fa-box::before { + content: "\f466"; } + +.fa-venus-mars::before { + content: "\f228"; } + +.fa-arrow-pointer::before { + content: "\f245"; } + +.fa-mouse-pointer::before { + content: "\f245"; } + +.fa-maximize::before { + content: "\f31e"; } + +.fa-expand-arrows-alt::before { + content: "\f31e"; } + +.fa-charging-station::before { + content: "\f5e7"; } + +.fa-shapes::before { + content: "\f61f"; } + +.fa-triangle-circle-square::before { + content: "\f61f"; } + +.fa-shuffle::before { + content: "\f074"; } + +.fa-random::before { + content: "\f074"; } + +.fa-person-running::before { + content: "\f70c"; } + +.fa-running::before { + content: "\f70c"; } + +.fa-mobile-retro::before { + content: "\e527"; } + +.fa-grip-lines-vertical::before { + content: "\f7a5"; } + +.fa-spider::before { + content: "\f717"; } + +.fa-hands-bound::before { + content: "\e4f9"; } + +.fa-file-invoice-dollar::before { + content: "\f571"; } + +.fa-plane-circle-exclamation::before { + content: "\e556"; } + +.fa-x-ray::before { + content: "\f497"; } + +.fa-spell-check::before { + content: "\f891"; } + +.fa-slash::before { + content: "\f715"; } + +.fa-computer-mouse::before { + content: "\f8cc"; } + +.fa-mouse::before { + content: "\f8cc"; } + +.fa-arrow-right-to-bracket::before { + content: "\f090"; } + +.fa-sign-in::before { + content: "\f090"; } + +.fa-shop-slash::before { + content: "\e070"; } + +.fa-store-alt-slash::before { + content: "\e070"; } + +.fa-server::before { + content: "\f233"; } + +.fa-virus-covid-slash::before { + content: "\e4a9"; } + +.fa-shop-lock::before { + content: "\e4a5"; } + +.fa-hourglass-start::before { + content: "\f251"; } + +.fa-hourglass-1::before { + content: "\f251"; } + +.fa-blender-phone::before { + content: "\f6b6"; } + +.fa-building-wheat::before { + content: "\e4db"; } + +.fa-person-breastfeeding::before { + content: "\e53a"; } + +.fa-right-to-bracket::before { + content: "\f2f6"; } + +.fa-sign-in-alt::before { + content: "\f2f6"; } + +.fa-venus::before { + content: "\f221"; } + +.fa-passport::before { + content: "\f5ab"; } + +.fa-heart-pulse::before { + content: "\f21e"; } + +.fa-heartbeat::before { + content: "\f21e"; } + +.fa-people-carry-box::before { + content: "\f4ce"; } + +.fa-people-carry::before { + content: "\f4ce"; } + +.fa-temperature-high::before { + content: "\f769"; } + +.fa-microchip::before { + content: "\f2db"; } + +.fa-crown::before { + content: "\f521"; } + +.fa-weight-hanging::before { + content: "\f5cd"; } + +.fa-xmarks-lines::before { + content: "\e59a"; } + +.fa-file-prescription::before { + content: "\f572"; } + +.fa-weight-scale::before { + content: "\f496"; } + +.fa-weight::before { + content: "\f496"; } + +.fa-user-group::before { + content: "\f500"; } + +.fa-user-friends::before { + content: "\f500"; } + +.fa-arrow-up-a-z::before { + content: "\f15e"; } + +.fa-sort-alpha-up::before { + content: "\f15e"; } + +.fa-chess-knight::before { + content: "\f441"; } + +.fa-face-laugh-squint::before { + content: "\f59b"; } + +.fa-laugh-squint::before { + content: "\f59b"; } + +.fa-wheelchair::before { + content: "\f193"; } + +.fa-circle-arrow-up::before { + content: "\f0aa"; } + +.fa-arrow-circle-up::before { + content: "\f0aa"; } + +.fa-toggle-on::before { + content: "\f205"; } + +.fa-person-walking::before { + content: "\f554"; } + +.fa-walking::before { + content: "\f554"; } + +.fa-l::before { + content: "\4c"; } + +.fa-fire::before { + content: "\f06d"; } + +.fa-bed-pulse::before { + content: "\f487"; } + +.fa-procedures::before { + content: "\f487"; } + +.fa-shuttle-space::before { + content: "\f197"; } + +.fa-space-shuttle::before { + content: "\f197"; } + +.fa-face-laugh::before { + content: "\f599"; } + +.fa-laugh::before { + content: "\f599"; } + +.fa-folder-open::before { + content: "\f07c"; } + +.fa-heart-circle-plus::before { + content: "\e500"; } + +.fa-code-fork::before { + content: "\e13b"; } + +.fa-city::before { + content: "\f64f"; } + +.fa-microphone-lines::before { + content: "\f3c9"; } + +.fa-microphone-alt::before { + content: "\f3c9"; } + +.fa-pepper-hot::before { + content: "\f816"; } + +.fa-unlock::before { + content: "\f09c"; } + +.fa-colon-sign::before { + content: "\e140"; } + +.fa-headset::before { + content: "\f590"; } + +.fa-store-slash::before { + content: "\e071"; } + +.fa-road-circle-xmark::before { + content: "\e566"; } + +.fa-user-minus::before { + content: "\f503"; } + +.fa-mars-stroke-up::before { + content: "\f22a"; } + +.fa-mars-stroke-v::before { + content: "\f22a"; } + +.fa-champagne-glasses::before { + content: "\f79f"; } + +.fa-glass-cheers::before { + content: "\f79f"; } + +.fa-clipboard::before { + content: "\f328"; } + +.fa-house-circle-exclamation::before { + content: "\e50a"; } + +.fa-file-arrow-up::before { + content: "\f574"; } + +.fa-file-upload::before { + content: "\f574"; } + +.fa-wifi::before { + content: "\f1eb"; } + +.fa-wifi-3::before { + content: "\f1eb"; } + +.fa-wifi-strong::before { + content: "\f1eb"; } + +.fa-bath::before { + content: "\f2cd"; } + +.fa-bathtub::before { + content: "\f2cd"; } + +.fa-underline::before { + content: "\f0cd"; } + +.fa-user-pen::before { + content: "\f4ff"; } + +.fa-user-edit::before { + content: "\f4ff"; } + +.fa-signature::before { + content: "\f5b7"; } + +.fa-stroopwafel::before { + content: "\f551"; } + +.fa-bold::before { + content: "\f032"; } + +.fa-anchor-lock::before { + content: "\e4ad"; } + +.fa-building-ngo::before { + content: "\e4d7"; } + +.fa-manat-sign::before { + content: "\e1d5"; } + +.fa-not-equal::before { + content: "\f53e"; } + +.fa-border-top-left::before { + content: "\f853"; } + +.fa-border-style::before { + content: "\f853"; } + +.fa-map-location-dot::before { + content: "\f5a0"; } + +.fa-map-marked-alt::before { + content: "\f5a0"; } + +.fa-jedi::before { + content: "\f669"; } + +.fa-square-poll-vertical::before { + content: "\f681"; } + +.fa-poll::before { + content: "\f681"; } + +.fa-mug-hot::before { + content: "\f7b6"; } + +.fa-car-battery::before { + content: "\f5df"; } + +.fa-battery-car::before { + content: "\f5df"; } + +.fa-gift::before { + content: "\f06b"; } + +.fa-dice-two::before { + content: "\f528"; } + +.fa-chess-queen::before { + content: "\f445"; } + +.fa-glasses::before { + content: "\f530"; } + +.fa-chess-board::before { + content: "\f43c"; } + +.fa-building-circle-check::before { + content: "\e4d2"; } + +.fa-person-chalkboard::before { + content: "\e53d"; } + +.fa-mars-stroke-right::before { + content: "\f22b"; } + +.fa-mars-stroke-h::before { + content: "\f22b"; } + +.fa-hand-back-fist::before { + content: "\f255"; } + +.fa-hand-rock::before { + content: "\f255"; } + +.fa-square-caret-up::before { + content: "\f151"; } + +.fa-caret-square-up::before { + content: "\f151"; } + +.fa-cloud-showers-water::before { + content: "\e4e4"; } + +.fa-chart-bar::before { + content: "\f080"; } + +.fa-bar-chart::before { + content: "\f080"; } + +.fa-hands-bubbles::before { + content: "\e05e"; } + +.fa-hands-wash::before { + content: "\e05e"; } + +.fa-less-than-equal::before { + content: "\f537"; } + +.fa-train::before { + content: "\f238"; } + +.fa-eye-low-vision::before { + content: "\f2a8"; } + +.fa-low-vision::before { + content: "\f2a8"; } + +.fa-crow::before { + content: "\f520"; } + +.fa-sailboat::before { + content: "\e445"; } + +.fa-window-restore::before { + content: "\f2d2"; } + +.fa-square-plus::before { + content: "\f0fe"; } + +.fa-plus-square::before { + content: "\f0fe"; } + +.fa-torii-gate::before { + content: "\f6a1"; } + +.fa-frog::before { + content: "\f52e"; } + +.fa-bucket::before { + content: "\e4cf"; } + +.fa-image::before { + content: "\f03e"; } + +.fa-microphone::before { + content: "\f130"; } + +.fa-cow::before { + content: "\f6c8"; } + +.fa-caret-up::before { + content: "\f0d8"; } + +.fa-screwdriver::before { + content: "\f54a"; } + +.fa-folder-closed::before { + content: "\e185"; } + +.fa-house-tsunami::before { + content: "\e515"; } + +.fa-square-nfi::before { + content: "\e576"; } + +.fa-arrow-up-from-ground-water::before { + content: "\e4b5"; } + +.fa-martini-glass::before { + content: "\f57b"; } + +.fa-glass-martini-alt::before { + content: "\f57b"; } + +.fa-rotate-left::before { + content: "\f2ea"; } + +.fa-rotate-back::before { + content: "\f2ea"; } + +.fa-rotate-backward::before { + content: "\f2ea"; } + +.fa-undo-alt::before { + content: "\f2ea"; } + +.fa-table-columns::before { + content: "\f0db"; } + +.fa-columns::before { + content: "\f0db"; } + +.fa-lemon::before { + content: "\f094"; } + +.fa-head-side-mask::before { + content: "\e063"; } + +.fa-handshake::before { + content: "\f2b5"; } + +.fa-gem::before { + content: "\f3a5"; } + +.fa-dolly::before { + content: "\f472"; } + +.fa-dolly-box::before { + content: "\f472"; } + +.fa-smoking::before { + content: "\f48d"; } + +.fa-minimize::before { + content: "\f78c"; } + +.fa-compress-arrows-alt::before { + content: "\f78c"; } + +.fa-monument::before { + content: "\f5a6"; } + +.fa-snowplow::before { + content: "\f7d2"; } + +.fa-angles-right::before { + content: "\f101"; } + +.fa-angle-double-right::before { + content: "\f101"; } + +.fa-cannabis::before { + content: "\f55f"; } + +.fa-circle-play::before { + content: "\f144"; } + +.fa-play-circle::before { + content: "\f144"; } + +.fa-tablets::before { + content: "\f490"; } + +.fa-ethernet::before { + content: "\f796"; } + +.fa-euro-sign::before { + content: "\f153"; } + +.fa-eur::before { + content: "\f153"; } + +.fa-euro::before { + content: "\f153"; } + +.fa-chair::before { + content: "\f6c0"; } + +.fa-circle-check::before { + content: "\f058"; } + +.fa-check-circle::before { + content: "\f058"; } + +.fa-circle-stop::before { + content: "\f28d"; } + +.fa-stop-circle::before { + content: "\f28d"; } + +.fa-compass-drafting::before { + content: "\f568"; } + +.fa-drafting-compass::before { + content: "\f568"; } + +.fa-plate-wheat::before { + content: "\e55a"; } + +.fa-icicles::before { + content: "\f7ad"; } + +.fa-person-shelter::before { + content: "\e54f"; } + +.fa-neuter::before { + content: "\f22c"; } + +.fa-id-badge::before { + content: "\f2c1"; } + +.fa-marker::before { + content: "\f5a1"; } + +.fa-face-laugh-beam::before { + content: "\f59a"; } + +.fa-laugh-beam::before { + content: "\f59a"; } + +.fa-helicopter-symbol::before { + content: "\e502"; } + +.fa-universal-access::before { + content: "\f29a"; } + +.fa-circle-chevron-up::before { + content: "\f139"; } + +.fa-chevron-circle-up::before { + content: "\f139"; } + +.fa-lari-sign::before { + content: "\e1c8"; } + +.fa-volcano::before { + content: "\f770"; } + +.fa-person-walking-dashed-line-arrow-right::before { + content: "\e553"; } + +.fa-sterling-sign::before { + content: "\f154"; } + +.fa-gbp::before { + content: "\f154"; } + +.fa-pound-sign::before { + content: "\f154"; } + +.fa-viruses::before { + content: "\e076"; } + +.fa-square-person-confined::before { + content: "\e577"; } + +.fa-user-tie::before { + content: "\f508"; } + +.fa-arrow-down-long::before { + content: "\f175"; } + +.fa-long-arrow-down::before { + content: "\f175"; } + +.fa-tent-arrow-down-to-line::before { + content: "\e57e"; } + +.fa-certificate::before { + content: "\f0a3"; } + +.fa-reply-all::before { + content: "\f122"; } + +.fa-mail-reply-all::before { + content: "\f122"; } + +.fa-suitcase::before { + content: "\f0f2"; } + +.fa-person-skating::before { + content: "\f7c5"; } + +.fa-skating::before { + content: "\f7c5"; } + +.fa-filter-circle-dollar::before { + content: "\f662"; } + +.fa-funnel-dollar::before { + content: "\f662"; } + +.fa-camera-retro::before { + content: "\f083"; } + +.fa-circle-arrow-down::before { + content: "\f0ab"; } + +.fa-arrow-circle-down::before { + content: "\f0ab"; } + +.fa-file-import::before { + content: "\f56f"; } + +.fa-arrow-right-to-file::before { + content: "\f56f"; } + +.fa-square-arrow-up-right::before { + content: "\f14c"; } + +.fa-external-link-square::before { + content: "\f14c"; } + +.fa-box-open::before { + content: "\f49e"; } + +.fa-scroll::before { + content: "\f70e"; } + +.fa-spa::before { + content: "\f5bb"; } + +.fa-location-pin-lock::before { + content: "\e51f"; } + +.fa-pause::before { + content: "\f04c"; } + +.fa-hill-avalanche::before { + content: "\e507"; } + +.fa-temperature-empty::before { + content: "\f2cb"; } + +.fa-temperature-0::before { + content: "\f2cb"; } + +.fa-thermometer-0::before { + content: "\f2cb"; } + +.fa-thermometer-empty::before { + content: "\f2cb"; } + +.fa-bomb::before { + content: "\f1e2"; } + +.fa-registered::before { + content: "\f25d"; } + +.fa-address-card::before { + content: "\f2bb"; } + +.fa-contact-card::before { + content: "\f2bb"; } + +.fa-vcard::before { + content: "\f2bb"; } + +.fa-scale-unbalanced-flip::before { + content: "\f516"; } + +.fa-balance-scale-right::before { + content: "\f516"; } + +.fa-subscript::before { + content: "\f12c"; } + +.fa-diamond-turn-right::before { + content: "\f5eb"; } + +.fa-directions::before { + content: "\f5eb"; } + +.fa-burst::before { + content: "\e4dc"; } + +.fa-house-laptop::before { + content: "\e066"; } + +.fa-laptop-house::before { + content: "\e066"; } + +.fa-face-tired::before { + content: "\f5c8"; } + +.fa-tired::before { + content: "\f5c8"; } + +.fa-money-bills::before { + content: "\e1f3"; } + +.fa-smog::before { + content: "\f75f"; } + +.fa-crutch::before { + content: "\f7f7"; } + +.fa-cloud-arrow-up::before { + content: "\f0ee"; } + +.fa-cloud-upload::before { + content: "\f0ee"; } + +.fa-cloud-upload-alt::before { + content: "\f0ee"; } + +.fa-palette::before { + content: "\f53f"; } + +.fa-arrows-turn-right::before { + content: "\e4c0"; } + +.fa-vest::before { + content: "\e085"; } + +.fa-ferry::before { + content: "\e4ea"; } + +.fa-arrows-down-to-people::before { + content: "\e4b9"; } + +.fa-seedling::before { + content: "\f4d8"; } + +.fa-sprout::before { + content: "\f4d8"; } + +.fa-left-right::before { + content: "\f337"; } + +.fa-arrows-alt-h::before { + content: "\f337"; } + +.fa-boxes-packing::before { + content: "\e4c7"; } + +.fa-circle-arrow-left::before { + content: "\f0a8"; } + +.fa-arrow-circle-left::before { + content: "\f0a8"; } + +.fa-group-arrows-rotate::before { + content: "\e4f6"; } + +.fa-bowl-food::before { + content: "\e4c6"; } + +.fa-candy-cane::before { + content: "\f786"; } + +.fa-arrow-down-wide-short::before { + content: "\f160"; } + +.fa-sort-amount-asc::before { + content: "\f160"; } + +.fa-sort-amount-down::before { + content: "\f160"; } + +.fa-cloud-bolt::before { + content: "\f76c"; } + +.fa-thunderstorm::before { + content: "\f76c"; } + +.fa-text-slash::before { + content: "\f87d"; } + +.fa-remove-format::before { + content: "\f87d"; } + +.fa-face-smile-wink::before { + content: "\f4da"; } + +.fa-smile-wink::before { + content: "\f4da"; } + +.fa-file-word::before { + content: "\f1c2"; } + +.fa-file-powerpoint::before { + content: "\f1c4"; } + +.fa-arrows-left-right::before { + content: "\f07e"; } + +.fa-arrows-h::before { + content: "\f07e"; } + +.fa-house-lock::before { + content: "\e510"; } + +.fa-cloud-arrow-down::before { + content: "\f0ed"; } + +.fa-cloud-download::before { + content: "\f0ed"; } + +.fa-cloud-download-alt::before { + content: "\f0ed"; } + +.fa-children::before { + content: "\e4e1"; } + +.fa-chalkboard::before { + content: "\f51b"; } + +.fa-blackboard::before { + content: "\f51b"; } + +.fa-user-large-slash::before { + content: "\f4fa"; } + +.fa-user-alt-slash::before { + content: "\f4fa"; } + +.fa-envelope-open::before { + content: "\f2b6"; } + +.fa-handshake-simple-slash::before { + content: "\e05f"; } + +.fa-handshake-alt-slash::before { + content: "\e05f"; } + +.fa-mattress-pillow::before { + content: "\e525"; } + +.fa-guarani-sign::before { + content: "\e19a"; } + +.fa-arrows-rotate::before { + content: "\f021"; } + +.fa-refresh::before { + content: "\f021"; } + +.fa-sync::before { + content: "\f021"; } + +.fa-fire-extinguisher::before { + content: "\f134"; } + +.fa-cruzeiro-sign::before { + content: "\e152"; } + +.fa-greater-than-equal::before { + content: "\f532"; } + +.fa-shield-halved::before { + content: "\f3ed"; } + +.fa-shield-alt::before { + content: "\f3ed"; } + +.fa-book-atlas::before { + content: "\f558"; } + +.fa-atlas::before { + content: "\f558"; } + +.fa-virus::before { + content: "\e074"; } + +.fa-envelope-circle-check::before { + content: "\e4e8"; } + +.fa-layer-group::before { + content: "\f5fd"; } + +.fa-arrows-to-dot::before { + content: "\e4be"; } + +.fa-archway::before { + content: "\f557"; } + +.fa-heart-circle-check::before { + content: "\e4fd"; } + +.fa-house-chimney-crack::before { + content: "\f6f1"; } + +.fa-house-damage::before { + content: "\f6f1"; } + +.fa-file-zipper::before { + content: "\f1c6"; } + +.fa-file-archive::before { + content: "\f1c6"; } + +.fa-square::before { + content: "\f0c8"; } + +.fa-martini-glass-empty::before { + content: "\f000"; } + +.fa-glass-martini::before { + content: "\f000"; } + +.fa-couch::before { + content: "\f4b8"; } + +.fa-cedi-sign::before { + content: "\e0df"; } + +.fa-italic::before { + content: "\f033"; } + +.fa-church::before { + content: "\f51d"; } + +.fa-comments-dollar::before { + content: "\f653"; } + +.fa-democrat::before { + content: "\f747"; } + +.fa-z::before { + content: "\5a"; } + +.fa-person-skiing::before { + content: "\f7c9"; } + +.fa-skiing::before { + content: "\f7c9"; } + +.fa-road-lock::before { + content: "\e567"; } + +.fa-a::before { + content: "\41"; } + +.fa-temperature-arrow-down::before { + content: "\e03f"; } + +.fa-temperature-down::before { + content: "\e03f"; } + +.fa-feather-pointed::before { + content: "\f56b"; } + +.fa-feather-alt::before { + content: "\f56b"; } + +.fa-p::before { + content: "\50"; } + +.fa-snowflake::before { + content: "\f2dc"; } + +.fa-newspaper::before { + content: "\f1ea"; } + +.fa-rectangle-ad::before { + content: "\f641"; } + +.fa-ad::before { + content: "\f641"; } + +.fa-circle-arrow-right::before { + content: "\f0a9"; } + +.fa-arrow-circle-right::before { + content: "\f0a9"; } + +.fa-filter-circle-xmark::before { + content: "\e17b"; } + +.fa-locust::before { + content: "\e520"; } + +.fa-sort::before { + content: "\f0dc"; } + +.fa-unsorted::before { + content: "\f0dc"; } + +.fa-list-ol::before { + content: "\f0cb"; } + +.fa-list-1-2::before { + content: "\f0cb"; } + +.fa-list-numeric::before { + content: "\f0cb"; } + +.fa-person-dress-burst::before { + content: "\e544"; } + +.fa-money-check-dollar::before { + content: "\f53d"; } + +.fa-money-check-alt::before { + content: "\f53d"; } + +.fa-vector-square::before { + content: "\f5cb"; } + +.fa-bread-slice::before { + content: "\f7ec"; } + +.fa-language::before { + content: "\f1ab"; } + +.fa-face-kiss-wink-heart::before { + content: "\f598"; } + +.fa-kiss-wink-heart::before { + content: "\f598"; } + +.fa-filter::before { + content: "\f0b0"; } + +.fa-question::before { + content: "\3f"; } + +.fa-file-signature::before { + content: "\f573"; } + +.fa-up-down-left-right::before { + content: "\f0b2"; } + +.fa-arrows-alt::before { + content: "\f0b2"; } + +.fa-house-chimney-user::before { + content: "\e065"; } + +.fa-hand-holding-heart::before { + content: "\f4be"; } + +.fa-puzzle-piece::before { + content: "\f12e"; } + +.fa-money-check::before { + content: "\f53c"; } + +.fa-star-half-stroke::before { + content: "\f5c0"; } + +.fa-star-half-alt::before { + content: "\f5c0"; } + +.fa-code::before { + content: "\f121"; } + +.fa-whiskey-glass::before { + content: "\f7a0"; } + +.fa-glass-whiskey::before { + content: "\f7a0"; } + +.fa-building-circle-exclamation::before { + content: "\e4d3"; } + +.fa-magnifying-glass-chart::before { + content: "\e522"; } + +.fa-arrow-up-right-from-square::before { + content: "\f08e"; } + +.fa-external-link::before { + content: "\f08e"; } + +.fa-cubes-stacked::before { + content: "\e4e6"; } + +.fa-won-sign::before { + content: "\f159"; } + +.fa-krw::before { + content: "\f159"; } + +.fa-won::before { + content: "\f159"; } + +.fa-virus-covid::before { + content: "\e4a8"; } + +.fa-austral-sign::before { + content: "\e0a9"; } + +.fa-f::before { + content: "\46"; } + +.fa-leaf::before { + content: "\f06c"; } + +.fa-road::before { + content: "\f018"; } + +.fa-taxi::before { + content: "\f1ba"; } + +.fa-cab::before { + content: "\f1ba"; } + +.fa-person-circle-plus::before { + content: "\e541"; } + +.fa-chart-pie::before { + content: "\f200"; } + +.fa-pie-chart::before { + content: "\f200"; } + +.fa-bolt-lightning::before { + content: "\e0b7"; } + +.fa-sack-xmark::before { + content: "\e56a"; } + +.fa-file-excel::before { + content: "\f1c3"; } + +.fa-file-contract::before { + content: "\f56c"; } + +.fa-fish-fins::before { + content: "\e4f2"; } + +.fa-building-flag::before { + content: "\e4d5"; } + +.fa-face-grin-beam::before { + content: "\f582"; } + +.fa-grin-beam::before { + content: "\f582"; } + +.fa-object-ungroup::before { + content: "\f248"; } + +.fa-poop::before { + content: "\f619"; } + +.fa-location-pin::before { + content: "\f041"; } + +.fa-map-marker::before { + content: "\f041"; } + +.fa-kaaba::before { + content: "\f66b"; } + +.fa-toilet-paper::before { + content: "\f71e"; } + +.fa-helmet-safety::before { + content: "\f807"; } + +.fa-hard-hat::before { + content: "\f807"; } + +.fa-hat-hard::before { + content: "\f807"; } + +.fa-eject::before { + content: "\f052"; } + +.fa-circle-right::before { + content: "\f35a"; } + +.fa-arrow-alt-circle-right::before { + content: "\f35a"; } + +.fa-plane-circle-check::before { + content: "\e555"; } + +.fa-face-rolling-eyes::before { + content: "\f5a5"; } + +.fa-meh-rolling-eyes::before { + content: "\f5a5"; } + +.fa-object-group::before { + content: "\f247"; } + +.fa-chart-line::before { + content: "\f201"; } + +.fa-line-chart::before { + content: "\f201"; } + +.fa-mask-ventilator::before { + content: "\e524"; } + +.fa-arrow-right::before { + content: "\f061"; } + +.fa-signs-post::before { + content: "\f277"; } + +.fa-map-signs::before { + content: "\f277"; } + +.fa-cash-register::before { + content: "\f788"; } + +.fa-person-circle-question::before { + content: "\e542"; } + +.fa-h::before { + content: "\48"; } + +.fa-tarp::before { + content: "\e57b"; } + +.fa-screwdriver-wrench::before { + content: "\f7d9"; } + +.fa-tools::before { + content: "\f7d9"; } + +.fa-arrows-to-eye::before { + content: "\e4bf"; } + +.fa-plug-circle-bolt::before { + content: "\e55b"; } + +.fa-heart::before { + content: "\f004"; } + +.fa-mars-and-venus::before { + content: "\f224"; } + +.fa-house-user::before { + content: "\e1b0"; } + +.fa-home-user::before { + content: "\e1b0"; } + +.fa-dumpster-fire::before { + content: "\f794"; } + +.fa-house-crack::before { + content: "\e3b1"; } + +.fa-martini-glass-citrus::before { + content: "\f561"; } + +.fa-cocktail::before { + content: "\f561"; } + +.fa-face-surprise::before { + content: "\f5c2"; } + +.fa-surprise::before { + content: "\f5c2"; } + +.fa-bottle-water::before { + content: "\e4c5"; } + +.fa-circle-pause::before { + content: "\f28b"; } + +.fa-pause-circle::before { + content: "\f28b"; } + +.fa-toilet-paper-slash::before { + content: "\e072"; } + +.fa-apple-whole::before { + content: "\f5d1"; } + +.fa-apple-alt::before { + content: "\f5d1"; } + +.fa-kitchen-set::before { + content: "\e51a"; } + +.fa-r::before { + content: "\52"; } + +.fa-temperature-quarter::before { + content: "\f2ca"; } + +.fa-temperature-1::before { + content: "\f2ca"; } + +.fa-thermometer-1::before { + content: "\f2ca"; } + +.fa-thermometer-quarter::before { + content: "\f2ca"; } + +.fa-cube::before { + content: "\f1b2"; } + +.fa-bitcoin-sign::before { + content: "\e0b4"; } + +.fa-shield-dog::before { + content: "\e573"; } + +.fa-solar-panel::before { + content: "\f5ba"; } + +.fa-lock-open::before { + content: "\f3c1"; } + +.fa-elevator::before { + content: "\e16d"; } + +.fa-money-bill-transfer::before { + content: "\e528"; } + +.fa-money-bill-trend-up::before { + content: "\e529"; } + +.fa-house-flood-water-circle-arrow-right::before { + content: "\e50f"; } + +.fa-square-poll-horizontal::before { + content: "\f682"; } + +.fa-poll-h::before { + content: "\f682"; } + +.fa-circle::before { + content: "\f111"; } + +.fa-backward-fast::before { + content: "\f049"; } + +.fa-fast-backward::before { + content: "\f049"; } + +.fa-recycle::before { + content: "\f1b8"; } + +.fa-user-astronaut::before { + content: "\f4fb"; } + +.fa-plane-slash::before { + content: "\e069"; } + +.fa-trademark::before { + content: "\f25c"; } + +.fa-basketball::before { + content: "\f434"; } + +.fa-basketball-ball::before { + content: "\f434"; } + +.fa-satellite-dish::before { + content: "\f7c0"; } + +.fa-circle-up::before { + content: "\f35b"; } + +.fa-arrow-alt-circle-up::before { + content: "\f35b"; } + +.fa-mobile-screen-button::before { + content: "\f3cd"; } + +.fa-mobile-alt::before { + content: "\f3cd"; } + +.fa-volume-high::before { + content: "\f028"; } + +.fa-volume-up::before { + content: "\f028"; } + +.fa-users-rays::before { + content: "\e593"; } + +.fa-wallet::before { + content: "\f555"; } + +.fa-clipboard-check::before { + content: "\f46c"; } + +.fa-file-audio::before { + content: "\f1c7"; } + +.fa-burger::before { + content: "\f805"; } + +.fa-hamburger::before { + content: "\f805"; } + +.fa-wrench::before { + content: "\f0ad"; } + +.fa-bugs::before { + content: "\e4d0"; } + +.fa-rupee-sign::before { + content: "\f156"; } + +.fa-rupee::before { + content: "\f156"; } + +.fa-file-image::before { + content: "\f1c5"; } + +.fa-circle-question::before { + content: "\f059"; } + +.fa-question-circle::before { + content: "\f059"; } + +.fa-plane-departure::before { + content: "\f5b0"; } + +.fa-handshake-slash::before { + content: "\e060"; } + +.fa-book-bookmark::before { + content: "\e0bb"; } + +.fa-code-branch::before { + content: "\f126"; } + +.fa-hat-cowboy::before { + content: "\f8c0"; } + +.fa-bridge::before { + content: "\e4c8"; } + +.fa-phone-flip::before { + content: "\f879"; } + +.fa-phone-alt::before { + content: "\f879"; } + +.fa-truck-front::before { + content: "\e2b7"; } + +.fa-cat::before { + content: "\f6be"; } + +.fa-anchor-circle-exclamation::before { + content: "\e4ab"; } + +.fa-truck-field::before { + content: "\e58d"; } + +.fa-route::before { + content: "\f4d7"; } + +.fa-clipboard-question::before { + content: "\e4e3"; } + +.fa-panorama::before { + content: "\e209"; } + +.fa-comment-medical::before { + content: "\f7f5"; } + +.fa-teeth-open::before { + content: "\f62f"; } + +.fa-file-circle-minus::before { + content: "\e4ed"; } + +.fa-tags::before { + content: "\f02c"; } + +.fa-wine-glass::before { + content: "\f4e3"; } + +.fa-forward-fast::before { + content: "\f050"; } + +.fa-fast-forward::before { + content: "\f050"; } + +.fa-face-meh-blank::before { + content: "\f5a4"; } + +.fa-meh-blank::before { + content: "\f5a4"; } + +.fa-square-parking::before { + content: "\f540"; } + +.fa-parking::before { + content: "\f540"; } + +.fa-house-signal::before { + content: "\e012"; } + +.fa-bars-progress::before { + content: "\f828"; } + +.fa-tasks-alt::before { + content: "\f828"; } + +.fa-faucet-drip::before { + content: "\e006"; } + +.fa-cart-flatbed::before { + content: "\f474"; } + +.fa-dolly-flatbed::before { + content: "\f474"; } + +.fa-ban-smoking::before { + content: "\f54d"; } + +.fa-smoking-ban::before { + content: "\f54d"; } + +.fa-terminal::before { + content: "\f120"; } + +.fa-mobile-button::before { + content: "\f10b"; } + +.fa-house-medical-flag::before { + content: "\e514"; } + +.fa-basket-shopping::before { + content: "\f291"; } + +.fa-shopping-basket::before { + content: "\f291"; } + +.fa-tape::before { + content: "\f4db"; } + +.fa-bus-simple::before { + content: "\f55e"; } + +.fa-bus-alt::before { + content: "\f55e"; } + +.fa-eye::before { + content: "\f06e"; } + +.fa-face-sad-cry::before { + content: "\f5b3"; } + +.fa-sad-cry::before { + content: "\f5b3"; } + +.fa-audio-description::before { + content: "\f29e"; } + +.fa-person-military-to-person::before { + content: "\e54c"; } + +.fa-file-shield::before { + content: "\e4f0"; } + +.fa-user-slash::before { + content: "\f506"; } + +.fa-pen::before { + content: "\f304"; } + +.fa-tower-observation::before { + content: "\e586"; } + +.fa-file-code::before { + content: "\f1c9"; } + +.fa-signal::before { + content: "\f012"; } + +.fa-signal-5::before { + content: "\f012"; } + +.fa-signal-perfect::before { + content: "\f012"; } + +.fa-bus::before { + content: "\f207"; } + +.fa-heart-circle-xmark::before { + content: "\e501"; } + +.fa-house-chimney::before { + content: "\e3af"; } + +.fa-home-lg::before { + content: "\e3af"; } + +.fa-window-maximize::before { + content: "\f2d0"; } + +.fa-face-frown::before { + content: "\f119"; } + +.fa-frown::before { + content: "\f119"; } + +.fa-prescription::before { + content: "\f5b1"; } + +.fa-shop::before { + content: "\f54f"; } + +.fa-store-alt::before { + content: "\f54f"; } + +.fa-floppy-disk::before { + content: "\f0c7"; } + +.fa-save::before { + content: "\f0c7"; } + +.fa-vihara::before { + content: "\f6a7"; } + +.fa-scale-unbalanced::before { + content: "\f515"; } + +.fa-balance-scale-left::before { + content: "\f515"; } + +.fa-sort-up::before { + content: "\f0de"; } + +.fa-sort-asc::before { + content: "\f0de"; } + +.fa-comment-dots::before { + content: "\f4ad"; } + +.fa-commenting::before { + content: "\f4ad"; } + +.fa-plant-wilt::before { + content: "\e5aa"; } + +.fa-diamond::before { + content: "\f219"; } + +.fa-face-grin-squint::before { + content: "\f585"; } + +.fa-grin-squint::before { + content: "\f585"; } + +.fa-hand-holding-dollar::before { + content: "\f4c0"; } + +.fa-hand-holding-usd::before { + content: "\f4c0"; } + +.fa-bacterium::before { + content: "\e05a"; } + +.fa-hand-pointer::before { + content: "\f25a"; } + +.fa-drum-steelpan::before { + content: "\f56a"; } + +.fa-hand-scissors::before { + content: "\f257"; } + +.fa-hands-praying::before { + content: "\f684"; } + +.fa-praying-hands::before { + content: "\f684"; } + +.fa-arrow-rotate-right::before { + content: "\f01e"; } + +.fa-arrow-right-rotate::before { + content: "\f01e"; } + +.fa-arrow-rotate-forward::before { + content: "\f01e"; } + +.fa-redo::before { + content: "\f01e"; } + +.fa-biohazard::before { + content: "\f780"; } + +.fa-location-crosshairs::before { + content: "\f601"; } + +.fa-location::before { + content: "\f601"; } + +.fa-mars-double::before { + content: "\f227"; } + +.fa-child-dress::before { + content: "\e59c"; } + +.fa-users-between-lines::before { + content: "\e591"; } + +.fa-lungs-virus::before { + content: "\e067"; } + +.fa-face-grin-tears::before { + content: "\f588"; } + +.fa-grin-tears::before { + content: "\f588"; } + +.fa-phone::before { + content: "\f095"; } + +.fa-calendar-xmark::before { + content: "\f273"; } + +.fa-calendar-times::before { + content: "\f273"; } + +.fa-child-reaching::before { + content: "\e59d"; } + +.fa-head-side-virus::before { + content: "\e064"; } + +.fa-user-gear::before { + content: "\f4fe"; } + +.fa-user-cog::before { + content: "\f4fe"; } + +.fa-arrow-up-1-9::before { + content: "\f163"; } + +.fa-sort-numeric-up::before { + content: "\f163"; } + +.fa-door-closed::before { + content: "\f52a"; } + +.fa-shield-virus::before { + content: "\e06c"; } + +.fa-dice-six::before { + content: "\f526"; } + +.fa-mosquito-net::before { + content: "\e52c"; } + +.fa-bridge-water::before { + content: "\e4ce"; } + +.fa-person-booth::before { + content: "\f756"; } + +.fa-text-width::before { + content: "\f035"; } + +.fa-hat-wizard::before { + content: "\f6e8"; } + +.fa-pen-fancy::before { + content: "\f5ac"; } + +.fa-person-digging::before { + content: "\f85e"; } + +.fa-digging::before { + content: "\f85e"; } + +.fa-trash::before { + content: "\f1f8"; } + +.fa-gauge-simple::before { + content: "\f629"; } + +.fa-gauge-simple-med::before { + content: "\f629"; } + +.fa-tachometer-average::before { + content: "\f629"; } + +.fa-book-medical::before { + content: "\f7e6"; } + +.fa-poo::before { + content: "\f2fe"; } + +.fa-quote-right::before { + content: "\f10e"; } + +.fa-quote-right-alt::before { + content: "\f10e"; } + +.fa-shirt::before { + content: "\f553"; } + +.fa-t-shirt::before { + content: "\f553"; } + +.fa-tshirt::before { + content: "\f553"; } + +.fa-cubes::before { + content: "\f1b3"; } + +.fa-divide::before { + content: "\f529"; } + +.fa-tenge-sign::before { + content: "\f7d7"; } + +.fa-tenge::before { + content: "\f7d7"; } + +.fa-headphones::before { + content: "\f025"; } + +.fa-hands-holding::before { + content: "\f4c2"; } + +.fa-hands-clapping::before { + content: "\e1a8"; } + +.fa-republican::before { + content: "\f75e"; } + +.fa-arrow-left::before { + content: "\f060"; } + +.fa-person-circle-xmark::before { + content: "\e543"; } + +.fa-ruler::before { + content: "\f545"; } + +.fa-align-left::before { + content: "\f036"; } + +.fa-dice-d6::before { + content: "\f6d1"; } + +.fa-restroom::before { + content: "\f7bd"; } + +.fa-j::before { + content: "\4a"; } + +.fa-users-viewfinder::before { + content: "\e595"; } + +.fa-file-video::before { + content: "\f1c8"; } + +.fa-up-right-from-square::before { + content: "\f35d"; } + +.fa-external-link-alt::before { + content: "\f35d"; } + +.fa-table-cells::before { + content: "\f00a"; } + +.fa-th::before { + content: "\f00a"; } + +.fa-file-pdf::before { + content: "\f1c1"; } + +.fa-book-bible::before { + content: "\f647"; } + +.fa-bible::before { + content: "\f647"; } + +.fa-o::before { + content: "\4f"; } + +.fa-suitcase-medical::before { + content: "\f0fa"; } + +.fa-medkit::before { + content: "\f0fa"; } + +.fa-user-secret::before { + content: "\f21b"; } + +.fa-otter::before { + content: "\f700"; } + +.fa-person-dress::before { + content: "\f182"; } + +.fa-female::before { + content: "\f182"; } + +.fa-comment-dollar::before { + content: "\f651"; } + +.fa-business-time::before { + content: "\f64a"; } + +.fa-briefcase-clock::before { + content: "\f64a"; } + +.fa-table-cells-large::before { + content: "\f009"; } + +.fa-th-large::before { + content: "\f009"; } + +.fa-book-tanakh::before { + content: "\f827"; } + +.fa-tanakh::before { + content: "\f827"; } + +.fa-phone-volume::before { + content: "\f2a0"; } + +.fa-volume-control-phone::before { + content: "\f2a0"; } + +.fa-hat-cowboy-side::before { + content: "\f8c1"; } + +.fa-clipboard-user::before { + content: "\f7f3"; } + +.fa-child::before { + content: "\f1ae"; } + +.fa-lira-sign::before { + content: "\f195"; } + +.fa-satellite::before { + content: "\f7bf"; } + +.fa-plane-lock::before { + content: "\e558"; } + +.fa-tag::before { + content: "\f02b"; } + +.fa-comment::before { + content: "\f075"; } + +.fa-cake-candles::before { + content: "\f1fd"; } + +.fa-birthday-cake::before { + content: "\f1fd"; } + +.fa-cake::before { + content: "\f1fd"; } + +.fa-envelope::before { + content: "\f0e0"; } + +.fa-angles-up::before { + content: "\f102"; } + +.fa-angle-double-up::before { + content: "\f102"; } + +.fa-paperclip::before { + content: "\f0c6"; } + +.fa-arrow-right-to-city::before { + content: "\e4b3"; } + +.fa-ribbon::before { + content: "\f4d6"; } + +.fa-lungs::before { + content: "\f604"; } + +.fa-arrow-up-9-1::before { + content: "\f887"; } + +.fa-sort-numeric-up-alt::before { + content: "\f887"; } + +.fa-litecoin-sign::before { + content: "\e1d3"; } + +.fa-border-none::before { + content: "\f850"; } + +.fa-circle-nodes::before { + content: "\e4e2"; } + +.fa-parachute-box::before { + content: "\f4cd"; } + +.fa-indent::before { + content: "\f03c"; } + +.fa-truck-field-un::before { + content: "\e58e"; } + +.fa-hourglass::before { + content: "\f254"; } + +.fa-hourglass-empty::before { + content: "\f254"; } + +.fa-mountain::before { + content: "\f6fc"; } + +.fa-user-doctor::before { + content: "\f0f0"; } + +.fa-user-md::before { + content: "\f0f0"; } + +.fa-circle-info::before { + content: "\f05a"; } + +.fa-info-circle::before { + content: "\f05a"; } + +.fa-cloud-meatball::before { + content: "\f73b"; } + +.fa-camera::before { + content: "\f030"; } + +.fa-camera-alt::before { + content: "\f030"; } + +.fa-square-virus::before { + content: "\e578"; } + +.fa-meteor::before { + content: "\f753"; } + +.fa-car-on::before { + content: "\e4dd"; } + +.fa-sleigh::before { + content: "\f7cc"; } + +.fa-arrow-down-1-9::before { + content: "\f162"; } + +.fa-sort-numeric-asc::before { + content: "\f162"; } + +.fa-sort-numeric-down::before { + content: "\f162"; } + +.fa-hand-holding-droplet::before { + content: "\f4c1"; } + +.fa-hand-holding-water::before { + content: "\f4c1"; } + +.fa-water::before { + content: "\f773"; } + +.fa-calendar-check::before { + content: "\f274"; } + +.fa-braille::before { + content: "\f2a1"; } + +.fa-prescription-bottle-medical::before { + content: "\f486"; } + +.fa-prescription-bottle-alt::before { + content: "\f486"; } + +.fa-landmark::before { + content: "\f66f"; } + +.fa-truck::before { + content: "\f0d1"; } + +.fa-crosshairs::before { + content: "\f05b"; } + +.fa-person-cane::before { + content: "\e53c"; } + +.fa-tent::before { + content: "\e57d"; } + +.fa-vest-patches::before { + content: "\e086"; } + +.fa-check-double::before { + content: "\f560"; } + +.fa-arrow-down-a-z::before { + content: "\f15d"; } + +.fa-sort-alpha-asc::before { + content: "\f15d"; } + +.fa-sort-alpha-down::before { + content: "\f15d"; } + +.fa-money-bill-wheat::before { + content: "\e52a"; } + +.fa-cookie::before { + content: "\f563"; } + +.fa-arrow-rotate-left::before { + content: "\f0e2"; } + +.fa-arrow-left-rotate::before { + content: "\f0e2"; } + +.fa-arrow-rotate-back::before { + content: "\f0e2"; } + +.fa-arrow-rotate-backward::before { + content: "\f0e2"; } + +.fa-undo::before { + content: "\f0e2"; } + +.fa-hard-drive::before { + content: "\f0a0"; } + +.fa-hdd::before { + content: "\f0a0"; } + +.fa-face-grin-squint-tears::before { + content: "\f586"; } + +.fa-grin-squint-tears::before { + content: "\f586"; } + +.fa-dumbbell::before { + content: "\f44b"; } + +.fa-rectangle-list::before { + content: "\f022"; } + +.fa-list-alt::before { + content: "\f022"; } + +.fa-tarp-droplet::before { + content: "\e57c"; } + +.fa-house-medical-circle-check::before { + content: "\e511"; } + +.fa-person-skiing-nordic::before { + content: "\f7ca"; } + +.fa-skiing-nordic::before { + content: "\f7ca"; } + +.fa-calendar-plus::before { + content: "\f271"; } + +.fa-plane-arrival::before { + content: "\f5af"; } + +.fa-circle-left::before { + content: "\f359"; } + +.fa-arrow-alt-circle-left::before { + content: "\f359"; } + +.fa-train-subway::before { + content: "\f239"; } + +.fa-subway::before { + content: "\f239"; } + +.fa-chart-gantt::before { + content: "\e0e4"; } + +.fa-indian-rupee-sign::before { + content: "\e1bc"; } + +.fa-indian-rupee::before { + content: "\e1bc"; } + +.fa-inr::before { + content: "\e1bc"; } + +.fa-crop-simple::before { + content: "\f565"; } + +.fa-crop-alt::before { + content: "\f565"; } + +.fa-money-bill-1::before { + content: "\f3d1"; } + +.fa-money-bill-alt::before { + content: "\f3d1"; } + +.fa-left-long::before { + content: "\f30a"; } + +.fa-long-arrow-alt-left::before { + content: "\f30a"; } + +.fa-dna::before { + content: "\f471"; } + +.fa-virus-slash::before { + content: "\e075"; } + +.fa-minus::before { + content: "\f068"; } + +.fa-subtract::before { + content: "\f068"; } + +.fa-chess::before { + content: "\f439"; } + +.fa-arrow-left-long::before { + content: "\f177"; } + +.fa-long-arrow-left::before { + content: "\f177"; } + +.fa-plug-circle-check::before { + content: "\e55c"; } + +.fa-street-view::before { + content: "\f21d"; } + +.fa-franc-sign::before { + content: "\e18f"; } + +.fa-volume-off::before { + content: "\f026"; } + +.fa-hands-asl-interpreting::before { + content: "\f2a3"; } + +.fa-american-sign-language-interpreting::before { + content: "\f2a3"; } + +.fa-asl-interpreting::before { + content: "\f2a3"; } + +.fa-hands-american-sign-language-interpreting::before { + content: "\f2a3"; } + +.fa-gear::before { + content: "\f013"; } + +.fa-cog::before { + content: "\f013"; } + +.fa-droplet-slash::before { + content: "\f5c7"; } + +.fa-tint-slash::before { + content: "\f5c7"; } + +.fa-mosque::before { + content: "\f678"; } + +.fa-mosquito::before { + content: "\e52b"; } + +.fa-star-of-david::before { + content: "\f69a"; } + +.fa-person-military-rifle::before { + content: "\e54b"; } + +.fa-cart-shopping::before { + content: "\f07a"; } + +.fa-shopping-cart::before { + content: "\f07a"; } + +.fa-vials::before { + content: "\f493"; } + +.fa-plug-circle-plus::before { + content: "\e55f"; } + +.fa-place-of-worship::before { + content: "\f67f"; } + +.fa-grip-vertical::before { + content: "\f58e"; } + +.fa-arrow-turn-up::before { + content: "\f148"; } + +.fa-level-up::before { + content: "\f148"; } + +.fa-u::before { + content: "\55"; } + +.fa-square-root-variable::before { + content: "\f698"; } + +.fa-square-root-alt::before { + content: "\f698"; } + +.fa-clock::before { + content: "\f017"; } + +.fa-clock-four::before { + content: "\f017"; } + +.fa-backward-step::before { + content: "\f048"; } + +.fa-step-backward::before { + content: "\f048"; } + +.fa-pallet::before { + content: "\f482"; } + +.fa-faucet::before { + content: "\e005"; } + +.fa-baseball-bat-ball::before { + content: "\f432"; } + +.fa-s::before { + content: "\53"; } + +.fa-timeline::before { + content: "\e29c"; } + +.fa-keyboard::before { + content: "\f11c"; } + +.fa-caret-down::before { + content: "\f0d7"; } + +.fa-house-chimney-medical::before { + content: "\f7f2"; } + +.fa-clinic-medical::before { + content: "\f7f2"; } + +.fa-temperature-three-quarters::before { + content: "\f2c8"; } + +.fa-temperature-3::before { + content: "\f2c8"; } + +.fa-thermometer-3::before { + content: "\f2c8"; } + +.fa-thermometer-three-quarters::before { + content: "\f2c8"; } + +.fa-mobile-screen::before { + content: "\f3cf"; } + +.fa-mobile-android-alt::before { + content: "\f3cf"; } + +.fa-plane-up::before { + content: "\e22d"; } + +.fa-piggy-bank::before { + content: "\f4d3"; } + +.fa-battery-half::before { + content: "\f242"; } + +.fa-battery-3::before { + content: "\f242"; } + +.fa-mountain-city::before { + content: "\e52e"; } + +.fa-coins::before { + content: "\f51e"; } + +.fa-khanda::before { + content: "\f66d"; } + +.fa-sliders::before { + content: "\f1de"; } + +.fa-sliders-h::before { + content: "\f1de"; } + +.fa-folder-tree::before { + content: "\f802"; } + +.fa-network-wired::before { + content: "\f6ff"; } + +.fa-map-pin::before { + content: "\f276"; } + +.fa-hamsa::before { + content: "\f665"; } + +.fa-cent-sign::before { + content: "\e3f5"; } + +.fa-flask::before { + content: "\f0c3"; } + +.fa-person-pregnant::before { + content: "\e31e"; } + +.fa-wand-sparkles::before { + content: "\f72b"; } + +.fa-ellipsis-vertical::before { + content: "\f142"; } + +.fa-ellipsis-v::before { + content: "\f142"; } + +.fa-ticket::before { + content: "\f145"; } + +.fa-power-off::before { + content: "\f011"; } + +.fa-right-long::before { + content: "\f30b"; } + +.fa-long-arrow-alt-right::before { + content: "\f30b"; } + +.fa-flag-usa::before { + content: "\f74d"; } + +.fa-laptop-file::before { + content: "\e51d"; } + +.fa-tty::before { + content: "\f1e4"; } + +.fa-teletype::before { + content: "\f1e4"; } + +.fa-diagram-next::before { + content: "\e476"; } + +.fa-person-rifle::before { + content: "\e54e"; } + +.fa-house-medical-circle-exclamation::before { + content: "\e512"; } + +.fa-closed-captioning::before { + content: "\f20a"; } + +.fa-person-hiking::before { + content: "\f6ec"; } + +.fa-hiking::before { + content: "\f6ec"; } + +.fa-venus-double::before { + content: "\f226"; } + +.fa-images::before { + content: "\f302"; } + +.fa-calculator::before { + content: "\f1ec"; } + +.fa-people-pulling::before { + content: "\e535"; } + +.fa-n::before { + content: "\4e"; } + +.fa-cable-car::before { + content: "\f7da"; } + +.fa-tram::before { + content: "\f7da"; } + +.fa-cloud-rain::before { + content: "\f73d"; } + +.fa-building-circle-xmark::before { + content: "\e4d4"; } + +.fa-ship::before { + content: "\f21a"; } + +.fa-arrows-down-to-line::before { + content: "\e4b8"; } + +.fa-download::before { + content: "\f019"; } + +.fa-face-grin::before { + content: "\f580"; } + +.fa-grin::before { + content: "\f580"; } + +.fa-delete-left::before { + content: "\f55a"; } + +.fa-backspace::before { + content: "\f55a"; } + +.fa-eye-dropper::before { + content: "\f1fb"; } + +.fa-eye-dropper-empty::before { + content: "\f1fb"; } + +.fa-eyedropper::before { + content: "\f1fb"; } + +.fa-file-circle-check::before { + content: "\e5a0"; } + +.fa-forward::before { + content: "\f04e"; } + +.fa-mobile::before { + content: "\f3ce"; } + +.fa-mobile-android::before { + content: "\f3ce"; } + +.fa-mobile-phone::before { + content: "\f3ce"; } + +.fa-face-meh::before { + content: "\f11a"; } + +.fa-meh::before { + content: "\f11a"; } + +.fa-align-center::before { + content: "\f037"; } + +.fa-book-skull::before { + content: "\f6b7"; } + +.fa-book-dead::before { + content: "\f6b7"; } + +.fa-id-card::before { + content: "\f2c2"; } + +.fa-drivers-license::before { + content: "\f2c2"; } + +.fa-outdent::before { + content: "\f03b"; } + +.fa-dedent::before { + content: "\f03b"; } + +.fa-heart-circle-exclamation::before { + content: "\e4fe"; } + +.fa-house::before { + content: "\f015"; } + +.fa-home::before { + content: "\f015"; } + +.fa-home-alt::before { + content: "\f015"; } + +.fa-home-lg-alt::before { + content: "\f015"; } + +.fa-calendar-week::before { + content: "\f784"; } + +.fa-laptop-medical::before { + content: "\f812"; } + +.fa-b::before { + content: "\42"; } + +.fa-file-medical::before { + content: "\f477"; } + +.fa-dice-one::before { + content: "\f525"; } + +.fa-kiwi-bird::before { + content: "\f535"; } + +.fa-arrow-right-arrow-left::before { + content: "\f0ec"; } + +.fa-exchange::before { + content: "\f0ec"; } + +.fa-rotate-right::before { + content: "\f2f9"; } + +.fa-redo-alt::before { + content: "\f2f9"; } + +.fa-rotate-forward::before { + content: "\f2f9"; } + +.fa-utensils::before { + content: "\f2e7"; } + +.fa-cutlery::before { + content: "\f2e7"; } + +.fa-arrow-up-wide-short::before { + content: "\f161"; } + +.fa-sort-amount-up::before { + content: "\f161"; } + +.fa-mill-sign::before { + content: "\e1ed"; } + +.fa-bowl-rice::before { + content: "\e2eb"; } + +.fa-skull::before { + content: "\f54c"; } + +.fa-tower-broadcast::before { + content: "\f519"; } + +.fa-broadcast-tower::before { + content: "\f519"; } + +.fa-truck-pickup::before { + content: "\f63c"; } + +.fa-up-long::before { + content: "\f30c"; } + +.fa-long-arrow-alt-up::before { + content: "\f30c"; } + +.fa-stop::before { + content: "\f04d"; } + +.fa-code-merge::before { + content: "\f387"; } + +.fa-upload::before { + content: "\f093"; } + +.fa-hurricane::before { + content: "\f751"; } + +.fa-mound::before { + content: "\e52d"; } + +.fa-toilet-portable::before { + content: "\e583"; } + +.fa-compact-disc::before { + content: "\f51f"; } + +.fa-file-arrow-down::before { + content: "\f56d"; } + +.fa-file-download::before { + content: "\f56d"; } + +.fa-caravan::before { + content: "\f8ff"; } + +.fa-shield-cat::before { + content: "\e572"; } + +.fa-bolt::before { + content: "\f0e7"; } + +.fa-zap::before { + content: "\f0e7"; } + +.fa-glass-water::before { + content: "\e4f4"; } + +.fa-oil-well::before { + content: "\e532"; } + +.fa-vault::before { + content: "\e2c5"; } + +.fa-mars::before { + content: "\f222"; } + +.fa-toilet::before { + content: "\f7d8"; } + +.fa-plane-circle-xmark::before { + content: "\e557"; } + +.fa-yen-sign::before { + content: "\f157"; } + +.fa-cny::before { + content: "\f157"; } + +.fa-jpy::before { + content: "\f157"; } + +.fa-rmb::before { + content: "\f157"; } + +.fa-yen::before { + content: "\f157"; } + +.fa-ruble-sign::before { + content: "\f158"; } + +.fa-rouble::before { + content: "\f158"; } + +.fa-rub::before { + content: "\f158"; } + +.fa-ruble::before { + content: "\f158"; } + +.fa-sun::before { + content: "\f185"; } + +.fa-guitar::before { + content: "\f7a6"; } + +.fa-face-laugh-wink::before { + content: "\f59c"; } + +.fa-laugh-wink::before { + content: "\f59c"; } + +.fa-horse-head::before { + content: "\f7ab"; } + +.fa-bore-hole::before { + content: "\e4c3"; } + +.fa-industry::before { + content: "\f275"; } + +.fa-circle-down::before { + content: "\f358"; } + +.fa-arrow-alt-circle-down::before { + content: "\f358"; } + +.fa-arrows-turn-to-dots::before { + content: "\e4c1"; } + +.fa-florin-sign::before { + content: "\e184"; } + +.fa-arrow-down-short-wide::before { + content: "\f884"; } + +.fa-sort-amount-desc::before { + content: "\f884"; } + +.fa-sort-amount-down-alt::before { + content: "\f884"; } + +.fa-less-than::before { + content: "\3c"; } + +.fa-angle-down::before { + content: "\f107"; } + +.fa-car-tunnel::before { + content: "\e4de"; } + +.fa-head-side-cough::before { + content: "\e061"; } + +.fa-grip-lines::before { + content: "\f7a4"; } + +.fa-thumbs-down::before { + content: "\f165"; } + +.fa-user-lock::before { + content: "\f502"; } + +.fa-arrow-right-long::before { + content: "\f178"; } + +.fa-long-arrow-right::before { + content: "\f178"; } + +.fa-anchor-circle-xmark::before { + content: "\e4ac"; } + +.fa-ellipsis::before { + content: "\f141"; } + +.fa-ellipsis-h::before { + content: "\f141"; } + +.fa-chess-pawn::before { + content: "\f443"; } + +.fa-kit-medical::before { + content: "\f479"; } + +.fa-first-aid::before { + content: "\f479"; } + +.fa-person-through-window::before { + content: "\e5a9"; } + +.fa-toolbox::before { + content: "\f552"; } + +.fa-hands-holding-circle::before { + content: "\e4fb"; } + +.fa-bug::before { + content: "\f188"; } + +.fa-credit-card::before { + content: "\f09d"; } + +.fa-credit-card-alt::before { + content: "\f09d"; } + +.fa-car::before { + content: "\f1b9"; } + +.fa-automobile::before { + content: "\f1b9"; } + +.fa-hand-holding-hand::before { + content: "\e4f7"; } + +.fa-book-open-reader::before { + content: "\f5da"; } + +.fa-book-reader::before { + content: "\f5da"; } + +.fa-mountain-sun::before { + content: "\e52f"; } + +.fa-arrows-left-right-to-line::before { + content: "\e4ba"; } + +.fa-dice-d20::before { + content: "\f6cf"; } + +.fa-truck-droplet::before { + content: "\e58c"; } + +.fa-file-circle-xmark::before { + content: "\e5a1"; } + +.fa-temperature-arrow-up::before { + content: "\e040"; } + +.fa-temperature-up::before { + content: "\e040"; } + +.fa-medal::before { + content: "\f5a2"; } + +.fa-bed::before { + content: "\f236"; } + +.fa-square-h::before { + content: "\f0fd"; } + +.fa-h-square::before { + content: "\f0fd"; } + +.fa-podcast::before { + content: "\f2ce"; } + +.fa-temperature-full::before { + content: "\f2c7"; } + +.fa-temperature-4::before { + content: "\f2c7"; } + +.fa-thermometer-4::before { + content: "\f2c7"; } + +.fa-thermometer-full::before { + content: "\f2c7"; } + +.fa-bell::before { + content: "\f0f3"; } + +.fa-superscript::before { + content: "\f12b"; } + +.fa-plug-circle-xmark::before { + content: "\e560"; } + +.fa-star-of-life::before { + content: "\f621"; } + +.fa-phone-slash::before { + content: "\f3dd"; } + +.fa-paint-roller::before { + content: "\f5aa"; } + +.fa-handshake-angle::before { + content: "\f4c4"; } + +.fa-hands-helping::before { + content: "\f4c4"; } + +.fa-location-dot::before { + content: "\f3c5"; } + +.fa-map-marker-alt::before { + content: "\f3c5"; } + +.fa-file::before { + content: "\f15b"; } + +.fa-greater-than::before { + content: "\3e"; } + +.fa-person-swimming::before { + content: "\f5c4"; } + +.fa-swimmer::before { + content: "\f5c4"; } + +.fa-arrow-down::before { + content: "\f063"; } + +.fa-droplet::before { + content: "\f043"; } + +.fa-tint::before { + content: "\f043"; } + +.fa-eraser::before { + content: "\f12d"; } + +.fa-earth-americas::before { + content: "\f57d"; } + +.fa-earth::before { + content: "\f57d"; } + +.fa-earth-america::before { + content: "\f57d"; } + +.fa-globe-americas::before { + content: "\f57d"; } + +.fa-person-burst::before { + content: "\e53b"; } + +.fa-dove::before { + content: "\f4ba"; } + +.fa-battery-empty::before { + content: "\f244"; } + +.fa-battery-0::before { + content: "\f244"; } + +.fa-socks::before { + content: "\f696"; } + +.fa-inbox::before { + content: "\f01c"; } + +.fa-section::before { + content: "\e447"; } + +.fa-gauge-high::before { + content: "\f625"; } + +.fa-tachometer-alt::before { + content: "\f625"; } + +.fa-tachometer-alt-fast::before { + content: "\f625"; } + +.fa-envelope-open-text::before { + content: "\f658"; } + +.fa-hospital::before { + content: "\f0f8"; } + +.fa-hospital-alt::before { + content: "\f0f8"; } + +.fa-hospital-wide::before { + content: "\f0f8"; } + +.fa-wine-bottle::before { + content: "\f72f"; } + +.fa-chess-rook::before { + content: "\f447"; } + +.fa-bars-staggered::before { + content: "\f550"; } + +.fa-reorder::before { + content: "\f550"; } + +.fa-stream::before { + content: "\f550"; } + +.fa-dharmachakra::before { + content: "\f655"; } + +.fa-hotdog::before { + content: "\f80f"; } + +.fa-person-walking-with-cane::before { + content: "\f29d"; } + +.fa-blind::before { + content: "\f29d"; } + +.fa-drum::before { + content: "\f569"; } + +.fa-ice-cream::before { + content: "\f810"; } + +.fa-heart-circle-bolt::before { + content: "\e4fc"; } + +.fa-fax::before { + content: "\f1ac"; } + +.fa-paragraph::before { + content: "\f1dd"; } + +.fa-check-to-slot::before { + content: "\f772"; } + +.fa-vote-yea::before { + content: "\f772"; } + +.fa-star-half::before { + content: "\f089"; } + +.fa-boxes-stacked::before { + content: "\f468"; } + +.fa-boxes::before { + content: "\f468"; } + +.fa-boxes-alt::before { + content: "\f468"; } + +.fa-link::before { + content: "\f0c1"; } + +.fa-chain::before { + content: "\f0c1"; } + +.fa-ear-listen::before { + content: "\f2a2"; } + +.fa-assistive-listening-systems::before { + content: "\f2a2"; } + +.fa-tree-city::before { + content: "\e587"; } + +.fa-play::before { + content: "\f04b"; } + +.fa-font::before { + content: "\f031"; } + +.fa-rupiah-sign::before { + content: "\e23d"; } + +.fa-magnifying-glass::before { + content: "\f002"; } + +.fa-search::before { + content: "\f002"; } + +.fa-table-tennis-paddle-ball::before { + content: "\f45d"; } + +.fa-ping-pong-paddle-ball::before { + content: "\f45d"; } + +.fa-table-tennis::before { + content: "\f45d"; } + +.fa-person-dots-from-line::before { + content: "\f470"; } + +.fa-diagnoses::before { + content: "\f470"; } + +.fa-trash-can-arrow-up::before { + content: "\f82a"; } + +.fa-trash-restore-alt::before { + content: "\f82a"; } + +.fa-naira-sign::before { + content: "\e1f6"; } + +.fa-cart-arrow-down::before { + content: "\f218"; } + +.fa-walkie-talkie::before { + content: "\f8ef"; } + +.fa-file-pen::before { + content: "\f31c"; } + +.fa-file-edit::before { + content: "\f31c"; } + +.fa-receipt::before { + content: "\f543"; } + +.fa-square-pen::before { + content: "\f14b"; } + +.fa-pen-square::before { + content: "\f14b"; } + +.fa-pencil-square::before { + content: "\f14b"; } + +.fa-suitcase-rolling::before { + content: "\f5c1"; } + +.fa-person-circle-exclamation::before { + content: "\e53f"; } + +.fa-chevron-down::before { + content: "\f078"; } + +.fa-battery-full::before { + content: "\f240"; } + +.fa-battery::before { + content: "\f240"; } + +.fa-battery-5::before { + content: "\f240"; } + +.fa-skull-crossbones::before { + content: "\f714"; } + +.fa-code-compare::before { + content: "\e13a"; } + +.fa-list-ul::before { + content: "\f0ca"; } + +.fa-list-dots::before { + content: "\f0ca"; } + +.fa-school-lock::before { + content: "\e56f"; } + +.fa-tower-cell::before { + content: "\e585"; } + +.fa-down-long::before { + content: "\f309"; } + +.fa-long-arrow-alt-down::before { + content: "\f309"; } + +.fa-ranking-star::before { + content: "\e561"; } + +.fa-chess-king::before { + content: "\f43f"; } + +.fa-person-harassing::before { + content: "\e549"; } + +.fa-brazilian-real-sign::before { + content: "\e46c"; } + +.fa-landmark-dome::before { + content: "\f752"; } + +.fa-landmark-alt::before { + content: "\f752"; } + +.fa-arrow-up::before { + content: "\f062"; } + +.fa-tv::before { + content: "\f26c"; } + +.fa-television::before { + content: "\f26c"; } + +.fa-tv-alt::before { + content: "\f26c"; } + +.fa-shrimp::before { + content: "\e448"; } + +.fa-list-check::before { + content: "\f0ae"; } + +.fa-tasks::before { + content: "\f0ae"; } + +.fa-jug-detergent::before { + content: "\e519"; } + +.fa-circle-user::before { + content: "\f2bd"; } + +.fa-user-circle::before { + content: "\f2bd"; } + +.fa-user-shield::before { + content: "\f505"; } + +.fa-wind::before { + content: "\f72e"; } + +.fa-car-burst::before { + content: "\f5e1"; } + +.fa-car-crash::before { + content: "\f5e1"; } + +.fa-y::before { + content: "\59"; } + +.fa-person-snowboarding::before { + content: "\f7ce"; } + +.fa-snowboarding::before { + content: "\f7ce"; } + +.fa-truck-fast::before { + content: "\f48b"; } + +.fa-shipping-fast::before { + content: "\f48b"; } + +.fa-fish::before { + content: "\f578"; } + +.fa-user-graduate::before { + content: "\f501"; } + +.fa-circle-half-stroke::before { + content: "\f042"; } + +.fa-adjust::before { + content: "\f042"; } + +.fa-clapperboard::before { + content: "\e131"; } + +.fa-circle-radiation::before { + content: "\f7ba"; } + +.fa-radiation-alt::before { + content: "\f7ba"; } + +.fa-baseball::before { + content: "\f433"; } + +.fa-baseball-ball::before { + content: "\f433"; } + +.fa-jet-fighter-up::before { + content: "\e518"; } + +.fa-diagram-project::before { + content: "\f542"; } + +.fa-project-diagram::before { + content: "\f542"; } + +.fa-copy::before { + content: "\f0c5"; } + +.fa-volume-xmark::before { + content: "\f6a9"; } + +.fa-volume-mute::before { + content: "\f6a9"; } + +.fa-volume-times::before { + content: "\f6a9"; } + +.fa-hand-sparkles::before { + content: "\e05d"; } + +.fa-grip::before { + content: "\f58d"; } + +.fa-grip-horizontal::before { + content: "\f58d"; } + +.fa-share-from-square::before { + content: "\f14d"; } + +.fa-share-square::before { + content: "\f14d"; } + +.fa-child-combatant::before { + content: "\e4e0"; } + +.fa-child-rifle::before { + content: "\e4e0"; } + +.fa-gun::before { + content: "\e19b"; } + +.fa-square-phone::before { + content: "\f098"; } + +.fa-phone-square::before { + content: "\f098"; } + +.fa-plus::before { + content: "\2b"; } + +.fa-add::before { + content: "\2b"; } + +.fa-expand::before { + content: "\f065"; } + +.fa-computer::before { + content: "\e4e5"; } + +.fa-xmark::before { + content: "\f00d"; } + +.fa-close::before { + content: "\f00d"; } + +.fa-multiply::before { + content: "\f00d"; } + +.fa-remove::before { + content: "\f00d"; } + +.fa-times::before { + content: "\f00d"; } + +.fa-arrows-up-down-left-right::before { + content: "\f047"; } + +.fa-arrows::before { + content: "\f047"; } + +.fa-chalkboard-user::before { + content: "\f51c"; } + +.fa-chalkboard-teacher::before { + content: "\f51c"; } + +.fa-peso-sign::before { + content: "\e222"; } + +.fa-building-shield::before { + content: "\e4d8"; } + +.fa-baby::before { + content: "\f77c"; } + +.fa-users-line::before { + content: "\e592"; } + +.fa-quote-left::before { + content: "\f10d"; } + +.fa-quote-left-alt::before { + content: "\f10d"; } + +.fa-tractor::before { + content: "\f722"; } + +.fa-trash-arrow-up::before { + content: "\f829"; } + +.fa-trash-restore::before { + content: "\f829"; } + +.fa-arrow-down-up-lock::before { + content: "\e4b0"; } + +.fa-lines-leaning::before { + content: "\e51e"; } + +.fa-ruler-combined::before { + content: "\f546"; } + +.fa-copyright::before { + content: "\f1f9"; } + +.fa-equals::before { + content: "\3d"; } + +.fa-blender::before { + content: "\f517"; } + +.fa-teeth::before { + content: "\f62e"; } + +.fa-shekel-sign::before { + content: "\f20b"; } + +.fa-ils::before { + content: "\f20b"; } + +.fa-shekel::before { + content: "\f20b"; } + +.fa-sheqel::before { + content: "\f20b"; } + +.fa-sheqel-sign::before { + content: "\f20b"; } + +.fa-map::before { + content: "\f279"; } + +.fa-rocket::before { + content: "\f135"; } + +.fa-photo-film::before { + content: "\f87c"; } + +.fa-photo-video::before { + content: "\f87c"; } + +.fa-folder-minus::before { + content: "\f65d"; } + +.fa-store::before { + content: "\f54e"; } + +.fa-arrow-trend-up::before { + content: "\e098"; } + +.fa-plug-circle-minus::before { + content: "\e55e"; } + +.fa-sign-hanging::before { + content: "\f4d9"; } + +.fa-sign::before { + content: "\f4d9"; } + +.fa-bezier-curve::before { + content: "\f55b"; } + +.fa-bell-slash::before { + content: "\f1f6"; } + +.fa-tablet::before { + content: "\f3fb"; } + +.fa-tablet-android::before { + content: "\f3fb"; } + +.fa-school-flag::before { + content: "\e56e"; } + +.fa-fill::before { + content: "\f575"; } + +.fa-angle-up::before { + content: "\f106"; } + +.fa-drumstick-bite::before { + content: "\f6d7"; } + +.fa-holly-berry::before { + content: "\f7aa"; } + +.fa-chevron-left::before { + content: "\f053"; } + +.fa-bacteria::before { + content: "\e059"; } + +.fa-hand-lizard::before { + content: "\f258"; } + +.fa-notdef::before { + content: "\e1fe"; } + +.fa-disease::before { + content: "\f7fa"; } + +.fa-briefcase-medical::before { + content: "\f469"; } + +.fa-genderless::before { + content: "\f22d"; } + +.fa-chevron-right::before { + content: "\f054"; } + +.fa-retweet::before { + content: "\f079"; } + +.fa-car-rear::before { + content: "\f5de"; } + +.fa-car-alt::before { + content: "\f5de"; } + +.fa-pump-soap::before { + content: "\e06b"; } + +.fa-video-slash::before { + content: "\f4e2"; } + +.fa-battery-quarter::before { + content: "\f243"; } + +.fa-battery-2::before { + content: "\f243"; } + +.fa-radio::before { + content: "\f8d7"; } + +.fa-baby-carriage::before { + content: "\f77d"; } + +.fa-carriage-baby::before { + content: "\f77d"; } + +.fa-traffic-light::before { + content: "\f637"; } + +.fa-thermometer::before { + content: "\f491"; } + +.fa-vr-cardboard::before { + content: "\f729"; } + +.fa-hand-middle-finger::before { + content: "\f806"; } + +.fa-percent::before { + content: "\25"; } + +.fa-percentage::before { + content: "\25"; } + +.fa-truck-moving::before { + content: "\f4df"; } + +.fa-glass-water-droplet::before { + content: "\e4f5"; } + +.fa-display::before { + content: "\e163"; } + +.fa-face-smile::before { + content: "\f118"; } + +.fa-smile::before { + content: "\f118"; } + +.fa-thumbtack::before { + content: "\f08d"; } + +.fa-thumb-tack::before { + content: "\f08d"; } + +.fa-trophy::before { + content: "\f091"; } + +.fa-person-praying::before { + content: "\f683"; } + +.fa-pray::before { + content: "\f683"; } + +.fa-hammer::before { + content: "\f6e3"; } + +.fa-hand-peace::before { + content: "\f25b"; } + +.fa-rotate::before { + content: "\f2f1"; } + +.fa-sync-alt::before { + content: "\f2f1"; } + +.fa-spinner::before { + content: "\f110"; } + +.fa-robot::before { + content: "\f544"; } + +.fa-peace::before { + content: "\f67c"; } + +.fa-gears::before { + content: "\f085"; } + +.fa-cogs::before { + content: "\f085"; } + +.fa-warehouse::before { + content: "\f494"; } + +.fa-arrow-up-right-dots::before { + content: "\e4b7"; } + +.fa-splotch::before { + content: "\f5bc"; } + +.fa-face-grin-hearts::before { + content: "\f584"; } + +.fa-grin-hearts::before { + content: "\f584"; } + +.fa-dice-four::before { + content: "\f524"; } + +.fa-sim-card::before { + content: "\f7c4"; } + +.fa-transgender::before { + content: "\f225"; } + +.fa-transgender-alt::before { + content: "\f225"; } + +.fa-mercury::before { + content: "\f223"; } + +.fa-arrow-turn-down::before { + content: "\f149"; } + +.fa-level-down::before { + content: "\f149"; } + +.fa-person-falling-burst::before { + content: "\e547"; } + +.fa-award::before { + content: "\f559"; } + +.fa-ticket-simple::before { + content: "\f3ff"; } + +.fa-ticket-alt::before { + content: "\f3ff"; } + +.fa-building::before { + content: "\f1ad"; } + +.fa-angles-left::before { + content: "\f100"; } + +.fa-angle-double-left::before { + content: "\f100"; } + +.fa-qrcode::before { + content: "\f029"; } + +.fa-clock-rotate-left::before { + content: "\f1da"; } + +.fa-history::before { + content: "\f1da"; } + +.fa-face-grin-beam-sweat::before { + content: "\f583"; } + +.fa-grin-beam-sweat::before { + content: "\f583"; } + +.fa-file-export::before { + content: "\f56e"; } + +.fa-arrow-right-from-file::before { + content: "\f56e"; } + +.fa-shield::before { + content: "\f132"; } + +.fa-shield-blank::before { + content: "\f132"; } + +.fa-arrow-up-short-wide::before { + content: "\f885"; } + +.fa-sort-amount-up-alt::before { + content: "\f885"; } + +.fa-house-medical::before { + content: "\e3b2"; } + +.fa-golf-ball-tee::before { + content: "\f450"; } + +.fa-golf-ball::before { + content: "\f450"; } + +.fa-circle-chevron-left::before { + content: "\f137"; } + +.fa-chevron-circle-left::before { + content: "\f137"; } + +.fa-house-chimney-window::before { + content: "\e00d"; } + +.fa-pen-nib::before { + content: "\f5ad"; } + +.fa-tent-arrow-turn-left::before { + content: "\e580"; } + +.fa-tents::before { + content: "\e582"; } + +.fa-wand-magic::before { + content: "\f0d0"; } + +.fa-magic::before { + content: "\f0d0"; } + +.fa-dog::before { + content: "\f6d3"; } + +.fa-carrot::before { + content: "\f787"; } + +.fa-moon::before { + content: "\f186"; } + +.fa-wine-glass-empty::before { + content: "\f5ce"; } + +.fa-wine-glass-alt::before { + content: "\f5ce"; } + +.fa-cheese::before { + content: "\f7ef"; } + +.fa-yin-yang::before { + content: "\f6ad"; } + +.fa-music::before { + content: "\f001"; } + +.fa-code-commit::before { + content: "\f386"; } + +.fa-temperature-low::before { + content: "\f76b"; } + +.fa-person-biking::before { + content: "\f84a"; } + +.fa-biking::before { + content: "\f84a"; } + +.fa-broom::before { + content: "\f51a"; } + +.fa-shield-heart::before { + content: "\e574"; } + +.fa-gopuram::before { + content: "\f664"; } + +.fa-earth-oceania::before { + content: "\e47b"; } + +.fa-globe-oceania::before { + content: "\e47b"; } + +.fa-square-xmark::before { + content: "\f2d3"; } + +.fa-times-square::before { + content: "\f2d3"; } + +.fa-xmark-square::before { + content: "\f2d3"; } + +.fa-hashtag::before { + content: "\23"; } + +.fa-up-right-and-down-left-from-center::before { + content: "\f424"; } + +.fa-expand-alt::before { + content: "\f424"; } + +.fa-oil-can::before { + content: "\f613"; } + +.fa-t::before { + content: "\54"; } + +.fa-hippo::before { + content: "\f6ed"; } + +.fa-chart-column::before { + content: "\e0e3"; } + +.fa-infinity::before { + content: "\f534"; } + +.fa-vial-circle-check::before { + content: "\e596"; } + +.fa-person-arrow-down-to-line::before { + content: "\e538"; } + +.fa-voicemail::before { + content: "\f897"; } + +.fa-fan::before { + content: "\f863"; } + +.fa-person-walking-luggage::before { + content: "\e554"; } + +.fa-up-down::before { + content: "\f338"; } + +.fa-arrows-alt-v::before { + content: "\f338"; } + +.fa-cloud-moon-rain::before { + content: "\f73c"; } + +.fa-calendar::before { + content: "\f133"; } + +.fa-trailer::before { + content: "\e041"; } + +.fa-bahai::before { + content: "\f666"; } + +.fa-haykal::before { + content: "\f666"; } + +.fa-sd-card::before { + content: "\f7c2"; } + +.fa-dragon::before { + content: "\f6d5"; } + +.fa-shoe-prints::before { + content: "\f54b"; } + +.fa-circle-plus::before { + content: "\f055"; } + +.fa-plus-circle::before { + content: "\f055"; } + +.fa-face-grin-tongue-wink::before { + content: "\f58b"; } + +.fa-grin-tongue-wink::before { + content: "\f58b"; } + +.fa-hand-holding::before { + content: "\f4bd"; } + +.fa-plug-circle-exclamation::before { + content: "\e55d"; } + +.fa-link-slash::before { + content: "\f127"; } + +.fa-chain-broken::before { + content: "\f127"; } + +.fa-chain-slash::before { + content: "\f127"; } + +.fa-unlink::before { + content: "\f127"; } + +.fa-clone::before { + content: "\f24d"; } + +.fa-person-walking-arrow-loop-left::before { + content: "\e551"; } + +.fa-arrow-up-z-a::before { + content: "\f882"; } + +.fa-sort-alpha-up-alt::before { + content: "\f882"; } + +.fa-fire-flame-curved::before { + content: "\f7e4"; } + +.fa-fire-alt::before { + content: "\f7e4"; } + +.fa-tornado::before { + content: "\f76f"; } + +.fa-file-circle-plus::before { + content: "\e494"; } + +.fa-book-quran::before { + content: "\f687"; } + +.fa-quran::before { + content: "\f687"; } + +.fa-anchor::before { + content: "\f13d"; } + +.fa-border-all::before { + content: "\f84c"; } + +.fa-face-angry::before { + content: "\f556"; } + +.fa-angry::before { + content: "\f556"; } + +.fa-cookie-bite::before { + content: "\f564"; } + +.fa-arrow-trend-down::before { + content: "\e097"; } + +.fa-rss::before { + content: "\f09e"; } + +.fa-feed::before { + content: "\f09e"; } + +.fa-draw-polygon::before { + content: "\f5ee"; } + +.fa-scale-balanced::before { + content: "\f24e"; } + +.fa-balance-scale::before { + content: "\f24e"; } + +.fa-gauge-simple-high::before { + content: "\f62a"; } + +.fa-tachometer::before { + content: "\f62a"; } + +.fa-tachometer-fast::before { + content: "\f62a"; } + +.fa-shower::before { + content: "\f2cc"; } + +.fa-desktop::before { + content: "\f390"; } + +.fa-desktop-alt::before { + content: "\f390"; } + +.fa-m::before { + content: "\4d"; } + +.fa-table-list::before { + content: "\f00b"; } + +.fa-th-list::before { + content: "\f00b"; } + +.fa-comment-sms::before { + content: "\f7cd"; } + +.fa-sms::before { + content: "\f7cd"; } + +.fa-book::before { + content: "\f02d"; } + +.fa-user-plus::before { + content: "\f234"; } + +.fa-check::before { + content: "\f00c"; } + +.fa-battery-three-quarters::before { + content: "\f241"; } + +.fa-battery-4::before { + content: "\f241"; } + +.fa-house-circle-check::before { + content: "\e509"; } + +.fa-angle-left::before { + content: "\f104"; } + +.fa-diagram-successor::before { + content: "\e47a"; } + +.fa-truck-arrow-right::before { + content: "\e58b"; } + +.fa-arrows-split-up-and-left::before { + content: "\e4bc"; } + +.fa-hand-fist::before { + content: "\f6de"; } + +.fa-fist-raised::before { + content: "\f6de"; } + +.fa-cloud-moon::before { + content: "\f6c3"; } + +.fa-briefcase::before { + content: "\f0b1"; } + +.fa-person-falling::before { + content: "\e546"; } + +.fa-image-portrait::before { + content: "\f3e0"; } + +.fa-portrait::before { + content: "\f3e0"; } + +.fa-user-tag::before { + content: "\f507"; } + +.fa-rug::before { + content: "\e569"; } + +.fa-earth-europe::before { + content: "\f7a2"; } + +.fa-globe-europe::before { + content: "\f7a2"; } + +.fa-cart-flatbed-suitcase::before { + content: "\f59d"; } + +.fa-luggage-cart::before { + content: "\f59d"; } + +.fa-rectangle-xmark::before { + content: "\f410"; } + +.fa-rectangle-times::before { + content: "\f410"; } + +.fa-times-rectangle::before { + content: "\f410"; } + +.fa-window-close::before { + content: "\f410"; } + +.fa-baht-sign::before { + content: "\e0ac"; } + +.fa-book-open::before { + content: "\f518"; } + +.fa-book-journal-whills::before { + content: "\f66a"; } + +.fa-journal-whills::before { + content: "\f66a"; } + +.fa-handcuffs::before { + content: "\e4f8"; } + +.fa-triangle-exclamation::before { + content: "\f071"; } + +.fa-exclamation-triangle::before { + content: "\f071"; } + +.fa-warning::before { + content: "\f071"; } + +.fa-database::before { + content: "\f1c0"; } + +.fa-share::before { + content: "\f064"; } + +.fa-arrow-turn-right::before { + content: "\f064"; } + +.fa-mail-forward::before { + content: "\f064"; } + +.fa-bottle-droplet::before { + content: "\e4c4"; } + +.fa-mask-face::before { + content: "\e1d7"; } + +.fa-hill-rockslide::before { + content: "\e508"; } + +.fa-right-left::before { + content: "\f362"; } + +.fa-exchange-alt::before { + content: "\f362"; } + +.fa-paper-plane::before { + content: "\f1d8"; } + +.fa-road-circle-exclamation::before { + content: "\e565"; } + +.fa-dungeon::before { + content: "\f6d9"; } + +.fa-align-right::before { + content: "\f038"; } + +.fa-money-bill-1-wave::before { + content: "\f53b"; } + +.fa-money-bill-wave-alt::before { + content: "\f53b"; } + +.fa-life-ring::before { + content: "\f1cd"; } + +.fa-hands::before { + content: "\f2a7"; } + +.fa-sign-language::before { + content: "\f2a7"; } + +.fa-signing::before { + content: "\f2a7"; } + +.fa-calendar-day::before { + content: "\f783"; } + +.fa-water-ladder::before { + content: "\f5c5"; } + +.fa-ladder-water::before { + content: "\f5c5"; } + +.fa-swimming-pool::before { + content: "\f5c5"; } + +.fa-arrows-up-down::before { + content: "\f07d"; } + +.fa-arrows-v::before { + content: "\f07d"; } + +.fa-face-grimace::before { + content: "\f57f"; } + +.fa-grimace::before { + content: "\f57f"; } + +.fa-wheelchair-move::before { + content: "\e2ce"; } + +.fa-wheelchair-alt::before { + content: "\e2ce"; } + +.fa-turn-down::before { + content: "\f3be"; } + +.fa-level-down-alt::before { + content: "\f3be"; } + +.fa-person-walking-arrow-right::before { + content: "\e552"; } + +.fa-square-envelope::before { + content: "\f199"; } + +.fa-envelope-square::before { + content: "\f199"; } + +.fa-dice::before { + content: "\f522"; } + +.fa-bowling-ball::before { + content: "\f436"; } + +.fa-brain::before { + content: "\f5dc"; } + +.fa-bandage::before { + content: "\f462"; } + +.fa-band-aid::before { + content: "\f462"; } + +.fa-calendar-minus::before { + content: "\f272"; } + +.fa-circle-xmark::before { + content: "\f057"; } + +.fa-times-circle::before { + content: "\f057"; } + +.fa-xmark-circle::before { + content: "\f057"; } + +.fa-gifts::before { + content: "\f79c"; } + +.fa-hotel::before { + content: "\f594"; } + +.fa-earth-asia::before { + content: "\f57e"; } + +.fa-globe-asia::before { + content: "\f57e"; } + +.fa-id-card-clip::before { + content: "\f47f"; } + +.fa-id-card-alt::before { + content: "\f47f"; } + +.fa-magnifying-glass-plus::before { + content: "\f00e"; } + +.fa-search-plus::before { + content: "\f00e"; } + +.fa-thumbs-up::before { + content: "\f164"; } + +.fa-user-clock::before { + content: "\f4fd"; } + +.fa-hand-dots::before { + content: "\f461"; } + +.fa-allergies::before { + content: "\f461"; } + +.fa-file-invoice::before { + content: "\f570"; } + +.fa-window-minimize::before { + content: "\f2d1"; } + +.fa-mug-saucer::before { + content: "\f0f4"; } + +.fa-coffee::before { + content: "\f0f4"; } + +.fa-brush::before { + content: "\f55d"; } + +.fa-mask::before { + content: "\f6fa"; } + +.fa-magnifying-glass-minus::before { + content: "\f010"; } + +.fa-search-minus::before { + content: "\f010"; } + +.fa-ruler-vertical::before { + content: "\f548"; } + +.fa-user-large::before { + content: "\f406"; } + +.fa-user-alt::before { + content: "\f406"; } + +.fa-train-tram::before { + content: "\e5b4"; } + +.fa-user-nurse::before { + content: "\f82f"; } + +.fa-syringe::before { + content: "\f48e"; } + +.fa-cloud-sun::before { + content: "\f6c4"; } + +.fa-stopwatch-20::before { + content: "\e06f"; } + +.fa-square-full::before { + content: "\f45c"; } + +.fa-magnet::before { + content: "\f076"; } + +.fa-jar::before { + content: "\e516"; } + +.fa-note-sticky::before { + content: "\f249"; } + +.fa-sticky-note::before { + content: "\f249"; } + +.fa-bug-slash::before { + content: "\e490"; } + +.fa-arrow-up-from-water-pump::before { + content: "\e4b6"; } + +.fa-bone::before { + content: "\f5d7"; } + +.fa-user-injured::before { + content: "\f728"; } + +.fa-face-sad-tear::before { + content: "\f5b4"; } + +.fa-sad-tear::before { + content: "\f5b4"; } + +.fa-plane::before { + content: "\f072"; } + +.fa-tent-arrows-down::before { + content: "\e581"; } + +.fa-exclamation::before { + content: "\21"; } + +.fa-arrows-spin::before { + content: "\e4bb"; } + +.fa-print::before { + content: "\f02f"; } + +.fa-turkish-lira-sign::before { + content: "\e2bb"; } + +.fa-try::before { + content: "\e2bb"; } + +.fa-turkish-lira::before { + content: "\e2bb"; } + +.fa-dollar-sign::before { + content: "\24"; } + +.fa-dollar::before { + content: "\24"; } + +.fa-usd::before { + content: "\24"; } + +.fa-x::before { + content: "\58"; } + +.fa-magnifying-glass-dollar::before { + content: "\f688"; } + +.fa-search-dollar::before { + content: "\f688"; } + +.fa-users-gear::before { + content: "\f509"; } + +.fa-users-cog::before { + content: "\f509"; } + +.fa-person-military-pointing::before { + content: "\e54a"; } + +.fa-building-columns::before { + content: "\f19c"; } + +.fa-bank::before { + content: "\f19c"; } + +.fa-institution::before { + content: "\f19c"; } + +.fa-museum::before { + content: "\f19c"; } + +.fa-university::before { + content: "\f19c"; } + +.fa-umbrella::before { + content: "\f0e9"; } + +.fa-trowel::before { + content: "\e589"; } + +.fa-d::before { + content: "\44"; } + +.fa-stapler::before { + content: "\e5af"; } + +.fa-masks-theater::before { + content: "\f630"; } + +.fa-theater-masks::before { + content: "\f630"; } + +.fa-kip-sign::before { + content: "\e1c4"; } + +.fa-hand-point-left::before { + content: "\f0a5"; } + +.fa-handshake-simple::before { + content: "\f4c6"; } + +.fa-handshake-alt::before { + content: "\f4c6"; } + +.fa-jet-fighter::before { + content: "\f0fb"; } + +.fa-fighter-jet::before { + content: "\f0fb"; } + +.fa-square-share-nodes::before { + content: "\f1e1"; } + +.fa-share-alt-square::before { + content: "\f1e1"; } + +.fa-barcode::before { + content: "\f02a"; } + +.fa-plus-minus::before { + content: "\e43c"; } + +.fa-video::before { + content: "\f03d"; } + +.fa-video-camera::before { + content: "\f03d"; } + +.fa-graduation-cap::before { + content: "\f19d"; } + +.fa-mortar-board::before { + content: "\f19d"; } + +.fa-hand-holding-medical::before { + content: "\e05c"; } + +.fa-person-circle-check::before { + content: "\e53e"; } + +.fa-turn-up::before { + content: "\f3bf"; } + +.fa-level-up-alt::before { + content: "\f3bf"; } + +.sr-only, +.fa-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } + +.sr-only-focusable:not(:focus), +.fa-sr-only-focusable:not(:focus) { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } +:root, :host { + --fa-style-family-brands: 'Font Awesome 6 Brands'; + --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } + +@font-face { + font-family: 'Font Awesome 6 Brands'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } + +.fab, +.fa-brands { + font-weight: 400; } + +.fa-monero:before { + content: "\f3d0"; } + +.fa-hooli:before { + content: "\f427"; } + +.fa-yelp:before { + content: "\f1e9"; } + +.fa-cc-visa:before { + content: "\f1f0"; } + +.fa-lastfm:before { + content: "\f202"; } + +.fa-shopware:before { + content: "\f5b5"; } + +.fa-creative-commons-nc:before { + content: "\f4e8"; } + +.fa-aws:before { + content: "\f375"; } + +.fa-redhat:before { + content: "\f7bc"; } + +.fa-yoast:before { + content: "\f2b1"; } + +.fa-cloudflare:before { + content: "\e07d"; } + +.fa-ups:before { + content: "\f7e0"; } + +.fa-wpexplorer:before { + content: "\f2de"; } + +.fa-dyalog:before { + content: "\f399"; } + +.fa-bity:before { + content: "\f37a"; } + +.fa-stackpath:before { + content: "\f842"; } + +.fa-buysellads:before { + content: "\f20d"; } + +.fa-first-order:before { + content: "\f2b0"; } + +.fa-modx:before { + content: "\f285"; } + +.fa-guilded:before { + content: "\e07e"; } + +.fa-vnv:before { + content: "\f40b"; } + +.fa-square-js:before { + content: "\f3b9"; } + +.fa-js-square:before { + content: "\f3b9"; } + +.fa-microsoft:before { + content: "\f3ca"; } + +.fa-qq:before { + content: "\f1d6"; } + +.fa-orcid:before { + content: "\f8d2"; } + +.fa-java:before { + content: "\f4e4"; } + +.fa-invision:before { + content: "\f7b0"; } + +.fa-creative-commons-pd-alt:before { + content: "\f4ed"; } + +.fa-centercode:before { + content: "\f380"; } + +.fa-glide-g:before { + content: "\f2a6"; } + +.fa-drupal:before { + content: "\f1a9"; } + +.fa-hire-a-helper:before { + content: "\f3b0"; } + +.fa-creative-commons-by:before { + content: "\f4e7"; } + +.fa-unity:before { + content: "\e049"; } + +.fa-whmcs:before { + content: "\f40d"; } + +.fa-rocketchat:before { + content: "\f3e8"; } + +.fa-vk:before { + content: "\f189"; } + +.fa-untappd:before { + content: "\f405"; } + +.fa-mailchimp:before { + content: "\f59e"; } + +.fa-css3-alt:before { + content: "\f38b"; } + +.fa-square-reddit:before { + content: "\f1a2"; } + +.fa-reddit-square:before { + content: "\f1a2"; } + +.fa-vimeo-v:before { + content: "\f27d"; } + +.fa-contao:before { + content: "\f26d"; } + +.fa-square-font-awesome:before { + content: "\e5ad"; } + +.fa-deskpro:before { + content: "\f38f"; } + +.fa-sistrix:before { + content: "\f3ee"; } + +.fa-square-instagram:before { + content: "\e055"; } + +.fa-instagram-square:before { + content: "\e055"; } + +.fa-battle-net:before { + content: "\f835"; } + +.fa-the-red-yeti:before { + content: "\f69d"; } + +.fa-square-hacker-news:before { + content: "\f3af"; } + +.fa-hacker-news-square:before { + content: "\f3af"; } + +.fa-edge:before { + content: "\f282"; } + +.fa-threads:before { + content: "\e618"; } + +.fa-napster:before { + content: "\f3d2"; } + +.fa-square-snapchat:before { + content: "\f2ad"; } + +.fa-snapchat-square:before { + content: "\f2ad"; } + +.fa-google-plus-g:before { + content: "\f0d5"; } + +.fa-artstation:before { + content: "\f77a"; } + +.fa-markdown:before { + content: "\f60f"; } + +.fa-sourcetree:before { + content: "\f7d3"; } + +.fa-google-plus:before { + content: "\f2b3"; } + +.fa-diaspora:before { + content: "\f791"; } + +.fa-foursquare:before { + content: "\f180"; } + +.fa-stack-overflow:before { + content: "\f16c"; } + +.fa-github-alt:before { + content: "\f113"; } + +.fa-phoenix-squadron:before { + content: "\f511"; } + +.fa-pagelines:before { + content: "\f18c"; } + +.fa-algolia:before { + content: "\f36c"; } + +.fa-red-river:before { + content: "\f3e3"; } + +.fa-creative-commons-sa:before { + content: "\f4ef"; } + +.fa-safari:before { + content: "\f267"; } + +.fa-google:before { + content: "\f1a0"; } + +.fa-square-font-awesome-stroke:before { + content: "\f35c"; } + +.fa-font-awesome-alt:before { + content: "\f35c"; } + +.fa-atlassian:before { + content: "\f77b"; } + +.fa-linkedin-in:before { + content: "\f0e1"; } + +.fa-digital-ocean:before { + content: "\f391"; } + +.fa-nimblr:before { + content: "\f5a8"; } + +.fa-chromecast:before { + content: "\f838"; } + +.fa-evernote:before { + content: "\f839"; } + +.fa-hacker-news:before { + content: "\f1d4"; } + +.fa-creative-commons-sampling:before { + content: "\f4f0"; } + +.fa-adversal:before { + content: "\f36a"; } + +.fa-creative-commons:before { + content: "\f25e"; } + +.fa-watchman-monitoring:before { + content: "\e087"; } + +.fa-fonticons:before { + content: "\f280"; } + +.fa-weixin:before { + content: "\f1d7"; } + +.fa-shirtsinbulk:before { + content: "\f214"; } + +.fa-codepen:before { + content: "\f1cb"; } + +.fa-git-alt:before { + content: "\f841"; } + +.fa-lyft:before { + content: "\f3c3"; } + +.fa-rev:before { + content: "\f5b2"; } + +.fa-windows:before { + content: "\f17a"; } + +.fa-wizards-of-the-coast:before { + content: "\f730"; } + +.fa-square-viadeo:before { + content: "\f2aa"; } + +.fa-viadeo-square:before { + content: "\f2aa"; } + +.fa-meetup:before { + content: "\f2e0"; } + +.fa-centos:before { + content: "\f789"; } + +.fa-adn:before { + content: "\f170"; } + +.fa-cloudsmith:before { + content: "\f384"; } + +.fa-pied-piper-alt:before { + content: "\f1a8"; } + +.fa-square-dribbble:before { + content: "\f397"; } + +.fa-dribbble-square:before { + content: "\f397"; } + +.fa-codiepie:before { + content: "\f284"; } + +.fa-node:before { + content: "\f419"; } + +.fa-mix:before { + content: "\f3cb"; } + +.fa-steam:before { + content: "\f1b6"; } + +.fa-cc-apple-pay:before { + content: "\f416"; } + +.fa-scribd:before { + content: "\f28a"; } + +.fa-debian:before { + content: "\e60b"; } + +.fa-openid:before { + content: "\f19b"; } + +.fa-instalod:before { + content: "\e081"; } + +.fa-expeditedssl:before { + content: "\f23e"; } + +.fa-sellcast:before { + content: "\f2da"; } + +.fa-square-twitter:before { + content: "\f081"; } + +.fa-twitter-square:before { + content: "\f081"; } + +.fa-r-project:before { + content: "\f4f7"; } + +.fa-delicious:before { + content: "\f1a5"; } + +.fa-freebsd:before { + content: "\f3a4"; } + +.fa-vuejs:before { + content: "\f41f"; } + +.fa-accusoft:before { + content: "\f369"; } + +.fa-ioxhost:before { + content: "\f208"; } + +.fa-fonticons-fi:before { + content: "\f3a2"; } + +.fa-app-store:before { + content: "\f36f"; } + +.fa-cc-mastercard:before { + content: "\f1f1"; } + +.fa-itunes-note:before { + content: "\f3b5"; } + +.fa-golang:before { + content: "\e40f"; } + +.fa-kickstarter:before { + content: "\f3bb"; } + +.fa-grav:before { + content: "\f2d6"; } + +.fa-weibo:before { + content: "\f18a"; } + +.fa-uncharted:before { + content: "\e084"; } + +.fa-firstdraft:before { + content: "\f3a1"; } + +.fa-square-youtube:before { + content: "\f431"; } + +.fa-youtube-square:before { + content: "\f431"; } + +.fa-wikipedia-w:before { + content: "\f266"; } + +.fa-wpressr:before { + content: "\f3e4"; } + +.fa-rendact:before { + content: "\f3e4"; } + +.fa-angellist:before { + content: "\f209"; } + +.fa-galactic-republic:before { + content: "\f50c"; } + +.fa-nfc-directional:before { + content: "\e530"; } + +.fa-skype:before { + content: "\f17e"; } + +.fa-joget:before { + content: "\f3b7"; } + +.fa-fedora:before { + content: "\f798"; } + +.fa-stripe-s:before { + content: "\f42a"; } + +.fa-meta:before { + content: "\e49b"; } + +.fa-laravel:before { + content: "\f3bd"; } + +.fa-hotjar:before { + content: "\f3b1"; } + +.fa-bluetooth-b:before { + content: "\f294"; } + +.fa-sticker-mule:before { + content: "\f3f7"; } + +.fa-creative-commons-zero:before { + content: "\f4f3"; } + +.fa-hips:before { + content: "\f452"; } + +.fa-behance:before { + content: "\f1b4"; } + +.fa-reddit:before { + content: "\f1a1"; } + +.fa-discord:before { + content: "\f392"; } + +.fa-chrome:before { + content: "\f268"; } + +.fa-app-store-ios:before { + content: "\f370"; } + +.fa-cc-discover:before { + content: "\f1f2"; } + +.fa-wpbeginner:before { + content: "\f297"; } + +.fa-confluence:before { + content: "\f78d"; } + +.fa-mdb:before { + content: "\f8ca"; } + +.fa-dochub:before { + content: "\f394"; } + +.fa-accessible-icon:before { + content: "\f368"; } + +.fa-ebay:before { + content: "\f4f4"; } + +.fa-amazon:before { + content: "\f270"; } + +.fa-unsplash:before { + content: "\e07c"; } + +.fa-yarn:before { + content: "\f7e3"; } + +.fa-square-steam:before { + content: "\f1b7"; } + +.fa-steam-square:before { + content: "\f1b7"; } + +.fa-500px:before { + content: "\f26e"; } + +.fa-square-vimeo:before { + content: "\f194"; } + +.fa-vimeo-square:before { + content: "\f194"; } + +.fa-asymmetrik:before { + content: "\f372"; } + +.fa-font-awesome:before { + content: "\f2b4"; } + +.fa-font-awesome-flag:before { + content: "\f2b4"; } + +.fa-font-awesome-logo-full:before { + content: "\f2b4"; } + +.fa-gratipay:before { + content: "\f184"; } + +.fa-apple:before { + content: "\f179"; } + +.fa-hive:before { + content: "\e07f"; } + +.fa-gitkraken:before { + content: "\f3a6"; } + +.fa-keybase:before { + content: "\f4f5"; } + +.fa-apple-pay:before { + content: "\f415"; } + +.fa-padlet:before { + content: "\e4a0"; } + +.fa-amazon-pay:before { + content: "\f42c"; } + +.fa-square-github:before { + content: "\f092"; } + +.fa-github-square:before { + content: "\f092"; } + +.fa-stumbleupon:before { + content: "\f1a4"; } + +.fa-fedex:before { + content: "\f797"; } + +.fa-phoenix-framework:before { + content: "\f3dc"; } + +.fa-shopify:before { + content: "\e057"; } + +.fa-neos:before { + content: "\f612"; } + +.fa-square-threads:before { + content: "\e619"; } + +.fa-hackerrank:before { + content: "\f5f7"; } + +.fa-researchgate:before { + content: "\f4f8"; } + +.fa-swift:before { + content: "\f8e1"; } + +.fa-angular:before { + content: "\f420"; } + +.fa-speakap:before { + content: "\f3f3"; } + +.fa-angrycreative:before { + content: "\f36e"; } + +.fa-y-combinator:before { + content: "\f23b"; } + +.fa-empire:before { + content: "\f1d1"; } + +.fa-envira:before { + content: "\f299"; } + +.fa-square-gitlab:before { + content: "\e5ae"; } + +.fa-gitlab-square:before { + content: "\e5ae"; } + +.fa-studiovinari:before { + content: "\f3f8"; } + +.fa-pied-piper:before { + content: "\f2ae"; } + +.fa-wordpress:before { + content: "\f19a"; } + +.fa-product-hunt:before { + content: "\f288"; } + +.fa-firefox:before { + content: "\f269"; } + +.fa-linode:before { + content: "\f2b8"; } + +.fa-goodreads:before { + content: "\f3a8"; } + +.fa-square-odnoklassniki:before { + content: "\f264"; } + +.fa-odnoklassniki-square:before { + content: "\f264"; } + +.fa-jsfiddle:before { + content: "\f1cc"; } + +.fa-sith:before { + content: "\f512"; } + +.fa-themeisle:before { + content: "\f2b2"; } + +.fa-page4:before { + content: "\f3d7"; } + +.fa-hashnode:before { + content: "\e499"; } + +.fa-react:before { + content: "\f41b"; } + +.fa-cc-paypal:before { + content: "\f1f4"; } + +.fa-squarespace:before { + content: "\f5be"; } + +.fa-cc-stripe:before { + content: "\f1f5"; } + +.fa-creative-commons-share:before { + content: "\f4f2"; } + +.fa-bitcoin:before { + content: "\f379"; } + +.fa-keycdn:before { + content: "\f3ba"; } + +.fa-opera:before { + content: "\f26a"; } + +.fa-itch-io:before { + content: "\f83a"; } + +.fa-umbraco:before { + content: "\f8e8"; } + +.fa-galactic-senate:before { + content: "\f50d"; } + +.fa-ubuntu:before { + content: "\f7df"; } + +.fa-draft2digital:before { + content: "\f396"; } + +.fa-stripe:before { + content: "\f429"; } + +.fa-houzz:before { + content: "\f27c"; } + +.fa-gg:before { + content: "\f260"; } + +.fa-dhl:before { + content: "\f790"; } + +.fa-square-pinterest:before { + content: "\f0d3"; } + +.fa-pinterest-square:before { + content: "\f0d3"; } + +.fa-xing:before { + content: "\f168"; } + +.fa-blackberry:before { + content: "\f37b"; } + +.fa-creative-commons-pd:before { + content: "\f4ec"; } + +.fa-playstation:before { + content: "\f3df"; } + +.fa-quinscape:before { + content: "\f459"; } + +.fa-less:before { + content: "\f41d"; } + +.fa-blogger-b:before { + content: "\f37d"; } + +.fa-opencart:before { + content: "\f23d"; } + +.fa-vine:before { + content: "\f1ca"; } + +.fa-paypal:before { + content: "\f1ed"; } + +.fa-gitlab:before { + content: "\f296"; } + +.fa-typo3:before { + content: "\f42b"; } + +.fa-reddit-alien:before { + content: "\f281"; } + +.fa-yahoo:before { + content: "\f19e"; } + +.fa-dailymotion:before { + content: "\e052"; } + +.fa-affiliatetheme:before { + content: "\f36b"; } + +.fa-pied-piper-pp:before { + content: "\f1a7"; } + +.fa-bootstrap:before { + content: "\f836"; } + +.fa-odnoklassniki:before { + content: "\f263"; } + +.fa-nfc-symbol:before { + content: "\e531"; } + +.fa-ethereum:before { + content: "\f42e"; } + +.fa-speaker-deck:before { + content: "\f83c"; } + +.fa-creative-commons-nc-eu:before { + content: "\f4e9"; } + +.fa-patreon:before { + content: "\f3d9"; } + +.fa-avianex:before { + content: "\f374"; } + +.fa-ello:before { + content: "\f5f1"; } + +.fa-gofore:before { + content: "\f3a7"; } + +.fa-bimobject:before { + content: "\f378"; } + +.fa-facebook-f:before { + content: "\f39e"; } + +.fa-square-google-plus:before { + content: "\f0d4"; } + +.fa-google-plus-square:before { + content: "\f0d4"; } + +.fa-mandalorian:before { + content: "\f50f"; } + +.fa-first-order-alt:before { + content: "\f50a"; } + +.fa-osi:before { + content: "\f41a"; } + +.fa-google-wallet:before { + content: "\f1ee"; } + +.fa-d-and-d-beyond:before { + content: "\f6ca"; } + +.fa-periscope:before { + content: "\f3da"; } + +.fa-fulcrum:before { + content: "\f50b"; } + +.fa-cloudscale:before { + content: "\f383"; } + +.fa-forumbee:before { + content: "\f211"; } + +.fa-mizuni:before { + content: "\f3cc"; } + +.fa-schlix:before { + content: "\f3ea"; } + +.fa-square-xing:before { + content: "\f169"; } + +.fa-xing-square:before { + content: "\f169"; } + +.fa-bandcamp:before { + content: "\f2d5"; } + +.fa-wpforms:before { + content: "\f298"; } + +.fa-cloudversify:before { + content: "\f385"; } + +.fa-usps:before { + content: "\f7e1"; } + +.fa-megaport:before { + content: "\f5a3"; } + +.fa-magento:before { + content: "\f3c4"; } + +.fa-spotify:before { + content: "\f1bc"; } + +.fa-optin-monster:before { + content: "\f23c"; } + +.fa-fly:before { + content: "\f417"; } + +.fa-aviato:before { + content: "\f421"; } + +.fa-itunes:before { + content: "\f3b4"; } + +.fa-cuttlefish:before { + content: "\f38c"; } + +.fa-blogger:before { + content: "\f37c"; } + +.fa-flickr:before { + content: "\f16e"; } + +.fa-viber:before { + content: "\f409"; } + +.fa-soundcloud:before { + content: "\f1be"; } + +.fa-digg:before { + content: "\f1a6"; } + +.fa-tencent-weibo:before { + content: "\f1d5"; } + +.fa-symfony:before { + content: "\f83d"; } + +.fa-maxcdn:before { + content: "\f136"; } + +.fa-etsy:before { + content: "\f2d7"; } + +.fa-facebook-messenger:before { + content: "\f39f"; } + +.fa-audible:before { + content: "\f373"; } + +.fa-think-peaks:before { + content: "\f731"; } + +.fa-bilibili:before { + content: "\e3d9"; } + +.fa-erlang:before { + content: "\f39d"; } + +.fa-x-twitter:before { + content: "\e61b"; } + +.fa-cotton-bureau:before { + content: "\f89e"; } + +.fa-dashcube:before { + content: "\f210"; } + +.fa-42-group:before { + content: "\e080"; } + +.fa-innosoft:before { + content: "\e080"; } + +.fa-stack-exchange:before { + content: "\f18d"; } + +.fa-elementor:before { + content: "\f430"; } + +.fa-square-pied-piper:before { + content: "\e01e"; } + +.fa-pied-piper-square:before { + content: "\e01e"; } + +.fa-creative-commons-nd:before { + content: "\f4eb"; } + +.fa-palfed:before { + content: "\f3d8"; } + +.fa-superpowers:before { + content: "\f2dd"; } + +.fa-resolving:before { + content: "\f3e7"; } + +.fa-xbox:before { + content: "\f412"; } + +.fa-searchengin:before { + content: "\f3eb"; } + +.fa-tiktok:before { + content: "\e07b"; } + +.fa-square-facebook:before { + content: "\f082"; } + +.fa-facebook-square:before { + content: "\f082"; } + +.fa-renren:before { + content: "\f18b"; } + +.fa-linux:before { + content: "\f17c"; } + +.fa-glide:before { + content: "\f2a5"; } + +.fa-linkedin:before { + content: "\f08c"; } + +.fa-hubspot:before { + content: "\f3b2"; } + +.fa-deploydog:before { + content: "\f38e"; } + +.fa-twitch:before { + content: "\f1e8"; } + +.fa-ravelry:before { + content: "\f2d9"; } + +.fa-mixer:before { + content: "\e056"; } + +.fa-square-lastfm:before { + content: "\f203"; } + +.fa-lastfm-square:before { + content: "\f203"; } + +.fa-vimeo:before { + content: "\f40a"; } + +.fa-mendeley:before { + content: "\f7b3"; } + +.fa-uniregistry:before { + content: "\f404"; } + +.fa-figma:before { + content: "\f799"; } + +.fa-creative-commons-remix:before { + content: "\f4ee"; } + +.fa-cc-amazon-pay:before { + content: "\f42d"; } + +.fa-dropbox:before { + content: "\f16b"; } + +.fa-instagram:before { + content: "\f16d"; } + +.fa-cmplid:before { + content: "\e360"; } + +.fa-facebook:before { + content: "\f09a"; } + +.fa-gripfire:before { + content: "\f3ac"; } + +.fa-jedi-order:before { + content: "\f50e"; } + +.fa-uikit:before { + content: "\f403"; } + +.fa-fort-awesome-alt:before { + content: "\f3a3"; } + +.fa-phabricator:before { + content: "\f3db"; } + +.fa-ussunnah:before { + content: "\f407"; } + +.fa-earlybirds:before { + content: "\f39a"; } + +.fa-trade-federation:before { + content: "\f513"; } + +.fa-autoprefixer:before { + content: "\f41c"; } + +.fa-whatsapp:before { + content: "\f232"; } + +.fa-slideshare:before { + content: "\f1e7"; } + +.fa-google-play:before { + content: "\f3ab"; } + +.fa-viadeo:before { + content: "\f2a9"; } + +.fa-line:before { + content: "\f3c0"; } + +.fa-google-drive:before { + content: "\f3aa"; } + +.fa-servicestack:before { + content: "\f3ec"; } + +.fa-simplybuilt:before { + content: "\f215"; } + +.fa-bitbucket:before { + content: "\f171"; } + +.fa-imdb:before { + content: "\f2d8"; } + +.fa-deezer:before { + content: "\e077"; } + +.fa-raspberry-pi:before { + content: "\f7bb"; } + +.fa-jira:before { + content: "\f7b1"; } + +.fa-docker:before { + content: "\f395"; } + +.fa-screenpal:before { + content: "\e570"; } + +.fa-bluetooth:before { + content: "\f293"; } + +.fa-gitter:before { + content: "\f426"; } + +.fa-d-and-d:before { + content: "\f38d"; } + +.fa-microblog:before { + content: "\e01a"; } + +.fa-cc-diners-club:before { + content: "\f24c"; } + +.fa-gg-circle:before { + content: "\f261"; } + +.fa-pied-piper-hat:before { + content: "\f4e5"; } + +.fa-kickstarter-k:before { + content: "\f3bc"; } + +.fa-yandex:before { + content: "\f413"; } + +.fa-readme:before { + content: "\f4d5"; } + +.fa-html5:before { + content: "\f13b"; } + +.fa-sellsy:before { + content: "\f213"; } + +.fa-sass:before { + content: "\f41e"; } + +.fa-wirsindhandwerk:before { + content: "\e2d0"; } + +.fa-wsh:before { + content: "\e2d0"; } + +.fa-buromobelexperte:before { + content: "\f37f"; } + +.fa-salesforce:before { + content: "\f83b"; } + +.fa-octopus-deploy:before { + content: "\e082"; } + +.fa-medapps:before { + content: "\f3c6"; } + +.fa-ns8:before { + content: "\f3d5"; } + +.fa-pinterest-p:before { + content: "\f231"; } + +.fa-apper:before { + content: "\f371"; } + +.fa-fort-awesome:before { + content: "\f286"; } + +.fa-waze:before { + content: "\f83f"; } + +.fa-cc-jcb:before { + content: "\f24b"; } + +.fa-snapchat:before { + content: "\f2ab"; } + +.fa-snapchat-ghost:before { + content: "\f2ab"; } + +.fa-fantasy-flight-games:before { + content: "\f6dc"; } + +.fa-rust:before { + content: "\e07a"; } + +.fa-wix:before { + content: "\f5cf"; } + +.fa-square-behance:before { + content: "\f1b5"; } + +.fa-behance-square:before { + content: "\f1b5"; } + +.fa-supple:before { + content: "\f3f9"; } + +.fa-rebel:before { + content: "\f1d0"; } + +.fa-css3:before { + content: "\f13c"; } + +.fa-staylinked:before { + content: "\f3f5"; } + +.fa-kaggle:before { + content: "\f5fa"; } + +.fa-space-awesome:before { + content: "\e5ac"; } + +.fa-deviantart:before { + content: "\f1bd"; } + +.fa-cpanel:before { + content: "\f388"; } + +.fa-goodreads-g:before { + content: "\f3a9"; } + +.fa-square-git:before { + content: "\f1d2"; } + +.fa-git-square:before { + content: "\f1d2"; } + +.fa-square-tumblr:before { + content: "\f174"; } + +.fa-tumblr-square:before { + content: "\f174"; } + +.fa-trello:before { + content: "\f181"; } + +.fa-creative-commons-nc-jp:before { + content: "\f4ea"; } + +.fa-get-pocket:before { + content: "\f265"; } + +.fa-perbyte:before { + content: "\e083"; } + +.fa-grunt:before { + content: "\f3ad"; } + +.fa-weebly:before { + content: "\f5cc"; } + +.fa-connectdevelop:before { + content: "\f20e"; } + +.fa-leanpub:before { + content: "\f212"; } + +.fa-black-tie:before { + content: "\f27e"; } + +.fa-themeco:before { + content: "\f5c6"; } + +.fa-python:before { + content: "\f3e2"; } + +.fa-android:before { + content: "\f17b"; } + +.fa-bots:before { + content: "\e340"; } + +.fa-free-code-camp:before { + content: "\f2c5"; } + +.fa-hornbill:before { + content: "\f592"; } + +.fa-js:before { + content: "\f3b8"; } + +.fa-ideal:before { + content: "\e013"; } + +.fa-git:before { + content: "\f1d3"; } + +.fa-dev:before { + content: "\f6cc"; } + +.fa-sketch:before { + content: "\f7c6"; } + +.fa-yandex-international:before { + content: "\f414"; } + +.fa-cc-amex:before { + content: "\f1f3"; } + +.fa-uber:before { + content: "\f402"; } + +.fa-github:before { + content: "\f09b"; } + +.fa-php:before { + content: "\f457"; } + +.fa-alipay:before { + content: "\f642"; } + +.fa-youtube:before { + content: "\f167"; } + +.fa-skyatlas:before { + content: "\f216"; } + +.fa-firefox-browser:before { + content: "\e007"; } + +.fa-replyd:before { + content: "\f3e6"; } + +.fa-suse:before { + content: "\f7d6"; } + +.fa-jenkins:before { + content: "\f3b6"; } + +.fa-twitter:before { + content: "\f099"; } + +.fa-rockrms:before { + content: "\f3e9"; } + +.fa-pinterest:before { + content: "\f0d2"; } + +.fa-buffer:before { + content: "\f837"; } + +.fa-npm:before { + content: "\f3d4"; } + +.fa-yammer:before { + content: "\f840"; } + +.fa-btc:before { + content: "\f15a"; } + +.fa-dribbble:before { + content: "\f17d"; } + +.fa-stumbleupon-circle:before { + content: "\f1a3"; } + +.fa-internet-explorer:before { + content: "\f26b"; } + +.fa-stubber:before { + content: "\e5c7"; } + +.fa-telegram:before { + content: "\f2c6"; } + +.fa-telegram-plane:before { + content: "\f2c6"; } + +.fa-old-republic:before { + content: "\f510"; } + +.fa-odysee:before { + content: "\e5c6"; } + +.fa-square-whatsapp:before { + content: "\f40c"; } + +.fa-whatsapp-square:before { + content: "\f40c"; } + +.fa-node-js:before { + content: "\f3d3"; } + +.fa-edge-legacy:before { + content: "\e078"; } + +.fa-slack:before { + content: "\f198"; } + +.fa-slack-hash:before { + content: "\f198"; } + +.fa-medrt:before { + content: "\f3c8"; } + +.fa-usb:before { + content: "\f287"; } + +.fa-tumblr:before { + content: "\f173"; } + +.fa-vaadin:before { + content: "\f408"; } + +.fa-quora:before { + content: "\f2c4"; } + +.fa-square-x-twitter:before { + content: "\e61a"; } + +.fa-reacteurope:before { + content: "\f75d"; } + +.fa-medium:before { + content: "\f23a"; } + +.fa-medium-m:before { + content: "\f23a"; } + +.fa-amilia:before { + content: "\f36d"; } + +.fa-mixcloud:before { + content: "\f289"; } + +.fa-flipboard:before { + content: "\f44d"; } + +.fa-viacoin:before { + content: "\f237"; } + +.fa-critical-role:before { + content: "\f6c9"; } + +.fa-sitrox:before { + content: "\e44a"; } + +.fa-discourse:before { + content: "\f393"; } + +.fa-joomla:before { + content: "\f1aa"; } + +.fa-mastodon:before { + content: "\f4f6"; } + +.fa-airbnb:before { + content: "\f834"; } + +.fa-wolf-pack-battalion:before { + content: "\f514"; } + +.fa-buy-n-large:before { + content: "\f8a6"; } + +.fa-gulp:before { + content: "\f3ae"; } + +.fa-creative-commons-sampling-plus:before { + content: "\f4f1"; } + +.fa-strava:before { + content: "\f428"; } + +.fa-ember:before { + content: "\f423"; } + +.fa-canadian-maple-leaf:before { + content: "\f785"; } + +.fa-teamspeak:before { + content: "\f4f9"; } + +.fa-pushed:before { + content: "\f3e1"; } + +.fa-wordpress-simple:before { + content: "\f411"; } + +.fa-nutritionix:before { + content: "\f3d6"; } + +.fa-wodu:before { + content: "\e088"; } + +.fa-google-pay:before { + content: "\e079"; } + +.fa-intercom:before { + content: "\f7af"; } + +.fa-zhihu:before { + content: "\f63f"; } + +.fa-korvue:before { + content: "\f42f"; } + +.fa-pix:before { + content: "\e43a"; } + +.fa-steam-symbol:before { + content: "\f3f6"; } +:root, :host { + --fa-style-family-classic: 'Font Awesome 6 Free'; + --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; } + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } + +.far, +.fa-regular { + font-weight: 400; } +:root, :host { + --fa-style-family-classic: 'Font Awesome 6 Free'; + --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; } + +@font-face { + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: block; + src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } + +.fas, +.fa-solid { + font-weight: 900; } +@font-face { + font-family: 'Font Awesome 5 Brands'; + font-display: block; + font-weight: 400; + src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-display: block; + font-weight: 900; + src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-display: block; + font-weight: 400; + src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } + +@font-face { + font-family: 'FontAwesome'; + font-display: block; + src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); } diff --git a/docs/deps/font-awesome-6.4.2/css/all.min.css b/docs/deps/font-awesome-6.4.2/css/all.min.css new file mode 100644 index 0000000..6604a06 --- /dev/null +++ b/docs/deps/font-awesome-6.4.2/css/all.min.css @@ -0,0 +1,9 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }.fa-regular,.far{font-weight:400}:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }.fa-solid,.fas{font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }@font-face{font-family:"FontAwesome";font-display:block;src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }@font-face{font-family:"FontAwesome";font-display:block;src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }@font-face{font-family:"FontAwesome";font-display:block;src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }@font-face{font-family:"FontAwesome";font-display:block;src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); } \ No newline at end of file diff --git a/docs/deps/font-awesome-6.4.2/css/v4-shims.css b/docs/deps/font-awesome-6.4.2/css/v4-shims.css new file mode 100644 index 0000000..a85953d --- /dev/null +++ b/docs/deps/font-awesome-6.4.2/css/v4-shims.css @@ -0,0 +1,2194 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa.fa-glass:before { + content: "\f000"; } + +.fa.fa-envelope-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-envelope-o:before { + content: "\f0e0"; } + +.fa.fa-star-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-star-o:before { + content: "\f005"; } + +.fa.fa-remove:before { + content: "\f00d"; } + +.fa.fa-close:before { + content: "\f00d"; } + +.fa.fa-gear:before { + content: "\f013"; } + +.fa.fa-trash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-trash-o:before { + content: "\f2ed"; } + +.fa.fa-home:before { + content: "\f015"; } + +.fa.fa-file-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-o:before { + content: "\f15b"; } + +.fa.fa-clock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-clock-o:before { + content: "\f017"; } + +.fa.fa-arrow-circle-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-down:before { + content: "\f358"; } + +.fa.fa-arrow-circle-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-up:before { + content: "\f35b"; } + +.fa.fa-play-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-play-circle-o:before { + content: "\f144"; } + +.fa.fa-repeat:before { + content: "\f01e"; } + +.fa.fa-rotate-right:before { + content: "\f01e"; } + +.fa.fa-refresh:before { + content: "\f021"; } + +.fa.fa-list-alt { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-list-alt:before { + content: "\f022"; } + +.fa.fa-dedent:before { + content: "\f03b"; } + +.fa.fa-video-camera:before { + content: "\f03d"; } + +.fa.fa-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-picture-o:before { + content: "\f03e"; } + +.fa.fa-photo { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-photo:before { + content: "\f03e"; } + +.fa.fa-image { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-image:before { + content: "\f03e"; } + +.fa.fa-map-marker:before { + content: "\f3c5"; } + +.fa.fa-pencil-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-pencil-square-o:before { + content: "\f044"; } + +.fa.fa-edit { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-edit:before { + content: "\f044"; } + +.fa.fa-share-square-o:before { + content: "\f14d"; } + +.fa.fa-check-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-check-square-o:before { + content: "\f14a"; } + +.fa.fa-arrows:before { + content: "\f0b2"; } + +.fa.fa-times-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-times-circle-o:before { + content: "\f057"; } + +.fa.fa-check-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-check-circle-o:before { + content: "\f058"; } + +.fa.fa-mail-forward:before { + content: "\f064"; } + +.fa.fa-expand:before { + content: "\f424"; } + +.fa.fa-compress:before { + content: "\f422"; } + +.fa.fa-eye { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-eye-slash { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-warning:before { + content: "\f071"; } + +.fa.fa-calendar:before { + content: "\f073"; } + +.fa.fa-arrows-v:before { + content: "\f338"; } + +.fa.fa-arrows-h:before { + content: "\f337"; } + +.fa.fa-bar-chart:before { + content: "\e0e3"; } + +.fa.fa-bar-chart-o:before { + content: "\e0e3"; } + +.fa.fa-twitter-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-twitter-square:before { + content: "\f081"; } + +.fa.fa-facebook-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-facebook-square:before { + content: "\f082"; } + +.fa.fa-gears:before { + content: "\f085"; } + +.fa.fa-thumbs-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-thumbs-o-up:before { + content: "\f164"; } + +.fa.fa-thumbs-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-thumbs-o-down:before { + content: "\f165"; } + +.fa.fa-heart-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-heart-o:before { + content: "\f004"; } + +.fa.fa-sign-out:before { + content: "\f2f5"; } + +.fa.fa-linkedin-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-linkedin-square:before { + content: "\f08c"; } + +.fa.fa-thumb-tack:before { + content: "\f08d"; } + +.fa.fa-external-link:before { + content: "\f35d"; } + +.fa.fa-sign-in:before { + content: "\f2f6"; } + +.fa.fa-github-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-github-square:before { + content: "\f092"; } + +.fa.fa-lemon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-lemon-o:before { + content: "\f094"; } + +.fa.fa-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-square-o:before { + content: "\f0c8"; } + +.fa.fa-bookmark-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-bookmark-o:before { + content: "\f02e"; } + +.fa.fa-twitter { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-facebook { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-facebook:before { + content: "\f39e"; } + +.fa.fa-facebook-f { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-facebook-f:before { + content: "\f39e"; } + +.fa.fa-github { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-credit-card { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-feed:before { + content: "\f09e"; } + +.fa.fa-hdd-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hdd-o:before { + content: "\f0a0"; } + +.fa.fa-hand-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-o-right:before { + content: "\f0a4"; } + +.fa.fa-hand-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-o-left:before { + content: "\f0a5"; } + +.fa.fa-hand-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-o-up:before { + content: "\f0a6"; } + +.fa.fa-hand-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-o-down:before { + content: "\f0a7"; } + +.fa.fa-globe:before { + content: "\f57d"; } + +.fa.fa-tasks:before { + content: "\f828"; } + +.fa.fa-arrows-alt:before { + content: "\f31e"; } + +.fa.fa-group:before { + content: "\f0c0"; } + +.fa.fa-chain:before { + content: "\f0c1"; } + +.fa.fa-cut:before { + content: "\f0c4"; } + +.fa.fa-files-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-files-o:before { + content: "\f0c5"; } + +.fa.fa-floppy-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-floppy-o:before { + content: "\f0c7"; } + +.fa.fa-save { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-save:before { + content: "\f0c7"; } + +.fa.fa-navicon:before { + content: "\f0c9"; } + +.fa.fa-reorder:before { + content: "\f0c9"; } + +.fa.fa-magic:before { + content: "\e2ca"; } + +.fa.fa-pinterest { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pinterest-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pinterest-square:before { + content: "\f0d3"; } + +.fa.fa-google-plus-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-square:before { + content: "\f0d4"; } + +.fa.fa-google-plus { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-plus:before { + content: "\f0d5"; } + +.fa.fa-money:before { + content: "\f3d1"; } + +.fa.fa-unsorted:before { + content: "\f0dc"; } + +.fa.fa-sort-desc:before { + content: "\f0dd"; } + +.fa.fa-sort-asc:before { + content: "\f0de"; } + +.fa.fa-linkedin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-linkedin:before { + content: "\f0e1"; } + +.fa.fa-rotate-left:before { + content: "\f0e2"; } + +.fa.fa-legal:before { + content: "\f0e3"; } + +.fa.fa-tachometer:before { + content: "\f625"; } + +.fa.fa-dashboard:before { + content: "\f625"; } + +.fa.fa-comment-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-comment-o:before { + content: "\f075"; } + +.fa.fa-comments-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-comments-o:before { + content: "\f086"; } + +.fa.fa-flash:before { + content: "\f0e7"; } + +.fa.fa-clipboard:before { + content: "\f0ea"; } + +.fa.fa-lightbulb-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-lightbulb-o:before { + content: "\f0eb"; } + +.fa.fa-exchange:before { + content: "\f362"; } + +.fa.fa-cloud-download:before { + content: "\f0ed"; } + +.fa.fa-cloud-upload:before { + content: "\f0ee"; } + +.fa.fa-bell-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-bell-o:before { + content: "\f0f3"; } + +.fa.fa-cutlery:before { + content: "\f2e7"; } + +.fa.fa-file-text-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-text-o:before { + content: "\f15c"; } + +.fa.fa-building-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-building-o:before { + content: "\f1ad"; } + +.fa.fa-hospital-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hospital-o:before { + content: "\f0f8"; } + +.fa.fa-tablet:before { + content: "\f3fa"; } + +.fa.fa-mobile:before { + content: "\f3cd"; } + +.fa.fa-mobile-phone:before { + content: "\f3cd"; } + +.fa.fa-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-circle-o:before { + content: "\f111"; } + +.fa.fa-mail-reply:before { + content: "\f3e5"; } + +.fa.fa-github-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-folder-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-folder-o:before { + content: "\f07b"; } + +.fa.fa-folder-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-folder-open-o:before { + content: "\f07c"; } + +.fa.fa-smile-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-smile-o:before { + content: "\f118"; } + +.fa.fa-frown-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-frown-o:before { + content: "\f119"; } + +.fa.fa-meh-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-meh-o:before { + content: "\f11a"; } + +.fa.fa-keyboard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-keyboard-o:before { + content: "\f11c"; } + +.fa.fa-flag-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-flag-o:before { + content: "\f024"; } + +.fa.fa-mail-reply-all:before { + content: "\f122"; } + +.fa.fa-star-half-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-star-half-o:before { + content: "\f5c0"; } + +.fa.fa-star-half-empty { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-star-half-empty:before { + content: "\f5c0"; } + +.fa.fa-star-half-full { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-star-half-full:before { + content: "\f5c0"; } + +.fa.fa-code-fork:before { + content: "\f126"; } + +.fa.fa-chain-broken:before { + content: "\f127"; } + +.fa.fa-unlink:before { + content: "\f127"; } + +.fa.fa-calendar-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-calendar-o:before { + content: "\f133"; } + +.fa.fa-maxcdn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-html5 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-css3 { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-unlock-alt:before { + content: "\f09c"; } + +.fa.fa-minus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-minus-square-o:before { + content: "\f146"; } + +.fa.fa-level-up:before { + content: "\f3bf"; } + +.fa.fa-level-down:before { + content: "\f3be"; } + +.fa.fa-pencil-square:before { + content: "\f14b"; } + +.fa.fa-external-link-square:before { + content: "\f360"; } + +.fa.fa-compass { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-down:before { + content: "\f150"; } + +.fa.fa-toggle-down { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-toggle-down:before { + content: "\f150"; } + +.fa.fa-caret-square-o-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-up:before { + content: "\f151"; } + +.fa.fa-toggle-up { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-toggle-up:before { + content: "\f151"; } + +.fa.fa-caret-square-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-right:before { + content: "\f152"; } + +.fa.fa-toggle-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-toggle-right:before { + content: "\f152"; } + +.fa.fa-eur:before { + content: "\f153"; } + +.fa.fa-euro:before { + content: "\f153"; } + +.fa.fa-gbp:before { + content: "\f154"; } + +.fa.fa-usd:before { + content: "\24"; } + +.fa.fa-dollar:before { + content: "\24"; } + +.fa.fa-inr:before { + content: "\e1bc"; } + +.fa.fa-rupee:before { + content: "\e1bc"; } + +.fa.fa-jpy:before { + content: "\f157"; } + +.fa.fa-cny:before { + content: "\f157"; } + +.fa.fa-rmb:before { + content: "\f157"; } + +.fa.fa-yen:before { + content: "\f157"; } + +.fa.fa-rub:before { + content: "\f158"; } + +.fa.fa-ruble:before { + content: "\f158"; } + +.fa.fa-rouble:before { + content: "\f158"; } + +.fa.fa-krw:before { + content: "\f159"; } + +.fa.fa-won:before { + content: "\f159"; } + +.fa.fa-btc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bitcoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bitcoin:before { + content: "\f15a"; } + +.fa.fa-file-text:before { + content: "\f15c"; } + +.fa.fa-sort-alpha-asc:before { + content: "\f15d"; } + +.fa.fa-sort-alpha-desc:before { + content: "\f881"; } + +.fa.fa-sort-amount-asc:before { + content: "\f884"; } + +.fa.fa-sort-amount-desc:before { + content: "\f160"; } + +.fa.fa-sort-numeric-asc:before { + content: "\f162"; } + +.fa.fa-sort-numeric-desc:before { + content: "\f886"; } + +.fa.fa-youtube-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-youtube-square:before { + content: "\f431"; } + +.fa.fa-youtube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-xing { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-xing-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-xing-square:before { + content: "\f169"; } + +.fa.fa-youtube-play { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-youtube-play:before { + content: "\f167"; } + +.fa.fa-dropbox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-stack-overflow { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-instagram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-flickr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-adn { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket-square:before { + content: "\f171"; } + +.fa.fa-tumblr { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-tumblr-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-tumblr-square:before { + content: "\f174"; } + +.fa.fa-long-arrow-down:before { + content: "\f309"; } + +.fa.fa-long-arrow-up:before { + content: "\f30c"; } + +.fa.fa-long-arrow-left:before { + content: "\f30a"; } + +.fa.fa-long-arrow-right:before { + content: "\f30b"; } + +.fa.fa-apple { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-windows { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-android { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-linux { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-dribbble { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-skype { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-foursquare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-trello { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gratipay { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gittip { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gittip:before { + content: "\f184"; } + +.fa.fa-sun-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-sun-o:before { + content: "\f185"; } + +.fa.fa-moon-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-moon-o:before { + content: "\f186"; } + +.fa.fa-vk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-renren { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pagelines { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-stack-exchange { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-right { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-right:before { + content: "\f35a"; } + +.fa.fa-arrow-circle-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-left:before { + content: "\f359"; } + +.fa.fa-caret-square-o-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-left:before { + content: "\f191"; } + +.fa.fa-toggle-left { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-toggle-left:before { + content: "\f191"; } + +.fa.fa-dot-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-dot-circle-o:before { + content: "\f192"; } + +.fa.fa-vimeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-vimeo-square:before { + content: "\f194"; } + +.fa.fa-try:before { + content: "\e2bb"; } + +.fa.fa-turkish-lira:before { + content: "\e2bb"; } + +.fa.fa-plus-square-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-plus-square-o:before { + content: "\f0fe"; } + +.fa.fa-slack { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wordpress { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-openid { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-institution:before { + content: "\f19c"; } + +.fa.fa-bank:before { + content: "\f19c"; } + +.fa.fa-mortar-board:before { + content: "\f19d"; } + +.fa.fa-yahoo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-reddit { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-reddit-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-reddit-square:before { + content: "\f1a2"; } + +.fa.fa-stumbleupon-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-stumbleupon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-delicious { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-digg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pied-piper-pp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pied-piper-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-drupal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-joomla { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-behance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-behance-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-behance-square:before { + content: "\f1b5"; } + +.fa.fa-steam { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-steam-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-steam-square:before { + content: "\f1b7"; } + +.fa.fa-automobile:before { + content: "\f1b9"; } + +.fa.fa-cab:before { + content: "\f1ba"; } + +.fa.fa-spotify { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-deviantart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-soundcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-file-pdf-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-pdf-o:before { + content: "\f1c1"; } + +.fa.fa-file-word-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-word-o:before { + content: "\f1c2"; } + +.fa.fa-file-excel-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-excel-o:before { + content: "\f1c3"; } + +.fa.fa-file-powerpoint-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-powerpoint-o:before { + content: "\f1c4"; } + +.fa.fa-file-image-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-image-o:before { + content: "\f1c5"; } + +.fa.fa-file-photo-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-photo-o:before { + content: "\f1c5"; } + +.fa.fa-file-picture-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-picture-o:before { + content: "\f1c5"; } + +.fa.fa-file-archive-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-archive-o:before { + content: "\f1c6"; } + +.fa.fa-file-zip-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-zip-o:before { + content: "\f1c6"; } + +.fa.fa-file-audio-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-audio-o:before { + content: "\f1c7"; } + +.fa.fa-file-sound-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-sound-o:before { + content: "\f1c7"; } + +.fa.fa-file-video-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-video-o:before { + content: "\f1c8"; } + +.fa.fa-file-movie-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-movie-o:before { + content: "\f1c8"; } + +.fa.fa-file-code-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-file-code-o:before { + content: "\f1c9"; } + +.fa.fa-vine { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-codepen { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-jsfiddle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-life-bouy:before { + content: "\f1cd"; } + +.fa.fa-life-buoy:before { + content: "\f1cd"; } + +.fa.fa-life-saver:before { + content: "\f1cd"; } + +.fa.fa-support:before { + content: "\f1cd"; } + +.fa.fa-circle-o-notch:before { + content: "\f1ce"; } + +.fa.fa-rebel { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-ra { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-ra:before { + content: "\f1d0"; } + +.fa.fa-resistance { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-resistance:before { + content: "\f1d0"; } + +.fa.fa-empire { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-ge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-ge:before { + content: "\f1d1"; } + +.fa.fa-git-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-git-square:before { + content: "\f1d2"; } + +.fa.fa-git { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-hacker-news { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator-square:before { + content: "\f1d4"; } + +.fa.fa-yc-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-yc-square:before { + content: "\f1d4"; } + +.fa.fa-tencent-weibo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-qq { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-weixin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wechat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wechat:before { + content: "\f1d7"; } + +.fa.fa-send:before { + content: "\f1d8"; } + +.fa.fa-paper-plane-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-paper-plane-o:before { + content: "\f1d8"; } + +.fa.fa-send-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-send-o:before { + content: "\f1d8"; } + +.fa.fa-circle-thin { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-circle-thin:before { + content: "\f111"; } + +.fa.fa-header:before { + content: "\f1dc"; } + +.fa.fa-futbol-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-futbol-o:before { + content: "\f1e3"; } + +.fa.fa-soccer-ball-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-soccer-ball-o:before { + content: "\f1e3"; } + +.fa.fa-slideshare { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-twitch { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-yelp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-newspaper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-newspaper-o:before { + content: "\f1ea"; } + +.fa.fa-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-wallet { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-visa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-mastercard { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-discover { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-amex { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-paypal { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-stripe { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bell-slash-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-bell-slash-o:before { + content: "\f1f6"; } + +.fa.fa-trash:before { + content: "\f2ed"; } + +.fa.fa-copyright { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-eyedropper:before { + content: "\f1fb"; } + +.fa.fa-area-chart:before { + content: "\f1fe"; } + +.fa.fa-pie-chart:before { + content: "\f200"; } + +.fa.fa-line-chart:before { + content: "\f201"; } + +.fa.fa-lastfm { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-lastfm-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-lastfm-square:before { + content: "\f203"; } + +.fa.fa-ioxhost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-angellist { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-cc:before { + content: "\f20a"; } + +.fa.fa-ils:before { + content: "\f20b"; } + +.fa.fa-shekel:before { + content: "\f20b"; } + +.fa.fa-sheqel:before { + content: "\f20b"; } + +.fa.fa-buysellads { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-connectdevelop { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-dashcube { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-forumbee { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-leanpub { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-sellsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-shirtsinbulk { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-simplybuilt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-skyatlas { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-diamond { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-diamond:before { + content: "\f3a5"; } + +.fa.fa-transgender:before { + content: "\f224"; } + +.fa.fa-intersex:before { + content: "\f224"; } + +.fa.fa-transgender-alt:before { + content: "\f225"; } + +.fa.fa-facebook-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-facebook-official:before { + content: "\f09a"; } + +.fa.fa-pinterest-p { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-whatsapp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-hotel:before { + content: "\f236"; } + +.fa.fa-viacoin { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-medium { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-yc { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-yc:before { + content: "\f23b"; } + +.fa.fa-optin-monster { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-opencart { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-expeditedssl { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-battery-4:before { + content: "\f240"; } + +.fa.fa-battery:before { + content: "\f240"; } + +.fa.fa-battery-3:before { + content: "\f241"; } + +.fa.fa-battery-2:before { + content: "\f242"; } + +.fa.fa-battery-1:before { + content: "\f243"; } + +.fa.fa-battery-0:before { + content: "\f244"; } + +.fa.fa-object-group { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-object-ungroup { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-sticky-note-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-sticky-note-o:before { + content: "\f249"; } + +.fa.fa-cc-jcb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-cc-diners-club { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-clone { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hourglass-o:before { + content: "\f254"; } + +.fa.fa-hourglass-1:before { + content: "\f251"; } + +.fa.fa-hourglass-2:before { + content: "\f252"; } + +.fa.fa-hourglass-3:before { + content: "\f253"; } + +.fa.fa-hand-rock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-rock-o:before { + content: "\f255"; } + +.fa.fa-hand-grab-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-grab-o:before { + content: "\f255"; } + +.fa.fa-hand-paper-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-paper-o:before { + content: "\f256"; } + +.fa.fa-hand-stop-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-stop-o:before { + content: "\f256"; } + +.fa.fa-hand-scissors-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-scissors-o:before { + content: "\f257"; } + +.fa.fa-hand-lizard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-lizard-o:before { + content: "\f258"; } + +.fa.fa-hand-spock-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-spock-o:before { + content: "\f259"; } + +.fa.fa-hand-pointer-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-pointer-o:before { + content: "\f25a"; } + +.fa.fa-hand-peace-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-hand-peace-o:before { + content: "\f25b"; } + +.fa.fa-registered { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-creative-commons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gg { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gg-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-odnoklassniki { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-odnoklassniki-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-odnoklassniki-square:before { + content: "\f264"; } + +.fa.fa-get-pocket { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wikipedia-w { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-safari { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-chrome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-firefox { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-opera { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-internet-explorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-television:before { + content: "\f26c"; } + +.fa.fa-contao { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-500px { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-amazon { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-calendar-plus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-calendar-plus-o:before { + content: "\f271"; } + +.fa.fa-calendar-minus-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-calendar-minus-o:before { + content: "\f272"; } + +.fa.fa-calendar-times-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-calendar-times-o:before { + content: "\f273"; } + +.fa.fa-calendar-check-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-calendar-check-o:before { + content: "\f274"; } + +.fa.fa-map-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-map-o:before { + content: "\f279"; } + +.fa.fa-commenting:before { + content: "\f4ad"; } + +.fa.fa-commenting-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-commenting-o:before { + content: "\f4ad"; } + +.fa.fa-houzz { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-vimeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-vimeo:before { + content: "\f27d"; } + +.fa.fa-black-tie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-fonticons { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-reddit-alien { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-edge { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-credit-card-alt:before { + content: "\f09d"; } + +.fa.fa-codiepie { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-modx { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-fort-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-usb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-product-hunt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-mixcloud { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-scribd { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-pause-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-pause-circle-o:before { + content: "\f28b"; } + +.fa.fa-stop-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-stop-circle-o:before { + content: "\f28d"; } + +.fa.fa-bluetooth { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-bluetooth-b { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-gitlab { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wpbeginner { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wpforms { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-envira { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wheelchair-alt { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wheelchair-alt:before { + content: "\f368"; } + +.fa.fa-question-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-question-circle-o:before { + content: "\f059"; } + +.fa.fa-volume-control-phone:before { + content: "\f2a0"; } + +.fa.fa-asl-interpreting:before { + content: "\f2a3"; } + +.fa.fa-deafness:before { + content: "\f2a4"; } + +.fa.fa-hard-of-hearing:before { + content: "\f2a4"; } + +.fa.fa-glide { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-glide-g { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-signing:before { + content: "\f2a7"; } + +.fa.fa-viadeo { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-viadeo-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-viadeo-square:before { + content: "\f2aa"; } + +.fa.fa-snapchat { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-snapchat-ghost { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-snapchat-ghost:before { + content: "\f2ab"; } + +.fa.fa-snapchat-square { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-snapchat-square:before { + content: "\f2ad"; } + +.fa.fa-pied-piper { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-first-order { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-yoast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-themeisle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-official { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-official:before { + content: "\f2b3"; } + +.fa.fa-google-plus-circle { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-circle:before { + content: "\f2b3"; } + +.fa.fa-font-awesome { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-fa { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-fa:before { + content: "\f2b4"; } + +.fa.fa-handshake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-handshake-o:before { + content: "\f2b5"; } + +.fa.fa-envelope-open-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-envelope-open-o:before { + content: "\f2b6"; } + +.fa.fa-linode { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-address-book-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-address-book-o:before { + content: "\f2b9"; } + +.fa.fa-vcard:before { + content: "\f2bb"; } + +.fa.fa-address-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-address-card-o:before { + content: "\f2bb"; } + +.fa.fa-vcard-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-vcard-o:before { + content: "\f2bb"; } + +.fa.fa-user-circle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-user-circle-o:before { + content: "\f2bd"; } + +.fa.fa-user-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-user-o:before { + content: "\f007"; } + +.fa.fa-id-badge { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-drivers-license:before { + content: "\f2c2"; } + +.fa.fa-id-card-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-id-card-o:before { + content: "\f2c2"; } + +.fa.fa-drivers-license-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-drivers-license-o:before { + content: "\f2c2"; } + +.fa.fa-quora { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-free-code-camp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-telegram { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-thermometer-4:before { + content: "\f2c7"; } + +.fa.fa-thermometer:before { + content: "\f2c7"; } + +.fa.fa-thermometer-3:before { + content: "\f2c8"; } + +.fa.fa-thermometer-2:before { + content: "\f2c9"; } + +.fa.fa-thermometer-1:before { + content: "\f2ca"; } + +.fa.fa-thermometer-0:before { + content: "\f2cb"; } + +.fa.fa-bathtub:before { + content: "\f2cd"; } + +.fa.fa-s15:before { + content: "\f2cd"; } + +.fa.fa-window-maximize { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-window-restore { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-times-rectangle:before { + content: "\f410"; } + +.fa.fa-window-close-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-window-close-o:before { + content: "\f410"; } + +.fa.fa-times-rectangle-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-times-rectangle-o:before { + content: "\f410"; } + +.fa.fa-bandcamp { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-grav { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-etsy { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-imdb { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-ravelry { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-eercast { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-eercast:before { + content: "\f2da"; } + +.fa.fa-snowflake-o { + font-family: 'Font Awesome 6 Free'; + font-weight: 400; } + +.fa.fa-snowflake-o:before { + content: "\f2dc"; } + +.fa.fa-superpowers { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-wpexplorer { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } + +.fa.fa-meetup { + font-family: 'Font Awesome 6 Brands'; + font-weight: 400; } diff --git a/docs/deps/font-awesome-6.4.2/css/v4-shims.min.css b/docs/deps/font-awesome-6.4.2/css/v4-shims.min.css new file mode 100644 index 0000000..64e4e8d --- /dev/null +++ b/docs/deps/font-awesome-6.4.2/css/v4-shims.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa.fa-glass:before{content:"\f000"}.fa.fa-envelope-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-star-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-home:before{content:"\f015"}.fa.fa-file-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-list-alt:before{content:"\f022"}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-edit{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-edit:before{content:"\f044"}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-expand:before{content:"\f424"}.fa.fa-compress:before{content:"\f422"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart-o:before,.fa.fa-bar-chart:before{content:"\e0e3"}.fa.fa-twitter-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-twitter-square:before{content:"\f081"}.fa.fa-facebook-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-square:before{content:"\f082"}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-github-square:before{content:"\f092"}.fa.fa-lemon-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-globe:before{content:"\f57d"}.fa.fa-tasks:before{content:"\f828"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-cut:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-save{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-save:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-magic:before{content:"\e2ca"}.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-pinterest-square:before{content:"\f0d3"}.fa.fa-google-plus-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-square:before{content:"\f0d4"}.fa.fa-google-plus{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f625"}.fa.fa-comment-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard:before{content:"\f0ea"}.fa.fa-lightbulb-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f0ed"}.fa.fa-cloud-upload:before{content:"\f0ee"}.fa.fa-bell-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f5c0"}.fa.fa-star-half-empty{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f5c0"}.fa.fa-star-half-full{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f5c0"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before,.fa.fa-unlink:before{content:"\f127"}.fa.fa-calendar-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-unlock-alt:before{content:"\f09c"}.fa.fa-minus-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\24"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\e1bc"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f881"}.fa.fa-sort-amount-asc:before{content:"\f884"}.fa.fa-sort-amount-desc:before{content:"\f160"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f886"}.fa.fa-youtube-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-youtube-square:before{content:"\f431"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-xing-square:before{content:"\f169"}.fa.fa-youtube-play{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-tumblr-square:before{content:"\f174"}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-vimeo-square:before{content:"\f194"}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\e2bb"}.fa.fa-plus-square-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-google,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-yahoo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-reddit-square:before{content:"\f1a2"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-behance-square:before{content:"\f1b5"}.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-steam-square:before{content:"\f1b7"}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-cab:before{content:"\f1ba"}.fa.fa-deviantart,.fa.fa-soundcloud,.fa.fa-spotify{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-life-bouy:before,.fa.fa-life-buoy:before,.fa.fa-life-saver:before,.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-git-square:before{content:"\f1d2"}.fa.fa-git,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-futbol-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-lastfm-square:before{content:"\f203"}.fa.fa-angellist,.fa.fa-ioxhost{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before,.fa.fa-transgender:before{content:"\f224"}.fa.fa-transgender-alt:before{content:"\f225"}.fa.fa-facebook-official{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-clone{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-creative-commons,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-odnoklassniki-square:before{content:"\f264"}.fa.fa-chrome,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-internet-explorer,.fa.fa-opera,.fa.fa-safari,.fa.fa-wikipedia-w{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-viadeo,.fa.fa-viadeo-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-viadeo-square:before{content:"\f2aa"}.fa.fa-snapchat,.fa.fa-snapchat-ghost{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-snapchat-ghost:before{content:"\f2ab"}.fa.fa-snapchat-square{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-snapchat-square:before{content:"\f2ad"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-themeisle,.fa.fa-yoast{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 6 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 6 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-meetup,.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 6 Brands";font-weight:400} \ No newline at end of file diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.ttf b/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000..30f55b7 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.ttf differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.woff2 b/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..8a480d9 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-brands-400.woff2 differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.ttf b/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000..c79589d Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.ttf differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.woff2 b/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..059a94e Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-regular-400.woff2 differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.ttf b/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000..e479fb2 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.ttf differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.woff2 b/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..88b0367 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-solid-900.woff2 differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.ttf b/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.ttf new file mode 100644 index 0000000..ba6cb25 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.ttf differ diff --git a/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.woff2 b/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.woff2 new file mode 100644 index 0000000..23b1c47 Binary files /dev/null and b/docs/deps/font-awesome-6.4.2/webfonts/fa-v4compatibility.woff2 differ diff --git a/docs/deps/headroom-0.11.0/headroom.min.js b/docs/deps/headroom-0.11.0/headroom.min.js new file mode 100644 index 0000000..433069f --- /dev/null +++ b/docs/deps/headroom-0.11.0/headroom.min.js @@ -0,0 +1,7 @@ +/*! + * headroom.js v0.11.0 - Give your page some headroom. Hide your header until you need it + * Copyright (c) 2020 Nick Williams - http://wicky.nillia.ms/headroom.js + * License: MIT + */ + +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t=t||self).Headroom=n()}(this,function(){"use strict";function t(){return"undefined"!=typeof window}function d(t){return function(t){return t&&t.document&&function(t){return 9===t.nodeType}(t.document)}(t)?function(t){var n=t.document,o=n.body,s=n.documentElement;return{scrollHeight:function(){return Math.max(o.scrollHeight,s.scrollHeight,o.offsetHeight,s.offsetHeight,o.clientHeight,s.clientHeight)},height:function(){return t.innerHeight||s.clientHeight||o.clientHeight},scrollY:function(){return void 0!==t.pageYOffset?t.pageYOffset:(s||o.parentNode||o).scrollTop}}}(t):function(t){return{scrollHeight:function(){return Math.max(t.scrollHeight,t.offsetHeight,t.clientHeight)},height:function(){return Math.max(t.offsetHeight,t.clientHeight)},scrollY:function(){return t.scrollTop}}}(t)}function n(t,s,e){var n,o=function(){var n=!1;try{var t={get passive(){n=!0}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch(t){n=!1}return n}(),i=!1,r=d(t),l=r.scrollY(),a={};function c(){var t=Math.round(r.scrollY()),n=r.height(),o=r.scrollHeight();a.scrollY=t,a.lastScrollY=l,a.direction=ls.tolerance[a.direction],e(a),l=t,i=!1}function h(){i||(i=!0,n=requestAnimationFrame(c))}var u=!!o&&{passive:!0,capture:!1};return t.addEventListener("scroll",h,u),c(),{destroy:function(){cancelAnimationFrame(n),t.removeEventListener("scroll",h,u)}}}function o(t,n){n=n||{},Object.assign(this,o.options,n),this.classes=Object.assign({},o.options.classes,n.classes),this.elem=t,this.tolerance=function(t){return t===Object(t)?t:{down:t,up:t}}(this.tolerance),this.initialised=!1,this.frozen=!1}return o.prototype={constructor:o,init:function(){return o.cutsTheMustard&&!this.initialised&&(this.addClass("initial"),this.initialised=!0,setTimeout(function(t){t.scrollTracker=n(t.scroller,{offset:t.offset,tolerance:t.tolerance},t.update.bind(t))},100,this)),this},destroy:function(){this.initialised=!1,Object.keys(this.classes).forEach(this.removeClass,this),this.scrollTracker.destroy()},unpin:function(){!this.hasClass("pinned")&&this.hasClass("unpinned")||(this.addClass("unpinned"),this.removeClass("pinned"),this.onUnpin&&this.onUnpin.call(this))},pin:function(){this.hasClass("unpinned")&&(this.addClass("pinned"),this.removeClass("unpinned"),this.onPin&&this.onPin.call(this))},freeze:function(){this.frozen=!0,this.addClass("frozen")},unfreeze:function(){this.frozen=!1,this.removeClass("frozen")},top:function(){this.hasClass("top")||(this.addClass("top"),this.removeClass("notTop"),this.onTop&&this.onTop.call(this))},notTop:function(){this.hasClass("notTop")||(this.addClass("notTop"),this.removeClass("top"),this.onNotTop&&this.onNotTop.call(this))},bottom:function(){this.hasClass("bottom")||(this.addClass("bottom"),this.removeClass("notBottom"),this.onBottom&&this.onBottom.call(this))},notBottom:function(){this.hasClass("notBottom")||(this.addClass("notBottom"),this.removeClass("bottom"),this.onNotBottom&&this.onNotBottom.call(this))},shouldUnpin:function(t){return"down"===t.direction&&!t.top&&t.toleranceExceeded},shouldPin:function(t){return"up"===t.direction&&t.toleranceExceeded||t.top},addClass:function(t){this.elem.classList.add.apply(this.elem.classList,this.classes[t].split(" "))},removeClass:function(t){this.elem.classList.remove.apply(this.elem.classList,this.classes[t].split(" "))},hasClass:function(t){return this.classes[t].split(" ").every(function(t){return this.classList.contains(t)},this.elem)},update:function(t){t.isOutOfBounds||!0!==this.frozen&&(t.top?this.top():this.notTop(),t.bottom?this.bottom():this.notBottom(),this.shouldUnpin(t)?this.unpin():this.shouldPin(t)&&this.pin())}},o.options={tolerance:{up:0,down:0},offset:0,scroller:t()?window:null,classes:{frozen:"headroom--frozen",pinned:"headroom--pinned",unpinned:"headroom--unpinned",top:"headroom--top",notTop:"headroom--not-top",bottom:"headroom--bottom",notBottom:"headroom--not-bottom",initial:"headroom"}},o.cutsTheMustard=!!(t()&&function(){}.bind&&"classList"in document.documentElement&&Object.assign&&Object.keys&&requestAnimationFrame),o}); \ No newline at end of file diff --git a/docs/deps/headroom-0.11.0/jQuery.headroom.min.js b/docs/deps/headroom-0.11.0/jQuery.headroom.min.js new file mode 100644 index 0000000..17f70c9 --- /dev/null +++ b/docs/deps/headroom-0.11.0/jQuery.headroom.min.js @@ -0,0 +1,7 @@ +/*! + * headroom.js v0.9.4 - Give your page some headroom. Hide your header until you need it + * Copyright (c) 2017 Nick Williams - http://wicky.nillia.ms/headroom.js + * License: MIT + */ + +!function(a){a&&(a.fn.headroom=function(b){return this.each(function(){var c=a(this),d=c.data("headroom"),e="object"==typeof b&&b;e=a.extend(!0,{},Headroom.options,e),d||(d=new Headroom(this,e),d.init(),c.data("headroom",d)),"string"==typeof b&&(d[b](),"destroy"===b&&c.removeData("headroom"))})},a("[data-headroom]").each(function(){var b=a(this);b.headroom(b.data())}))}(window.Zepto||window.jQuery); \ No newline at end of file diff --git a/docs/deps/jquery-3.6.0/jquery-3.6.0.js b/docs/deps/jquery-3.6.0/jquery-3.6.0.js new file mode 100644 index 0000000..fc6c299 --- /dev/null +++ b/docs/deps/jquery-3.6.0/jquery-3.6.0.js @@ -0,0 +1,10881 @@ +/*! + * jQuery JavaScript Library v3.6.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2021-03-02T17:08Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.6.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.6 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2021-02-16 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the primary Deferred + primary = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return primary.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + } + + return primary.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + + // Support: Chrome 86+ + // In Chrome, if an element having a focusout handler is blurred by + // clicking outside of it, it invokes the handler synchronously. If + // that handler calls `.remove()` on the element, the data is cleared, + // leaving `result` undefined. We need to guard against this. + return result && result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + // + // Support: Firefox 70+ + // Only Firefox includes border widths + // in computed dimensions. (gh-4529) + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; + tr.style.cssText = "border:1px solid"; + + // Support: Chrome 86+ + // Height set through cssText does not get applied. + // Computed height then comes back as 0. + tr.style.height = "1px"; + trChild.style.height = "9px"; + + // Support: Android 8 Chrome 86+ + // In our bodyBackground.html iframe, + // display for all div elements is set to "inline", + // which causes a problem only in Android 8 Chrome 86. + // Ensuring the div is display: block + // gets around this issue. + trChild.style.display = "block"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + + parseInt( trStyle.borderTopWidth, 10 ) + + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ).filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ).map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + +originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script but not if jsonp + if ( !isSuccess && + jQuery.inArray( "script", s.dataTypes ) > -1 && + jQuery.inArray( "json", s.dataTypes ) < 0 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " - - - - - - + + + + + + + - - - + + Skip to contents -
    -
    -
    -
    - +
    +
    +
    + + +

    Simulate adverse event reporting in clinical trials with the goal of detecting under-reporting sites.

    @@ -160,22 +110,17 @@

    IMPALA

    IMPALA logo

    -

    Publication +

    Publications

    +

    Koneswarakantha, B., Adyanthaya, R., Emerson, J. et al. An Open-Source R Package for Detection of Adverse Events Under-Reporting in Clinical Trials: Implementation and Validation by the IMPALA (Inter coMPany quALity Analytics) Consortium. Ther Innov Regul Sci 58, 591–599 (2024). https://doi.org/10.1007/s43441-024-00631-8

    Koneswarakantha, B., Barmaz, Y., Ménard, T. et al. Follow-up on the Use of Advanced Analytics for Clinical Quality Assurance: Bootstrap Resampling to Enhance Detection of Adverse Event Under-Reporting. Drug Saf (2020). https://doi.org/10.1007/s40264-020-01011-5

    @@ -403,10 +348,7 @@

    Application - +

    -
    - - + diff --git a/docs/katex-auto.js b/docs/katex-auto.js new file mode 100644 index 0000000..20651d9 --- /dev/null +++ b/docs/katex-auto.js @@ -0,0 +1,14 @@ +// https://github.com/jgm/pandoc/blob/29fa97ab96b8e2d62d48326e1b949a71dc41f47a/src/Text/Pandoc/Writers/HTML.hs#L332-L345 +document.addEventListener("DOMContentLoaded", function () { + var mathElements = document.getElementsByClassName("math"); + var macros = []; + for (var i = 0; i < mathElements.length; i++) { + var texText = mathElements[i].firstChild; + if (mathElements[i].tagName == "SPAN") { + katex.render(texText.data, mathElements[i], { + displayMode: mathElements[i].classList.contains("display"), + throwOnError: false, + macros: macros, + fleqn: false + }); + }}}); diff --git a/docs/lightswitch.js b/docs/lightswitch.js new file mode 100644 index 0000000..9467125 --- /dev/null +++ b/docs/lightswitch.js @@ -0,0 +1,85 @@ + +/*! + * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under the Creative Commons Attribution 3.0 Unported License. + * Updates for {pkgdown} by the {bslib} authors, also licensed under CC-BY-3.0. + */ + +const getStoredTheme = () => localStorage.getItem('theme') +const setStoredTheme = theme => localStorage.setItem('theme', theme) + +const getPreferredTheme = () => { + const storedTheme = getStoredTheme() + if (storedTheme) { + return storedTheme + } + + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +} + +const setTheme = theme => { + if (theme === 'auto') { + document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) + } else { + document.documentElement.setAttribute('data-bs-theme', theme) + } +} + +function bsSetupThemeToggle () { + 'use strict' + + const showActiveTheme = (theme, focus = false) => { + var activeLabel, activeIcon; + + document.querySelectorAll('[data-bs-theme-value]').forEach(element => { + const buttonTheme = element.getAttribute('data-bs-theme-value') + const isActive = buttonTheme == theme + + element.classList.toggle('active', isActive) + element.setAttribute('aria-pressed', isActive) + + if (isActive) { + activeLabel = element.textContent; + activeIcon = element.querySelector('span').classList.value; + } + }) + + const themeSwitcher = document.querySelector('#dropdown-lightswitch') + if (!themeSwitcher) { + return + } + + themeSwitcher.setAttribute('aria-label', activeLabel) + themeSwitcher.querySelector('span').classList.value = activeIcon; + + if (focus) { + themeSwitcher.focus() + } + } + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + const storedTheme = getStoredTheme() + if (storedTheme !== 'light' && storedTheme !== 'dark') { + setTheme(getPreferredTheme()) + } + }) + + window.addEventListener('DOMContentLoaded', () => { + showActiveTheme(getPreferredTheme()) + + document + .querySelectorAll('[data-bs-theme-value]') + .forEach(toggle => { + toggle.addEventListener('click', () => { + const theme = toggle.getAttribute('data-bs-theme-value') + setTheme(theme) + setStoredTheme(theme) + showActiveTheme(theme, true) + }) + }) + }) +} + +setTheme(getPreferredTheme()); +bsSetupThemeToggle(); diff --git a/docs/news/index.html b/docs/news/index.html index 91d308d..b2561cd 100644 --- a/docs/news/index.html +++ b/docs/news/index.html @@ -1,174 +1,131 @@ -Changelog • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    - +

    simaerep 0.6.0

    +
    +
    +

    simaerep 0.5.0

    CRAN release: 2024-04-03

    - +

    simaerep 0.4.3

    CRAN release: 2023-03-03

    • delete performance unit tests (poisson faster than bootstrap) to accommodate CRAN request
    - +

    simaerep 0.4.2

    CRAN release: 2023-02-25

    • CRAN submission
    - +

    simaerep 0.4.1

    • validation report
    - +

    simaerep 0.4.0

    • S3 interface #17
    • better performance specification #26
    • more meaningful unit test names #24 #30 #33
    - +

    simaerep 0.3.3

    • better data checks and error messages #19, #20, # 21
    • data check and repair by check_df_visit() became optionable
    - +

    simaerep 0.3.2

    • added portfolio performance assessment
    - +

    simaerep 0.3.1

    • changed MIT License holder from openpharma to F. Hoffmann-La Roche Ltd and simaerep authors
    - +

    simaerep 0.3.0

    • improved evaluation point visit_med75
    • allow days instead of visit
    - +

    simaerep 0.2.0

    • use Benjamin Hochberg procedure for alpha error correction
    • fix warnings around parallel processing
    • improved SAS files vignette
    -
    +
    - +
    - -
    -
    - - diff --git a/docs/pkgdown.js b/docs/pkgdown.js index 6f0eee4..9757bf9 100644 --- a/docs/pkgdown.js +++ b/docs/pkgdown.js @@ -2,83 +2,43 @@ (function($) { $(function() { - $('.navbar-fixed-top').headroom(); + $('nav.navbar').headroom(); - $('body').css('padding-top', $('.navbar').height() + 10); - $(window).resize(function(){ - $('body').css('padding-top', $('.navbar').height() + 10); + Toc.init({ + $nav: $("#toc"), + $scope: $("main h2, main h3, main h4, main h5, main h6") }); - $('[data-toggle="tooltip"]').tooltip(); - - var cur_path = paths(location.pathname); - var links = $("#navbar ul li a"); - var max_length = -1; - var pos = -1; - for (var i = 0; i < links.length; i++) { - if (links[i].getAttribute("href") === "#") - continue; - // Ignore external links - if (links[i].host !== location.host) - continue; - - var nav_path = paths(links[i].pathname); - - var length = prefix_length(nav_path, cur_path); - if (length > max_length) { - max_length = length; - pos = i; - } - } - - // Add class to parent
  • , and enclosing
  • if in dropdown - if (pos >= 0) { - var menu_anchor = $(links[pos]); - menu_anchor.parent().addClass("active"); - menu_anchor.closest("li.dropdown").addClass("active"); - } - }); - - function paths(pathname) { - var pieces = pathname.split("/"); - pieces.shift(); // always starts with / - - var end = pieces[pieces.length - 1]; - if (end === "index.html" || end === "") - pieces.pop(); - return(pieces); - } - - // Returns -1 if not found - function prefix_length(needle, haystack) { - if (needle.length > haystack.length) - return(-1); - - // Special case for length-0 haystack, since for loop won't run - if (haystack.length === 0) { - return(needle.length === 0 ? 0 : -1); + if ($('#toc').length) { + $('body').scrollspy({ + target: '#toc', + offset: $("nav.navbar").outerHeight() + 1 + }); } - for (var i = 0; i < haystack.length; i++) { - if (needle[i] != haystack[i]) - return(i); - } + // Activate popovers + $('[data-bs-toggle="popover"]').popover({ + container: 'body', + html: true, + trigger: 'focus', + placement: "top", + sanitize: false, + }); - return(haystack.length); - } + $('[data-bs-toggle="tooltip"]').tooltip(); /* Clipboard --------------------------*/ function changeTooltipMessage(element, msg) { - var tooltipOriginalTitle=element.getAttribute('data-original-title'); - element.setAttribute('data-original-title', msg); + var tooltipOriginalTitle=element.getAttribute('data-bs-original-title'); + element.setAttribute('data-bs-original-title', msg); $(element).tooltip('show'); - element.setAttribute('data-original-title', tooltipOriginalTitle); + element.setAttribute('data-bs-original-title', tooltipOriginalTitle); } if(ClipboardJS.isSupported()) { $(document).ready(function() { - var copyButton = ""; + var copyButton = ""; $("div.sourceCode").addClass("hasCopyButton"); @@ -89,20 +49,106 @@ $('.btn-copy-ex').tooltip({container: 'body'}); // Initialize clipboard: - var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { + var clipboard = new ClipboardJS('[data-clipboard-copy]', { text: function(trigger) { return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); } }); - clipboardBtnCopies.on('success', function(e) { + clipboard.on('success', function(e) { changeTooltipMessage(e.trigger, 'Copied!'); e.clearSelection(); }); - clipboardBtnCopies.on('error', function() { + clipboard.on('error', function(e) { changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); }); + }); } + + /* Search marking --------------------------*/ + var url = new URL(window.location.href); + var toMark = url.searchParams.get("q"); + var mark = new Mark("main#main"); + if (toMark) { + mark.mark(toMark, { + accuracy: { + value: "complementary", + limiters: [",", ".", ":", "/"], + } + }); + } + + /* Search --------------------------*/ + /* Adapted from https://github.com/rstudio/bookdown/blob/2d692ba4b61f1e466c92e78fd712b0ab08c11d31/inst/resources/bs4_book/bs4_book.js#L25 */ + // Initialise search index on focus + var fuse; + $("#search-input").focus(async function(e) { + if (fuse) { + return; + } + + $(e.target).addClass("loading"); + var response = await fetch($("#search-input").data("search-index")); + var data = await response.json(); + + var options = { + keys: ["what", "text", "code"], + ignoreLocation: true, + threshold: 0.1, + includeMatches: true, + includeScore: true, + }; + fuse = new Fuse(data, options); + + $(e.target).removeClass("loading"); + }); + + // Use algolia autocomplete + var options = { + autoselect: true, + debug: true, + hint: false, + minLength: 2, + }; + var q; +async function searchFuse(query, callback) { + await fuse; + + var items; + if (!fuse) { + items = []; + } else { + q = query; + var results = fuse.search(query, { limit: 20 }); + items = results + .filter((x) => x.score <= 0.75) + .map((x) => x.item); + if (items.length === 0) { + items = [{dir:"Sorry 😿",previous_headings:"",title:"No results found.",what:"No results found.",path:window.location.href}]; + } + } + callback(items); +} + $("#search-input").autocomplete(options, [ + { + name: "content", + source: searchFuse, + templates: { + suggestion: (s) => { + if (s.title == s.what) { + return `${s.dir} >
    ${s.title}
    `; + } else if (s.previous_headings == "") { + return `${s.dir} >
    ${s.title}
    > ${s.what}`; + } else { + return `${s.dir} >
    ${s.title}
    > ${s.previous_headings} > ${s.what}`; + } + }, + }, + }, + ]).on('autocomplete:selected', function(event, s) { + window.location.href = s.path + "?q=" + q + "#" + s.id; + }); + }); })(window.jQuery || window.$) diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 7653385..7dfeb96 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -1,18 +1,19 @@ pandoc: 3.1.1 -pkgdown: 2.0.7 +pkgdown: 2.1.0 pkgdown_sha: ~ articles: check_poisson: check_poisson.html - gsm_perf: gsm_perf.html + funnel_perf: funnel_perf.html + inframe: inframe.html intro: intro.html over: over.html + performance: performance.html portfolio_perf: portfolio_perf.html sas_files: sas_files.html usability_limits: usability_limits.html visit_med75: visit_med75.html visits_or_days: visits_or_days.html -last_built: 2024-02-28T14:04Z +last_built: 2024-09-24T15:41Z urls: reference: https://openpharma.github.io/simaerep/reference article: https://openpharma.github.io/simaerep/articles - diff --git a/docs/reference/Rplot002.png b/docs/reference/Rplot002.png index 4ba7ebc..549c530 100644 Binary files a/docs/reference/Rplot002.png and b/docs/reference/Rplot002.png differ diff --git a/docs/reference/aggr_duplicated_visits.html b/docs/reference/aggr_duplicated_visits.html index 6d5a32d..3b6837d 100644 --- a/docs/reference/aggr_duplicated_visits.html +++ b/docs/reference/aggr_duplicated_visits.html @@ -1,145 +1,97 @@ -Aggregate duplicated visits. — aggr_duplicated_visits • simaerep - - -
  • +
    + + +
    +
    +
    +
    -
    +

    Internal function called by check_df_visit().

    -
    +
    +

    Usage

    aggr_duplicated_visits(df_visit)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe with columns: study_id, site_number, patnum, visit, n_ae

    -
    -

    Value

    - - -

    df_visit corrected

    +
    +

    Value

    +

    df_visit corrected

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/check_df_visit.html b/docs/reference/check_df_visit.html index 0fe1765..38647f7 100644 --- a/docs/reference/check_df_visit.html +++ b/docs/reference/check_df_visit.html @@ -1,130 +1,88 @@ -Integrity check for df_visit. — check_df_visit • simaerepIntegrity check for df_visit. — check_df_visit • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by all functions that accept df_visit as a parameter. Checks for NA columns, numeric visits and AEs, implicitly missing and duplicated visits.

    -
    +
    +

    Usage

    check_df_visit(df_visit)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe with columns: study_id, site_number, patnum, visit, n_ae

    -
    -

    Value

    - - -

    corrected df_visit

    +
    +

    Value

    +

    corrected df_visit

    -
    -

    Examples

    +
    +

    Examples

    
     df_visit <- sim_test_data_study(
       n_pat = 100,
    @@ -152,27 +110,23 @@ 

    Examples

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/eval_sites.html b/docs/reference/eval_sites.html index 6af5a06..2691856 100644 --- a/docs/reference/eval_sites.html +++ b/docs/reference/eval_sites.html @@ -1,136 +1,92 @@ -Evaluate sites. — eval_sites • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Correct under-reporting probabilities using p.adjust.

    -
    +
    +

    Usage

    eval_sites(df_sim_sites, method = "BH", under_only = TRUE, ...)
    -
    -

    Arguments

    -
    df_sim_sites
    +
    +

    Arguments

    + + +
    df_sim_sites

    dataframe generated by sim_sites

    -
    method
    +
    method

    character, passed to stats::p.adjust(), if NULL eval_sites_deprecated() is used instead, Default = "BH"

    -
    under_only
    +
    under_only

    compute under-reporting probabilities only, default = TRUE check_df_visit(), computationally expensive on large data sets. Default: TRUE

    -
    ...
    +
    ...

    use to pass r_sim_sites parameter to eval_sites_deprecated()

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number
    @@ -166,15 +122,15 @@

    Value

    -
    -

    See also

    + -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5,
         frac_site_with_ur = 0.4, ur_rate = 0.6)
     
    @@ -187,9 +143,9 @@ 

    Examples

    df_eval #> # A tibble: 5 × 14 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> -#> 1 A S0002 20 16 16 2.75 -#> 2 A S0001 20 16 15 2.62 +#> <chr> <chr> <int> <dbl> <dbl> <dbl> +#> 1 A S0001 20 16 15 2.62 +#> 2 A S0002 20 16 16 2.75 #> 3 A S0003 20 19 15 6.37 #> 4 A S0004 20 16 16 7.81 #> 5 A S0005 20 17 16 8.59 @@ -203,7 +159,7 @@

    Examples

    df_eval #> # A tibble: 5 × 19 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> +#> <chr> <chr> <int> <dbl> <dbl> <dbl> #> 1 A S0002 20 16 16 2.75 #> 2 A S0001 20 16 15 2.62 #> 3 A S0003 20 19 15 6.37 @@ -216,27 +172,23 @@

    Examples

    #> # prob_low_prob_ur <dbl>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/eval_sites_deprecated.html b/docs/reference/eval_sites_deprecated.html index f30c540..0b813e4 100644 --- a/docs/reference/eval_sites_deprecated.html +++ b/docs/reference/eval_sites_deprecated.html @@ -1,130 +1,88 @@ -Evaluate sites. — eval_sites_deprecated • simaerepEvaluate sites. — eval_sites_deprecated • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Correct under-reporting probabilities by the expected number of false positives (fp). This has been deprecated in favor of more conventional methods available via p.adjust.

    -
    +
    +

    Usage

    eval_sites_deprecated(df_sim_sites, r_sim_sites)
    -
    -

    Arguments

    -
    df_sim_sites
    +
    +

    Arguments

    + + +
    df_sim_sites

    dataframe generated by sim_sites()

    -
    r_sim_sites
    +
    r_sim_sites

    integer, number of repeats for bootstrap resampling for site simulation, needed for zero probability correction for fp calculation, Default: 1000

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number
    @@ -174,19 +132,19 @@

    Value

    -
    -

    Details

    +
    +

    Details

    If by chance expected number of false positives (fp) is greater than the total number of positives (p) we set p_vs_fp_ratio = 1 and prob_ur = 0.

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5,
         frac_site_with_ur = 0.4, ur_rate = 0.6)
     
    @@ -199,7 +157,7 @@ 

    Examples

    df_eval #> # A tibble: 5 × 19 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> +#> <chr> <chr> <int> <dbl> <dbl> <dbl> #> 1 A S0002 20 17 18 3.94 #> 2 A S0001 20 18 16 3.39 #> 3 A S0003 20 18 17 10.2 @@ -212,27 +170,23 @@

    Examples

    #> # prob_low_prob_ur <dbl>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/exp_implicit_missing_visits.html b/docs/reference/exp_implicit_missing_visits.html index 4c2c72e..73e313c 100644 --- a/docs/reference/exp_implicit_missing_visits.html +++ b/docs/reference/exp_implicit_missing_visits.html @@ -1,145 +1,97 @@ -Expose implicitly missing visits. — exp_implicit_missing_visits • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function called by check_df_visit().

    -
    +
    +

    Usage

    exp_implicit_missing_visits(df_visit)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe with columns: study_id, site_number, patnum, visit, n_ae

    -
    -

    Value

    - - -

    df_visit corrected

    +
    +

    Value

    +

    df_visit corrected

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_config.html b/docs/reference/get_config.html index ca8d27a..34d2c50 100644 --- a/docs/reference/get_config.html +++ b/docs/reference/get_config.html @@ -1,107 +1,66 @@ -Get Portfolio Configuration — get_config • simaerepGet Portfolio Configuration — get_config • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Get Portfolio configuration from a dataframe aggregated on patient level with max_ae and max_visit. Will filter studies with only a few sites and patients and will anonymize IDs. Portfolio configuration can be @@ -109,7 +68,8 @@

    Get Portfolio Configuration

    artificial portfolio.

    -
    +
    +

    Usage

    get_config(
       df_site,
       min_pat_per_study = 100,
    @@ -119,33 +79,33 @@ 

    Get Portfolio Configuration

    )
    -
    -

    Arguments

    -
    df_site
    +
    +

    Arguments

    + + +
    df_site

    dataframe aggregated on patient level with max_ae and max_visit

    -
    min_pat_per_study
    +
    min_pat_per_study

    minimum number of patients per study, Default: 100

    -
    min_sites_per_study
    +
    min_sites_per_study

    minimum number of sites per study, Default: 10

    -
    anonymize
    +
    anonymize

    logical, Default: TRUE

    -
    pad_width
    +
    pad_width

    padding width for newly created IDs, Default: 4

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    ae_per_visit_mean

    mean @@ -161,10 +121,10 @@

    Value

    n_pat

    number of patients

    - +
    -
    -

    See also

    +
    +

    See also

    sim_test_data_study get_config sim_test_data_portfolio @@ -172,8 +132,8 @@

    See also

    get_portf_perf

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10,
                                      frac_site_with_ur = 0.4, ur_rate = 0.6)
    @@ -251,22 +211,23 @@ 

    Examples

    df_scen #> # A tibble: 140 × 14 -#> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> -#> 1 0001 0001 10 9 14 5.78 -#> 2 0001 0001 10 9 14 2.89 -#> 3 0001 0001 10 9 14 0 -#> 4 0001 0001 10 9 14 2.89 -#> 5 0001 0001 10 9 14 0 -#> 6 0001 0001 10 9 14 2.89 -#> 7 0001 0001 10 9 14 0 -#> 8 0001 0002 10 9 15 5.78 -#> 9 0001 0002 10 9 15 2.89 -#> 10 0001 0002 10 9 15 0 +#> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat +#> <chr> <chr> <dbl> <dbl> <dbl> <int> +#> 1 0001 0001 0 0 0 10 +#> 2 0001 0001 0 0.0989 0.5 10 +#> 3 0001 0001 0 0.0989 1 10 +#> 4 0001 0001 1 0.199 0.5 10 +#> 5 0001 0001 1 0.199 1 10 +#> 6 0001 0001 2 0.299 0.5 10 +#> 7 0001 0001 2 0.299 1 10 +#> 8 0001 0002 0 0 0 10 +#> 9 0001 0002 0 0.107 0.5 10 +#> 10 0001 0002 0 0.107 1 10 #> # ℹ 130 more rows -#> # ℹ 8 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>, -#> # extra_ur_sites <dbl>, frac_pat_with_ur <dbl>, ur_rate <dbl>, -#> # prob_low <dbl>, prob_low_adj <dbl>, prob_low_prob_ur <dbl> +#> # ℹ 8 more variables: n_pat_with_med75 <dbl>, visit_med75 <dbl>, +#> # mean_ae_site_med75 <dbl>, mean_ae_study_med75 <dbl>, +#> # n_pat_with_med75_study <int>, prob_low <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl> df_perf <- get_portf_perf(df_scen) @@ -274,41 +235,37 @@

    Examples

    #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> <dbl> <dbl> <dbl> <dbl> <dbl> -#> 1 0.001 0.925 0 0 0.05 -#> 2 0.001 0.925 1 0 0.05 -#> 3 0.001 0.925 2 0 0.05 -#> 4 0.001 0.925 0 0.5 1 -#> 5 0.001 0.925 1 0.5 1 -#> 6 0.001 0.925 2 0.5 1 -#> 7 0.001 0.925 0 1 1 -#> 8 0.001 0.925 1 1 1 -#> 9 0.001 0.925 2 1 1 -#> 10 0.01 0.920 0 0 0.05 +#> 1 0.001 0.53 0 0 0.2 +#> 2 0.001 0.53 1 0 0.2 +#> 3 0.001 0.53 2 0 0.2 +#> 4 0.001 0.53 0 0.5 1 +#> 5 0.001 0.53 1 0.5 1 +#> 6 0.001 0.53 2 0.5 1 +#> 7 0.001 0.53 0 1 1 +#> 8 0.001 0.53 1 1 1 +#> 9 0.001 0.53 2 1 1 +#> 10 0.01 0.53 0 0 0.2 #> # ℹ 17 more rows # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_ecd_values.html b/docs/reference/get_ecd_values.html index 0d1a6a2..707ed75 100644 --- a/docs/reference/get_ecd_values.html +++ b/docs/reference/get_ecd_values.html @@ -1,134 +1,94 @@ -Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values • simaerepGet empirical cumulative distribution values of pval or prob_lower — get_ecd_values • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Test function, test applicability of poisson test, by calculating

    • the bootstrapped probability of obtaining a specific p-value or lower, use in combination with sim_studies().

    -
    +
    +

    Usage

    get_ecd_values(df_sim_studies, df_sim_sites, val_str)
    -
    -

    Arguments

    -
    df_sim_studies
    +
    +

    Arguments

    + + +
    df_sim_studies

    dataframe, generated by sim_studies()

    -
    df_sim_sites
    +
    df_sim_sites

    dataframe, generated by sim_sites()

    -
    val_str
    +
    val_str

    c("prob_low","pval")

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number
    @@ -151,14 +111,14 @@

    Value

    -
    -

    Details

    +
    +

    Details

    trains a ecdf function for each studies based on the results of sim_studies()

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5,
         frac_site_with_ur = 0.4, ur_rate = 0.3)
     
    @@ -179,7 +139,7 @@ 

    Examples

    get_ecd_values(df_sim_studies, df_sim_sites, "prob_low") #> # A tibble: 5 × 11 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> +#> <chr> <chr> <int> <dbl> <dbl> <dbl> #> 1 A S0001 20 18 17 5.89 #> 2 A S0002 20 18 15 5.5 #> 3 A S0003 20 17 16 8.53 @@ -190,7 +150,7 @@

    Examples

    get_ecd_values(df_sim_studies, df_sim_sites, "pval") #> # A tibble: 5 × 11 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> +#> <chr> <chr> <int> <dbl> <dbl> <dbl> #> 1 A S0001 20 18 17 5.89 #> 2 A S0002 20 18 15 5.5 #> 3 A S0003 20 17 16 8.53 @@ -200,27 +160,23 @@

    Examples

    #> # pval <dbl>, prob_low <dbl>, pval_ecd <dbl>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_legend.html b/docs/reference/get_legend.html index edfdd6d..2a3ee6f 100644 --- a/docs/reference/get_legend.html +++ b/docs/reference/get_legend.html @@ -1,137 +1,86 @@ -replace cowplot::get_legend, to silence warning -Multiple components found; returning the first one. To return all, use `return_all = TRUE — get_legend • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    replace cowplot::get_legend, to silence warning Multiple components found; returning the first one. To return all, use `return_all = TRUE

    -
    +
    +

    Usage

    get_legend(p)
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_pat_pool_config.html b/docs/reference/get_pat_pool_config.html index 4b66ad0..49a254c 100644 --- a/docs/reference/get_pat_pool_config.html +++ b/docs/reference/get_pat_pool_config.html @@ -1,139 +1,95 @@ -Configure study patient pool by site parameters. — get_pat_pool_config • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal Function used by sim_sites()

    -
    +
    +

    Usage

    get_pat_pool_config(df_visit, df_site, min_n_pat_with_med75 = 1)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe

    -
    df_site
    +
    df_site

    dataframe as created by site_aggr()

    -
    min_n_pat_with_med75
    +
    min_n_pat_with_med75

    minimum number of patients with visit_med_75 for simulation, Default: 1

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    -

    Details

    +
    +

    Details

    For simulating a study we need to configure the study patient pool to match the configuration of the sites

    -
    -

    Examples

    +
    +

    Examples

    df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 5,
                                           frac_site_with_ur = 0.4, ur_rate = 0.6)
     
    @@ -153,7 +109,7 @@ 

    Examples

    df_config #> # A tibble: 8 × 6 #> study_id site_number visit_med75 n_pat_with_med75 n_pat_study pat_pool -#> <chr> <chr> <dbl> <int> <dbl> <list> +#> <chr> <chr> <dbl> <dbl> <dbl> <list> #> 1 A S0001 15 19 71 <tibble> #> 2 A S0002 15 18 72 <tibble> #> 3 A S0003 16 17 66 <tibble> @@ -164,27 +120,23 @@

    Examples

    #> 8 B S0003 15 301 603 <tibble>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_portf_perf.html b/docs/reference/get_portf_perf.html index 839363a..5ab2ada 100644 --- a/docs/reference/get_portf_perf.html +++ b/docs/reference/get_portf_perf.html @@ -1,147 +1,104 @@ -Get Portfolio Performance — get_portf_perf • simaerep - - -
    -
    -
    - +
    +
    +
    -
    +

    Performance as true positive rate (tpr as tp/P) on the basis of desired false positive rates (fpr as fp/P).

    -
    +
    +

    Usage

    get_portf_perf(df_scen, stat = "prob_low_prob_ur", fpr = c(0.001, 0.01, 0.05))
    -
    -

    Arguments

    -
    df_scen
    +
    +

    Arguments

    + + +
    df_scen

    dataframe as returned by sim_ur_scenarios

    -
    stat
    +
    stat

    character denoting the column name of the under-reporting statistic, Default: 'prob_low_prob_ur'

    -
    fpr
    +
    fpr

    numeric vector specifying false positive rates, Default: c(0.001, 0.01, 0.05)

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    -

    Details

    +
    +

    Details

    DETAILS

    -
    -

    See also

    + -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10,
                                      frac_site_with_ur = 0.4, ur_rate = 0.6)
    @@ -219,22 +176,23 @@ 

    Examples

    df_scen #> # A tibble: 140 × 14 -#> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> -#> 1 0001 0001 10 10 15 5.1 -#> 2 0001 0001 10 10 15 2.55 -#> 3 0001 0001 10 10 15 0 -#> 4 0001 0001 10 10 15 2.55 -#> 5 0001 0001 10 10 15 0 -#> 6 0001 0001 10 10 15 2.55 -#> 7 0001 0001 10 10 15 0 -#> 8 0001 0002 10 10 16 6.5 -#> 9 0001 0002 10 10 16 3.25 -#> 10 0001 0002 10 10 16 0 +#> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat +#> <chr> <chr> <dbl> <dbl> <dbl> <int> +#> 1 0001 0001 0 0 0 10 +#> 2 0001 0001 0 0.111 0.5 10 +#> 3 0001 0001 0 0.111 1 10 +#> 4 0001 0001 1 0.211 0.5 10 +#> 5 0001 0001 1 0.211 1 10 +#> 6 0001 0001 2 0.311 0.5 10 +#> 7 0001 0001 2 0.311 1 10 +#> 8 0001 0002 0 0 0 10 +#> 9 0001 0002 0 0.120 0.5 10 +#> 10 0001 0002 0 0.120 1 10 #> # ℹ 130 more rows -#> # ℹ 8 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>, -#> # extra_ur_sites <dbl>, frac_pat_with_ur <dbl>, ur_rate <dbl>, -#> # prob_low <dbl>, prob_low_adj <dbl>, prob_low_prob_ur <dbl> +#> # ℹ 8 more variables: n_pat_with_med75 <dbl>, visit_med75 <dbl>, +#> # mean_ae_site_med75 <dbl>, mean_ae_study_med75 <dbl>, +#> # n_pat_with_med75_study <int>, prob_low <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl> df_perf <- get_portf_perf(df_scen) @@ -242,41 +200,37 @@

    Examples

    #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> <dbl> <dbl> <dbl> <dbl> <dbl> -#> 1 0.001 0.983 0 0 0.05 -#> 2 0.001 0.983 1 0 0.05 -#> 3 0.001 0.983 2 0 0.05 -#> 4 0.001 0.983 0 0.5 0.95 -#> 5 0.001 0.983 1 0.5 0.95 -#> 6 0.001 0.983 2 0.5 0.9 -#> 7 0.001 0.983 0 1 1 -#> 8 0.001 0.983 1 1 1 -#> 9 0.001 0.983 2 1 1 -#> 10 0.01 0.963 0 0 0.05 +#> 1 0.001 0.872 0 0 0.05 +#> 2 0.001 0.872 1 0 0.05 +#> 3 0.001 0.872 2 0 0.05 +#> 4 0.001 0.872 0 0.5 1 +#> 5 0.001 0.872 1 0.5 0.95 +#> 6 0.001 0.872 2 0.5 0.95 +#> 7 0.001 0.872 0 1 1 +#> 8 0.001 0.872 1 1 1 +#> 9 0.001 0.872 2 1 1 +#> 10 0.01 0.799 0 0 0.05 #> # ℹ 17 more rows # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_site_mean_ae_dev.html b/docs/reference/get_site_mean_ae_dev.html index 3ba87ca..9fe8bc4 100644 --- a/docs/reference/get_site_mean_ae_dev.html +++ b/docs/reference/get_site_mean_ae_dev.html @@ -1,154 +1,107 @@ -Get site mean ae development. — get_site_mean_ae_dev • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by site_aggr(), plot_visit_med75(), returns mean AE development from visit 0 to visit_med75.

    -
    +
    +

    Usage

    get_site_mean_ae_dev(df_visit, df_pat, df_site)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe

    -
    df_pat
    +
    df_pat

    dataframe as returned by pat_aggr()

    -
    df_site
    +
    df_site

    dataframe as returned by site_aggr()

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/get_visit_med75.html b/docs/reference/get_visit_med75.html index afea8d3..77bf395 100644 --- a/docs/reference/get_visit_med75.html +++ b/docs/reference/get_visit_med75.html @@ -1,154 +1,106 @@ -Get visit_med75. — get_visit_med75 • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by site_aggr().

    -
    +
    +

    Usage

    get_visit_med75(df_pat, method = "med75_adj", min_pat_pool = 0.2)
    -
    -

    Arguments

    -
    df_pat
    +
    +

    Arguments

    + + +
    df_pat

    dataframe as returned by pat_aggr()

    -
    method
    +
    method

    character, one of c("med75", "med75_adj") defining method for defining evaluation point visit_med75 (see details), Default: "med75_adj"

    -
    min_pat_pool
    +
    min_pat_pool

    double, minimum ratio of available patients available for sampling. Determines maximum visit_med75 value see Details. Default: 0.2

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/index.html b/docs/reference/index.html index 16c5b21..ef6be9b 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -1,299 +1,396 @@ -Function reference • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    S3 Interface

    -

    -
    -

    simaerep()

    -

    create simaerep object

    -

    plot(<simaerep>)

    -

    plot AE under-reporting simulation results

    -

    Main Functions

    -

    -
    -

    site_aggr()

    -

    Aggregate from visit to site level.

    -

    sim_sites()

    -

    Calculate prob_lower and poisson.test pvalue for study sites.

    -

    eval_sites()

    -

    Evaluate sites.

    -

    plot_study()

    -

    Plot ae development of study and sites highlighting at risk sites.

    -

    check_df_visit()

    -

    Integrity check for df_visit.

    -

    Simulate Data

    -

    -
    -

    sim_test_data_study()

    -

    simulate study test data

    -

    get_config()

    -

    Get Portfolio Configuration

    -

    sim_test_data_portfolio()

    -

    Simulate Portfolio Test Data

    -

    Test Functions

    -

    These functions are intented to check simaerep performance.

    -
    -

    sim_studies()

    -

    Simulate studies.

    -

    sim_ur_scenarios()

    -

    Simulate Under-Reporting Scenarios

    -

    get_portf_perf()

    -

    Get Portfolio Performance

    -

    Helper Functions

    -

    These functions are intended to be called by other functions.

    -
    -

    get_pat_pool_config()

    -

    Configure study patient pool by site parameters.

    -

    pat_pool()

    -

    Create a study specific patient pool for sampling

    -

    poiss_test_site_ae_vs_study_ae()

    -

    Poisson test for vector with site AEs vs vector with study AEs.

    -

    prob_lower_site_ae_vs_study_ae()

    -

    Calculate bootstrapped probability for obtaining a lower site mean AE number.

    -

    sim_test_data_patient()

    -

    simulate patient ae reporting test data

    -

    get_visit_med75()

    -

    Get visit_med75.

    -

    pat_aggr()

    -

    Aggregate visit to patient level.

    -

    get_site_mean_ae_dev()

    -

    Get site mean ae development.

    -

    aggr_duplicated_visits()

    -

    Aggregate duplicated visits.

    -

    exp_implicit_missing_visits()

    -

    Expose implicitly missing visits.

    -

    prep_for_sim()

    -

    Prepare data for simulation.

    -

    sim_after_prep()

    -

    Start simulation after preparation.

    -

    get_ecd_values()

    -

    Get empirical cumulative distribution values of pval or prob_lower

    -

    purrr_bar()

    -

    Execute a purrr or furrr function with a progress -bar.

    -

    with_progress_cnd()

    -

    Conditional with_progress.

    -

    sim_scenario()

    -

    simulate single scenario

    -

    orivisit()

    -

    create orivisit object

    -

    is_simaerep()

    -

    is simaerep class

    -

    is_orivisit()

    -

    is orivisit class

    -

    Deprecated

    -

    -
    -

    eval_sites_deprecated()

    -

    Evaluate sites.

    -

    Additional Plot Functions

    -

    These functions are intended for creating plots for the documentation.

    -
    -

    plot_visit_med75()

    -

    Plot patient visits against visit_med75.

    -

    plot_sim_examples()

    -

    Plot multiple simulation examples.

    -

    plot_sim_example()

    -

    Plot simulation example.

    -

    plot_dots()

    -

    Plots AE per site as dots.

    - - -
    +
    +

    S3 Interface

    + + + + +
    + + + + +
    + + simaerep() + +
    +
    Create simaerep object
    +
    + + plot(<simaerep>) + +
    +
    plot AE under-reporting simulation results
    +
    +

    Main Functions

    + + + + +
    + + + + +
    + + site_aggr() + +
    +
    Aggregate from visit to site level.
    +
    + + sim_sites() + +
    +
    Calculate prob_lower and poisson.test pvalue for study sites.
    +
    + + sim_inframe() + +
    +
    Calculate prob_lower for study sites using table operations
    +
    + + eval_sites() + +
    +
    Evaluate sites.
    +
    + + prune_to_visit_med75_inframe() + +
    +
    prune visits to visit_med75 using table operations
    +
    + + plot_study() + +
    +
    Plot ae development of study and sites highlighting at risk sites.
    +
    + + check_df_visit() + +
    +
    Integrity check for df_visit.
    +
    +

    Simulate Data

    + + + + +
    + + + + +
    + + sim_test_data_study() + +
    +
    simulate study test data
    +
    + + get_config() + +
    +
    Get Portfolio Configuration
    +
    + + sim_test_data_portfolio() + +
    +
    Simulate Portfolio Test Data
    +
    +

    Test Functions

    + +

    These functions are intented to check simaerep performance.

    + + +
    + + + + +
    + + sim_studies() + +
    +
    Simulate studies.
    +
    + + sim_ur_scenarios() + +
    +
    Simulate Under-Reporting Scenarios
    +
    + + sim_ur() + +
    +
    simulate under-reporting
    +
    + + get_portf_perf() + +
    +
    Get Portfolio Performance
    +
    +

    Helper Functions

    +

    These functions are intended to be called by other functions.

    -
    + + +
    -
    -

    Site built with pkgdown 2.0.7.

    + -
    +
    + - - diff --git a/docs/reference/is_orivisit.html b/docs/reference/is_orivisit.html index 1ec2512..f9d273f 100644 --- a/docs/reference/is_orivisit.html +++ b/docs/reference/is_orivisit.html @@ -1,144 +1,96 @@ -is orivisit class — is_orivisit • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    internal function

    -
    +
    +

    Usage

    is_orivisit(x)
    -
    -

    Arguments

    -
    x
    +
    +

    Arguments

    + + +
    x

    object

    -
    -

    Value

    - - -

    logical

    +
    +

    Value

    +

    logical

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/is_simaerep.html b/docs/reference/is_simaerep.html index c465739..2ae3ae2 100644 --- a/docs/reference/is_simaerep.html +++ b/docs/reference/is_simaerep.html @@ -1,144 +1,96 @@ -is simaerep class — is_simaerep • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    internal function

    -
    +
    +

    Usage

    is_simaerep(x)
    -
    -

    Arguments

    -
    x
    +
    +

    Arguments

    + + +
    x

    object

    -
    -

    Value

    - - -

    logical

    +
    +

    Value

    +

    logical

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/max_rank.html b/docs/reference/max_rank.html new file mode 100644 index 0000000..c673e32 --- /dev/null +++ b/docs/reference/max_rank.html @@ -0,0 +1,145 @@ + +Calculate Max Rank — max_rank • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    like rank() with ties.method = "max", works on tbl objects

    +
    + +
    +

    Usage

    +
    max_rank(df, col, col_new)
    +
    + +
    +

    Arguments

    + + +
    df
    +

    dataframe

    + + +
    col
    +

    character column name to rank y

    + + +
    col_new
    +

    character column name for rankings

    + +
    +
    +

    Details

    +

    this is needed for hochberg p value adjustment. We need to assign higher +rank when multiple sites have same p value

    +
    + +
    +

    Examples

    +
    
    +df <- tibble::tibble(s = c(1, 2, 2, 2, 5, 10)) %>%
    + dplyr::mutate(
    +   rank = rank(s, ties.method = "max")
    + )
    +
    +df %>%
    + max_rank("s", "max_rank")
    +#> # A tibble: 6 × 3
    +#>       s  rank max_rank
    +#>   <dbl> <int>    <int>
    +#> 1     1     1        1
    +#> 2     2     4        4
    +#> 3     2     4        4
    +#> 4     2     4        4
    +#> 5     5     5        5
    +#> 6    10     6        6
    +# \donttest{
    +# Database
    +con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:")
    +
    +dplyr::copy_to(con, df, "df")
    +max_rank(dplyr::tbl(con, "df"), "s", "max_rank")
    +#> # Source:   SQL [6 x 3]
    +#> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:]
    +#>       s  rank max_rank
    +#>   <dbl> <int>    <dbl>
    +#> 1    10     6        6
    +#> 2     2     4        4
    +#> 3     2     4        4
    +#> 4     2     4        4
    +#> 5     1     1        1
    +#> 6     5     5        5
    +
    +DBI::dbDisconnect(con)
    +# }
    +
    +
    +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/orivisit.html b/docs/reference/orivisit.html index 5b9eed6..70f4a65 100644 --- a/docs/reference/orivisit.html +++ b/docs/reference/orivisit.html @@ -1,141 +1,98 @@ -create orivisit object — orivisit • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal S3 object, stores lazy reference to original visit data.

    -
    +
    +

    Usage

    orivisit(df_visit, call = NULL, env = parent.frame())
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe with original visit data

    -
    call
    +
    call

    optional, provide call, Default: NULL

    -
    env
    +
    env

    optional, provide environment of original visit data, Default: parent.frame()

    -
    -

    Value

    - - -

    orivisit object

    +
    +

    Value

    +

    orivisit object

    -
    -

    Details

    +
    +

    Details

    Saves variable name of original visit data, checks whether it can be retrieved from parent environment and stores summary. Original data can be retrieved using as.data.frame(x).

    -
    -

    Examples

    +
    +

    Examples

    
     df_visit <- sim_test_data_study(
       n_pat = 100,
    @@ -149,12 +106,12 @@ 

    Examples

    visit <- orivisit(df_visit) object.size(df_visit) -#> 126128 bytes +#> 125288 bytes object.size(visit) #> 2288 bytes as.data.frame(visit) -#> # A tibble: 1,970 × 9 +#> # A tibble: 1,956 × 9 #> patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit #> <chr> <chr> <lgl> <dbl> <dbl> <dbl> <int> #> 1 P000001 S0001 TRUE 20 4 0.2 1 @@ -167,32 +124,28 @@

    Examples

    #> 8 P000001 S0001 TRUE 20 4 0.2 8 #> 9 P000001 S0001 TRUE 20 4 0.2 9 #> 10 P000001 S0001 TRUE 20 4 0.2 10 -#> # ℹ 1,960 more rows +#> # ℹ 1,946 more rows #> # ℹ 2 more variables: n_ae <int>, study_id <chr>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/p_adjust_bh_inframe.html b/docs/reference/p_adjust_bh_inframe.html new file mode 100644 index 0000000..96e4044 --- /dev/null +++ b/docs/reference/p_adjust_bh_inframe.html @@ -0,0 +1,83 @@ + +benjamini hochberg p value correction using table operations — p_adjust_bh_inframe • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    benjamini hochberg p value correction using table operations

    +
    + +
    +

    Usage

    +
    p_adjust_bh_inframe(df_eval, col, suffix)
    +
    + + +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/pat_aggr.html b/docs/reference/pat_aggr.html index eb08abc..dfe17a6 100644 --- a/docs/reference/pat_aggr.html +++ b/docs/reference/pat_aggr.html @@ -1,146 +1,99 @@ -Aggregate visit to patient level. — pat_aggr • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by site_aggr() and plot_visit_med75(), adds the maximum visit for each patient.

    -
    +
    +

    Usage

    pat_aggr(df_visit)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/pat_pool.html b/docs/reference/pat_pool.html index 9569fa3..b21d530 100644 --- a/docs/reference/pat_pool.html +++ b/docs/reference/pat_pool.html @@ -1,135 +1,94 @@ -Create a study specific patient pool for sampling — pat_pool • simaerepCreate a study specific patient pool for sampling — pat_pool • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function for sim_sites, filter all visits greater than max_visit_med75_study returns dataframe with one column for studies and one column with nested patient data.

    -
    +
    +

    Usage

    pat_pool(df_visit, df_site)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe, created by sim_sites

    -
    df_site
    +
    df_site

    dataframe created by site_aggr

    -
    -

    Value

    - - -

    dataframe with nested pat_pool column

    +
    +

    Value

    +

    dataframe with nested pat_pool column

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(
       n_pat = 100,
       n_sites = 5,
    @@ -150,27 +109,23 @@ 

    Examples

    #> 1 A <tibble [1,637 × 3]>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/pipe.html b/docs/reference/pipe.html index 175d768..7b0d52c 100644 --- a/docs/reference/pipe.html +++ b/docs/reference/pipe.html @@ -1,138 +1,88 @@ -Pipe operator — %>% • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    See magrittr::%>% for details.

    -
    +
    +

    Usage

    lhs %>% rhs
    -
    -

    Value

    - - -

    returns output of rhs function

    +
    +

    Value

    +

    returns output of rhs function

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot.simaerep-1.png b/docs/reference/plot.simaerep-1.png index 11d9fb8..9222fc8 100644 Binary files a/docs/reference/plot.simaerep-1.png and b/docs/reference/plot.simaerep-1.png differ diff --git a/docs/reference/plot.simaerep-2.png b/docs/reference/plot.simaerep-2.png index ad6d80b..704b93c 100644 Binary files a/docs/reference/plot.simaerep-2.png and b/docs/reference/plot.simaerep-2.png differ diff --git a/docs/reference/plot.simaerep.html b/docs/reference/plot.simaerep.html index 38e459e..518be86 100644 --- a/docs/reference/plot.simaerep.html +++ b/docs/reference/plot.simaerep.html @@ -1,108 +1,64 @@ -plot AE under-reporting simulation results — plot.simaerep • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    generic plot function for simaerep objects

    -
    -
    # S3 method for simaerep
    +    
    +

    Usage

    +
    # S3 method for class 'simaerep'
     plot(
       x,
       ...,
    @@ -114,54 +70,54 @@ 

    plot AE under-reporting simulation results

    )
    -
    -

    Arguments

    -
    x
    +
    +

    Arguments

    + + +
    x

    simaerep object

    -
    ...
    +
    ...

    additional parameters passed to plot_study() or plot_visit_med75()

    -
    study
    +
    study

    character specifying study to be plotted, Default: NULL

    -
    what
    +
    what

    one of c("ur", "med75"), specifying whether to plot site AE under-reporting or visit_med75 values, Default: 'ur'

    -
    n_sites
    +
    n_sites

    number of sites to plot, Default: 16

    -
    df_visit
    +
    df_visit

    optional, pass original visit data if it cannot be retrieved from parent environment, Default: NULL

    -
    env
    +
    env

    optional, pass environment from which to retrieve original visit data, Default: parent.frame()

    -
    -

    Value

    - - -

    ggplot object

    +
    +

    Value

    +

    ggplot object

    -
    -

    Details

    +
    +

    Details

    see plot_study() and plot_visit_med75()

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit <- sim_test_data_study(
       n_pat = 100,
    @@ -188,27 +144,23 @@ 

    Examples

    # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot_dots-1.png b/docs/reference/plot_dots-1.png index 15e197b..e8255d4 100644 Binary files a/docs/reference/plot_dots-1.png and b/docs/reference/plot_dots-1.png differ diff --git a/docs/reference/plot_dots.html b/docs/reference/plot_dots.html index 7c36095..f6a9eb1 100644 --- a/docs/reference/plot_dots.html +++ b/docs/reference/plot_dots.html @@ -1,107 +1,63 @@ -Plots AE per site as dots. — plot_dots • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    This plot is meant to supplement the package documentation.

    -
    +
    +

    Usage

    plot_dots(
       df,
       nrow = 10,
    @@ -117,61 +73,61 @@ 

    Plots AE per site as dots.

    )
    -
    -

    Arguments

    -
    df
    +
    +

    Arguments

    + + +
    df

    dataframe, cols = c('site', 'patients', 'n_ae')

    -
    nrow
    +
    nrow

    integer, number of rows, Default: 10

    -
    ncols
    +
    ncols

    integer, number of columns, Default: 10

    -
    col_group
    +
    col_group

    character, grouping column, Default: 'site'

    -
    thresh
    +
    thresh

    numeric, threshold to determine color of mean_ae annotation, Default: NULL

    -
    color_site_a
    +
    color_site_a

    character, hex color value, Default: '#BDBDBD'

    -
    color_site_b
    +
    color_site_b

    character, hex color value, Default: '#757575'

    -
    color_site_c
    +
    color_site_c

    character, hex color value, Default: 'gold3'

    -
    color_high
    +
    color_high

    character, hex color value, Default: '#00695C'

    -
    color_low
    +
    color_low

    character, hex color value, Default: '#25A69A'

    -
    size_dots
    +
    size_dots

    integer, Default: 10

    -
    -

    Value

    - - -

    ggplot object

    +
    +

    Value

    +

    ggplot object

    -
    -

    Examples

    +
    +

    Examples

    study <- tibble::tibble(
       site = LETTERS[1:3],
       patients = c(list(seq(1, 50, 1)), list(seq(1, 40, 1)), list(seq(1, 10, 1)))
    @@ -183,27 +139,23 @@ 

    Examples

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot_sim_example-1.png b/docs/reference/plot_sim_example-1.png index 316507a..ac7ca58 100644 Binary files a/docs/reference/plot_sim_example-1.png and b/docs/reference/plot_sim_example-1.png differ diff --git a/docs/reference/plot_sim_example.html b/docs/reference/plot_sim_example.html index b23ae83..7d5f748 100644 --- a/docs/reference/plot_sim_example.html +++ b/docs/reference/plot_sim_example.html @@ -1,107 +1,63 @@ -Plot simulation example. — plot_sim_example • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    This plots supplements the package documentation.

    -
    +
    +

    Usage

    plot_sim_example(
       substract_ae_per_pat = 0,
       size_dots = 10,
    @@ -117,97 +73,93 @@ 

    Plot simulation example.

    )
    -
    -

    Arguments

    -
    substract_ae_per_pat
    +
    +

    Arguments

    + + +
    substract_ae_per_pat

    integer, subtract aes from patients at site C, Default: 0

    -
    size_dots
    +
    size_dots

    integer, Default: 10

    -
    size_raster_label
    +
    size_raster_label

    integer, Default: 12

    -
    color_site_a
    +
    color_site_a

    character, hex color value, Default: '#BDBDBD'

    -
    color_site_b
    +
    color_site_b

    character, hex color value, Default: '#757575'

    -
    color_site_c
    +
    color_site_c

    character, hex color value, Default: 'gold3'

    -
    color_high
    +
    color_high

    character, hex color value, Default: '#00695C'

    -
    color_low
    +
    color_low

    character, hex color value, Default: '#25A69A'

    -
    title
    +
    title

    logical, include title, Default: T

    -
    legend
    +
    legend

    logical, include legend, Default: T

    -
    seed
    +
    seed

    pass seed for simulations Default: 5

    -
    -

    Value

    - - -

    ggplot

    +
    +

    Value

    +

    ggplot

    -
    -

    Details

    +
    +

    Details

    uses plot_dots() and adds 2 simulation panels, uses made-up site config with three sites A,B,C simulating site C

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     plot_sim_example(size_dots = 5)
     
     # }
     
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot_sim_examples-1.png b/docs/reference/plot_sim_examples-1.png index 8e1b8c6..c6fc4c8 100644 Binary files a/docs/reference/plot_sim_examples-1.png and b/docs/reference/plot_sim_examples-1.png differ diff --git a/docs/reference/plot_sim_examples-2.png b/docs/reference/plot_sim_examples-2.png index 7f05a85..1f877b4 100644 Binary files a/docs/reference/plot_sim_examples-2.png and b/docs/reference/plot_sim_examples-2.png differ diff --git a/docs/reference/plot_sim_examples.html b/docs/reference/plot_sim_examples.html index 0871caa..4370af3 100644 --- a/docs/reference/plot_sim_examples.html +++ b/docs/reference/plot_sim_examples.html @@ -1,137 +1,93 @@ -Plot multiple simulation examples. — plot_sim_examples • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    This plot is meant to supplement the package documentation.

    -
    +
    +

    Usage

    plot_sim_examples(substract_ae_per_pat = c(0, 1, 3), ...)
    -
    -

    Arguments

    -
    substract_ae_per_pat
    +
    +

    Arguments

    + + +
    substract_ae_per_pat

    integer, Default: c(0, 1, 3)

    -
    ...
    +
    ...

    parameters passed to plot_sim_example()

    -
    -

    Value

    - - -

    ggplot

    +
    +

    Value

    +

    ggplot

    -
    -

    Details

    +
    +

    Details

    This function is a wrapper for plot_sim_example()

    -
    -

    See also

    + -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     plot_sim_examples(size_dot = 3, size_raster_label = 10)
     
    @@ -140,27 +96,23 @@ 

    Examples

    # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot_study-1.png b/docs/reference/plot_study-1.png index a3edc27..fbfa662 100644 Binary files a/docs/reference/plot_study-1.png and b/docs/reference/plot_study-1.png differ diff --git a/docs/reference/plot_study.html b/docs/reference/plot_study.html index c244bf0..ff62400 100644 --- a/docs/reference/plot_study.html +++ b/docs/reference/plot_study.html @@ -1,107 +1,63 @@ -Plot ae development of study and sites highlighting at risk sites. — plot_study • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Most suitable visual representation of the AE under-reporting statistics.

    -
    +
    +

    Usage

    plot_study(
       df_visit,
       df_site,
    @@ -114,49 +70,49 @@ 

    Plot ae development of study and sites highlighting at risk sites.

    )
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe, created by sim_sites()

    -
    df_site
    +
    df_site

    dataframe created by site_aggr()

    -
    df_eval
    +
    df_eval

    dataframe created by eval_sites()

    -
    study
    +
    study

    study

    -
    df_al
    +
    df_al

    dataframe containing study_id, site_number, alert_level_site, alert_level_study (optional), Default: NA

    -
    n_sites
    +
    n_sites

    integer number of most at risk sites, Default: 16

    -
    pval
    +
    pval

    logical show p-value, Default:FALSE

    -
    prob_col
    +
    prob_col

    character, denotes probability column, Default: "prob_low_prob_ur"

    -
    -

    Value

    - - -

    ggplot

    +
    +

    Value

    +

    ggplot

    -
    -

    Details

    +
    +

    Details

    Left panel shows mean AE reporting per site (lightblue and darkblue lines) against mean AE reporting of the entire study (golden line). Single sites are plotted in descending order by AE under-reporting probability on @@ -169,8 +125,8 @@

    Details

    reach the evaluation point (visit_med75) will be ignored.

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit <- sim_test_data_study(n_pat = 1000, n_sites = 10,
         frac_site_with_ur = 0.2, ur_rate = 0.15, max_visit_sd = 8)
    @@ -187,27 +143,23 @@ 

    Examples

    # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/plot_visit_med75-1.png b/docs/reference/plot_visit_med75-1.png index f1e87b0..15c8ccb 100644 Binary files a/docs/reference/plot_visit_med75-1.png and b/docs/reference/plot_visit_med75-1.png differ diff --git a/docs/reference/plot_visit_med75.html b/docs/reference/plot_visit_med75.html index 960b062..452194d 100644 --- a/docs/reference/plot_visit_med75.html +++ b/docs/reference/plot_visit_med75.html @@ -1,109 +1,66 @@ -Plot patient visits against visit_med75. — plot_visit_med75 • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Plots cumulative AEs against visits for patients at sites of given study and compares against visit_med75.

    -
    +
    +

    Usage

    plot_visit_med75(
       df_visit,
       df_site = NULL,
    @@ -114,42 +71,42 @@ 

    Plot patient visits against visit_med75.

    )
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe

    -
    df_site
    +
    df_site

    dataframe, as returned by site_aggr()

    -
    study_id_str
    +
    study_id_str

    character, specify study in study_id column

    -
    n_sites
    +
    n_sites

    integer, Default: 6

    -
    min_pat_pool
    +
    min_pat_pool

    double, minimum ratio of available patients available for sampling. Determines maximum visit_med75 value see Details. Default: 0.2

    -
    verbose
    +
    verbose

    logical, Default: TRUE

    -
    -

    Value

    - - -

    ggplot

    +
    +

    Value

    +

    ggplot

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(n_pat = 120, n_sites = 6,
         frac_site_with_ur = 0.4, ur_rate = 0.6)
     
    @@ -167,27 +124,23 @@ 

    Examples

    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/poiss_test_site_ae_vs_study_ae.html b/docs/reference/poiss_test_site_ae_vs_study_ae.html index b786e90..6ee78a8 100644 --- a/docs/reference/poiss_test_site_ae_vs_study_ae.html +++ b/docs/reference/poiss_test_site_ae_vs_study_ae.html @@ -1,141 +1,97 @@ -Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by sim_sites().

    -
    +
    +

    Usage

    poiss_test_site_ae_vs_study_ae(site_ae, study_ae, visit_med75)
    -
    -

    Arguments

    -
    site_ae
    +
    +

    Arguments

    + + +
    site_ae

    vector with AE numbers

    -
    study_ae
    +
    study_ae

    vector with AE numbers

    -
    visit_med75
    +
    visit_med75

    integer

    -
    -

    Value

    - - -

    pval

    +
    +

    Value

    +

    pval

    -
    -

    Details

    +
    +

    Details

    sets pvalue=1 if mean AE site is greater than mean AE study or ttest gives error

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    poiss_test_site_ae_vs_study_ae(
        site_ae = c(5, 3, 3, 2, 1, 6),
        study_ae = c(9, 8, 7, 9, 6, 7, 8),
    @@ -151,27 +107,23 @@ 

    Examples

    #> [1] 0.9154536
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/prep_for_sim.html b/docs/reference/prep_for_sim.html index 2111757..13ac595 100644 --- a/docs/reference/prep_for_sim.html +++ b/docs/reference/prep_for_sim.html @@ -1,137 +1,95 @@ -Prepare data for simulation. — prep_for_sim • simaerepPrepare data for simulation. — prep_for_sim • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function called by sim_sites. Collect AEs per patient at visit_med75 for site and study as a vector of integers.

    -
    +
    +

    Usage

    prep_for_sim(df_site, df_visit)
    -
    -

    Arguments

    -
    df_site
    +
    +

    Arguments

    + + +
    df_site

    dataframe created by site_aggr

    -
    df_visit
    +
    df_visit

    dataframe, created by sim_sites

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    -

    See also

    + -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(
        n_pat = 100,
        n_sites = 5,
    @@ -147,7 +105,7 @@ 

    Examples

    df_prep #> # A tibble: 5 × 7 #> study_id site_number n_pat n_pat_with_med75 visit_med75 n_ae_site n_ae_study -#> <chr> <chr> <int> <int> <dbl> <list> <list> +#> <chr> <chr> <int> <dbl> <dbl> <list> <list> #> 1 A S0001 20 18 15 <int [18]> <int [71]> #> 2 A S0002 20 16 16 <int [16]> <int [64]> #> 3 A S0003 20 19 15 <int [19]> <int [70]> @@ -155,27 +113,23 @@

    Examples

    #> 5 A S0005 20 17 15 <int [17]> <int [72]>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/prob_lower_site_ae_vs_study_ae.html b/docs/reference/prob_lower_site_ae_vs_study_ae.html index 7500eb5..238c8aa 100644 --- a/docs/reference/prob_lower_site_ae_vs_study_ae.html +++ b/docs/reference/prob_lower_site_ae_vs_study_ae.html @@ -1,107 +1,63 @@ -Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function used by sim_sites()

    -
    +
    +

    Usage

    prob_lower_site_ae_vs_study_ae(
       site_ae,
       study_ae,
    @@ -111,45 +67,45 @@ 

    Calculate bootstrapped probability for obtaining a lower site mean AE number )

    -
    -

    Arguments

    -
    site_ae
    +
    +

    Arguments

    + + +
    site_ae

    vector with AE numbers

    -
    study_ae
    +
    study_ae

    vector with AE numbers

    -
    r
    +
    r

    integer, denotes number of simulations, default = 1000

    -
    parallel
    +
    parallel

    logical, toggles parallel processing on and of, default = F

    -
    under_only
    +
    under_only

    compute under-reporting probabilities only, default = TRUE

    -
    -

    Value

    - - -

    pval

    +
    +

    Value

    +

    pval

    -
    -

    Details

    +
    +

    Details

    sets pvalue=1 if mean AE site is greater than mean AE study

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    prob_lower_site_ae_vs_study_ae(
       site_ae = c(5, 3, 3, 2, 1, 6),
       study_ae = c(9, 8, 7, 9, 6, 7, 8),
    @@ -158,27 +114,23 @@ 

    Examples

    #> [1] 0.011
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/prune_to_visit_med75_inframe.html b/docs/reference/prune_to_visit_med75_inframe.html new file mode 100644 index 0000000..145e550 --- /dev/null +++ b/docs/reference/prune_to_visit_med75_inframe.html @@ -0,0 +1,97 @@ + +prune visits to visit_med75 using table operations — prune_to_visit_med75_inframe • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    prune visits to visit_med75 using table operations

    +
    + +
    +

    Usage

    +
    prune_to_visit_med75_inframe(df_visit, df_site)
    +
    + +
    +

    Arguments

    + + +
    df_visit
    +

    Data frame with columns: study_id, site_number, patnum, visit, +n_ae.

    + + +
    df_site
    +

    dataframe, as returned by site_aggr()

    + +
    + +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/purrr_bar.html b/docs/reference/purrr_bar.html index f9fc1e1..1498923 100644 --- a/docs/reference/purrr_bar.html +++ b/docs/reference/purrr_bar.html @@ -1,110 +1,63 @@ -Execute a purrr or furrr function with a progress -bar. — purrr_bar • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal utility function.

    -
    +
    +

    Usage

    purrr_bar(
       ...,
       .purrr,
    @@ -117,54 +70,54 @@ 

    Execute a purrr or furrr function with a progress )

    -
    -

    Arguments

    -
    ...
    +
    +

    Arguments

    + + +
    ...

    iterable arguments passed to .purrr

    -
    .purrr
    +
    .purrr

    purrr or furrr function

    -
    .f
    +
    .f

    function to be executed over iterables

    -
    .f_args
    +
    .f_args

    list of arguments passed to .f, Default: list()

    -
    .purrr_args
    +
    .purrr_args

    list of arguments passed to .purrr, Default: list()

    -
    .steps
    +
    .steps

    integer number of iterations

    -
    .slow
    +
    .slow

    logical slows down execution, Default: FALSE

    -
    .progress
    +
    .progress

    logical, show progress bar, Default: TRUE

    -
    -

    Value

    - - -

    result of function passed to .f

    +
    +

    Value

    +

    result of function passed to .f

    -
    -

    Details

    +
    +

    Details

    Call still needs to be wrapped in with_progress or with_progress_cnd()

    -
    -

    Examples

    +
    +

    Examples

    # purrr::map
     progressr::with_progress(
       purrr_bar(rep(0.25, 5), .purrr = purrr::map, .f = Sys.sleep, .steps = 5)
    @@ -294,27 +247,23 @@ 

    Examples

    # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_after_prep.html b/docs/reference/sim_after_prep.html index 6d123e1..9996be0 100644 --- a/docs/reference/sim_after_prep.html +++ b/docs/reference/sim_after_prep.html @@ -1,109 +1,66 @@ -Start simulation after preparation. — sim_after_prep • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function called by sim_sites after prep_for_sim

    -
    +
    +

    Usage

    sim_after_prep(
       df_sim_prep,
       r = 1000,
    @@ -114,49 +71,49 @@ 

    Start simulation after preparation.

    )
    -
    -

    Arguments

    -
    df_sim_prep
    +
    +

    Arguments

    + + +
    df_sim_prep

    dataframe as returned by prep_for_sim

    -
    r
    +
    r

    integer, denotes number of simulations, default = 1000

    -
    poisson_test
    +
    poisson_test

    logical, calculates poisson.test pvalue

    -
    prob_lower
    +
    prob_lower

    logical, calculates probability for getting a lower value

    -
    progress
    +
    progress

    logical, display progress bar, Default = TRUE

    -
    under_only
    +
    under_only

    compute under-reporting probabilities only, default = TRUE check_df_visit(), computationally expensive on large data sets. Default: TRUE

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(
        n_pat = 100,
        n_sites = 5,
    @@ -175,7 +132,7 @@ 

    Examples

    df_sim #> # A tibble: 5 × 9 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> +#> <chr> <chr> <int> <dbl> <dbl> <dbl> #> 1 A S0001 20 20 15 6.1 #> 2 A S0002 20 17 16 6.53 #> 3 A S0003 20 17 16 8.35 @@ -185,27 +142,23 @@

    Examples

    #> # prob_low <dbl>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_inframe.html b/docs/reference/sim_inframe.html new file mode 100644 index 0000000..7b16f95 --- /dev/null +++ b/docs/reference/sim_inframe.html @@ -0,0 +1,129 @@ + +Calculate prob_lower for study sites using table operations — sim_inframe • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    Calculate prob_lower for study sites using table operations

    +
    + +
    +

    Usage

    +
    sim_inframe(df_visit, r = 1000, df_site = NULL)
    +
    + +
    +

    Arguments

    + + +
    df_visit
    +

    Data frame with columns: study_id, site_number, patnum, visit, +n_ae.

    + + +
    r
    +

    Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.

    + + +
    df_site,
    +

    dataframe as returned be site_aggr(), Will switch to visit_med75. +Default: NULL

    + +
    + +
    +

    Examples

    +
    df_visit <- sim_test_data_study(
    +  n_pat = 100,
    +  n_sites = 5,
    +  frac_site_with_ur = 0.4,
    +  ur_rate = 0.6
    +)
    +df_visit$study_id <- "A"
    +
    +df_sim <- sim_inframe(df_visit)
    +df_eval <- eval_sites(df_sim)
    +df_eval
    +#> # A tibble: 5 × 10
    +#>   study_id site_number events_per_visit_site events visits n_pat prob_low
    +#>   <chr>    <chr>                       <dbl>  <dbl>  <dbl> <int>    <dbl>
    +#> 1 A        S0001                       0.188     75    400    20    0    
    +#> 2 A        S0002                       0.186     75    403    20    0    
    +#> 3 A        S0003                       0.496    187    377    20    0.998
    +#> 4 A        S0004                       0.453    180    397    20    0.98 
    +#> 5 A        S0005                       0.492    187    380    20    0.998
    +#> # ℹ 3 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
    +#> #   prob_low_prob_ur <dbl>
    +
    +
    +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/sim_scenario.html b/docs/reference/sim_scenario.html index 0eeed0f..61db4c7 100644 --- a/docs/reference/sim_scenario.html +++ b/docs/reference/sim_scenario.html @@ -1,137 +1,93 @@ -simulate single scenario — sim_scenario • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    internal function called by simulate_scenarios()

    -
    +
    +

    Usage

    sim_scenario(n_ae_site, n_ae_study, frac_pat_with_ur, ur_rate)
    -
    -

    Arguments

    -
    n_ae_site
    +
    +

    Arguments

    + + +
    n_ae_site

    integer vector

    -
    n_ae_study
    +
    n_ae_study

    integer vector

    -
    frac_pat_with_ur
    +
    frac_pat_with_ur

    double

    -
    ur_rate
    +
    ur_rate

    double

    -
    -

    Value

    - - -

    list

    +
    +

    Value

    +

    list

    -
    -

    Examples

    +
    +

    Examples

    sim_scenario(c(5,5,5,5), c(8,8,8,8), 0.2, 0.5)
     #> $n_ae_site
     #> [1] 2.5 2.5 5.0 5.0
    @@ -176,27 +132,23 @@ 

    Examples

    #>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_sites.html b/docs/reference/sim_sites.html index dcd26d2..eaea3b8 100644 --- a/docs/reference/sim_sites.html +++ b/docs/reference/sim_sites.html @@ -1,111 +1,69 @@ -Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites • simaerepCalculate prob_lower and poisson.test pvalue for study sites. — sim_sites • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Collects the number of AEs of all eligible patients that meet visit_med75 criteria of site. Then calculates poisson.test pvalue and bootstrapped probability of having a lower mean value.

    -
    +
    +

    Usage

    sim_sites(
       df_site,
       df_visit,
    @@ -118,47 +76,47 @@ 

    Calculate prob_lower and poisson.test pvalue for study sites.

    )
    -
    -

    Arguments

    -
    df_site
    +
    +

    Arguments

    + + +
    df_site

    dataframe created by site_aggr

    -
    df_visit
    +
    df_visit

    dataframe, created by sim_sites

    -
    r
    +
    r

    integer, denotes number of simulations, default = 1000

    -
    poisson_test
    +
    poisson_test

    logical, calculates poisson.test pvalue

    -
    prob_lower
    +
    prob_lower

    logical, calculates probability for getting a lower value

    -
    progress
    +
    progress

    logical, display progress bar, Default = TRUE

    -
    check,
    +
    check,

    logical, perform data check and attempt repair with

    -
    under_only
    +
    under_only

    compute under-reporting probabilities only, default = TRUE check_df_visit(), computationally expensive on large data sets. Default: TRUE

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number
    @@ -190,8 +148,8 @@

    Value

    -
    -

    See also

    +
    +

    See also

    sim_sites, site_aggr, pat_pool, @@ -201,8 +159,8 @@

    See also

    prep_for_sim

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(
        n_pat = 100,
        n_sites = 5,
    @@ -222,34 +180,30 @@ 

    Examples

    #> #> |study_id |site_number | n_pat| n_pat_with_med75| visit_med75| mean_ae_site_med75| mean_ae_study_med75| n_pat_with_med75_study| pval| prob_low| #> |:--------|:-----------|-----:|----------------:|-----------:|------------------:|-------------------:|----------------------:|----:|--------:| -#> |A |S0001 | 20| 19| 15| 6.63| 7.07| 71| 0.56| 0.26| -#> |A |S0002 | 20| 18| 15| 6.11| 7.19| 72| 0.12| 0.10| -#> |A |S0003 | 20| 17| 16| 8.53| 7.17| 66| 1.00| 1.00| -#> |A |S0004 | 20| 20| 15| 6.40| 7.14| 70| 0.29| 0.14| -#> |A |S0005 | 20| 16| 16| 8.38| 7.22| 67| 1.00| 1.00| +#> |A |S0001 | 20| 20| 15| 6.35| 6.31| 71| 1.00| 1.00| +#> |A |S0002 | 20| 19| 15| 5.37| 6.57| 72| 0.06| 0.06| +#> |A |S0003 | 20| 18| 15| 7.11| 6.12| 73| 1.00| 1.00| +#> |A |S0004 | 20| 15| 17| 7.87| 7.03| 61| 1.00| 1.00| +#> |A |S0005 | 20| 19| 15| 6.37| 6.31| 72| 1.00| 1.00|
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_studies.html b/docs/reference/sim_studies.html index 2ae9c06..3b4c30d 100644 --- a/docs/reference/sim_studies.html +++ b/docs/reference/sim_studies.html @@ -1,111 +1,69 @@ -Simulate studies. — sim_studies • simaerepSimulate studies. — sim_studies • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Test function, test applicability of poisson test, by calculating a the bootstrapped probability of obtaining a specific p-value or lower, use in combination with get_ecd_values().

    -
    +
    +

    Usage

    sim_studies(
       df_visit,
       df_site,
    @@ -122,69 +80,69 @@ 

    Simulate studies.

    )
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe

    -
    df_site
    +
    df_site

    dataframe

    -
    r
    +
    r

    integer, denotes number of simulations, Default: 1000

    -
    poisson_test
    +
    poisson_test

    logical, calculates poisson.test pvalue, Default: TRUE

    -
    prob_lower
    +
    prob_lower

    logical, calculates probability for getting a lower value, Default: FALSE

    -
    r_prob_lower
    +
    r_prob_lower

    integer, denotes number of simulations for prob_lower value calculation,, Default: 1000

    -
    under_only
    +
    under_only

    compute under-reporting probabilities only, default = TRUE

    -
    parallel
    +
    parallel

    logical, see examples for registering parallel processing framework , Default: FALSE

    -
    keep_ae
    +
    keep_ae

    logical, keep ae numbers in output dataframe memory increase roughly 30 percent, Default: F

    -
    min_n_pat_with_med75
    +
    min_n_pat_with_med75

    integer, min number of patients with med75 at site to simulate, Default: 1

    -
    studies
    +
    studies

    vector with study names, Default: NULL

    -
    .progress
    +
    .progress

    logical, show progress bar

    -
    -

    Value

    - - -

    dataframe

    +
    +

    Value

    +

    dataframe

    -
    -

    Details

    +
    +

    Details

    Here we simulate study replicates maintaining the same number of sites, patients and visit_med75 by bootstrap resampling, then probabilities for obtaining lower or same mean_ae count and p-values using poisson.test @@ -193,8 +151,8 @@

    Details

    mean_ae at visit_med75

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 5,
                                           frac_site_with_ur = 0.4, ur_rate = 0.6)
    @@ -213,50 +171,46 @@ 

    Examples

    sim_studies(df_visit, df_site, r = 3, keep_ae = TRUE) #> # A tibble: 24 × 10 #> r study_id site_number visit_med75 n_pat_with_med75 n_pat_study n_ae_site -#> <dbl> <chr> <chr> <dbl> <int> <dbl> <chr> -#> 1 1 A S0001 16 20 68 7,13,7,4… -#> 2 1 A S0002 18 17 57 1,10,4,4… -#> 3 1 A S0003 17 18 63 13,11,13… -#> 4 1 A S0004 14 18 76 3,9,9,9,… -#> 5 1 A S0005 15 18 72 9,4,7,6,… -#> 6 1 B S0001 15 297 597 9,8,5,7,… -#> 7 1 B S0002 15 296 598 14,8,7,1… -#> 8 1 B S0003 15 301 593 9,5,8,6,… -#> 9 2 A S0001 16 20 68 10,7,5,3… -#> 10 2 A S0002 18 17 57 12,12,6,… +#> <dbl> <chr> <chr> <dbl> <dbl> <dbl> <chr> +#> 1 1 A S0001 16 18 68 11,5,7,4… +#> 2 1 A S0002 18 17 53 11,16,8,… +#> 3 1 A S0003 17 18 62 13,11,13… +#> 4 1 A S0004 14 18 74 7,9,7,6,… +#> 5 1 A S0005 15 17 71 6,9,5,4,… +#> 6 1 B S0001 15 297 597 10,13,11… +#> 7 1 B S0002 15 296 598 3,10,4,6… +#> 8 1 B S0003 15 301 593 3,6,7,10… +#> 9 2 A S0001 16 18 68 10,5,7,3… +#> 10 2 A S0002 18 17 53 5,17,8,6… #> # ℹ 14 more rows #> # ℹ 3 more variables: n_ae_study <chr>, pval <dbl>, prob_low <dbl> # } -if (FALSE) { +if (FALSE) { # \dontrun{ # parallel processing ------------------------- library(future) future::plan(multiprocess) sim_studies(df_visit, df_site, r = 3, keep_ae = TRUE, parallel = TRUE) future::plan(sequential) -} +} # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_test_data_patient.html b/docs/reference/sim_test_data_patient.html index 3f5958e..7b2ac56 100644 --- a/docs/reference/sim_test_data_patient.html +++ b/docs/reference/sim_test_data_patient.html @@ -1,215 +1,166 @@ -simulate patient ae reporting test data — sim_test_data_patient • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    helper function for sim_test_data_study()

    -
    +
    +

    Usage

    sim_test_data_patient(
       .f_sample_max_visit = function() rnorm(1, mean = 20, sd = 4),
       .f_sample_ae_per_visit = function(max_visit) rpois(max_visit, 0.5)
     )
    -
    -

    Arguments

    -
    .f_sample_max_visit
    +
    +

    Arguments

    + + +
    .f_sample_max_visit

    function used to sample the maximum number of aes, Default: function() rnorm(1, mean = 20, sd = 4)

    -
    .f_sample_ae_per_visit
    +
    .f_sample_ae_per_visit

    function used to sample the aes for each visit, Default: function(x) rpois(x, 0.5)

    -
    -

    Value

    - - -

    vector containing cumulative aes

    +
    +

    Value

    +

    vector containing cumulative aes

    -
    -

    Details

    +
    +

    Details

    ""

    -
    -

    Examples

    +
    +

    Examples

    replicate(5, sim_test_data_patient())
     #> [[1]]
    -#>  [1]  0  0  0  0  1  1  1  3  4  5  6  6  7  7  8 10 10 11 12 14 14
    +#>  [1]  1  2  3  3  4  4  4  4  4  4  5  5  5  7  8  9 10 10 11 11 12 14 14
     #> 
     #> [[2]]
    -#>  [1]  1  1  1  2  2  2  3  3  4  5  5  7  7  7  7  7  7  7  8  8  9  9 11 13
    +#>  [1]  2  2  3  4  5  5  5  6  6  6  7  7  8  9  9 11 11 11 11 11 11 11 12 12
     #> 
     #> [[3]]
    -#>  [1] 0 1 2 4 4 4 5 5 6 7 7 8 8 8 8 8 8 8 8 8 8 9
    +#>  [1]  2  4  5  7  7  8  9 11 11 11 12 12 13 14 14 15 15 15 15 15 15
     #> 
     #> [[4]]
    -#>  [1] 0 0 0 1 2 2 2 2 2 3 3 4 4 4 4 4 4 5 5 7 7 7 7
    +#>  [1] 0 0 1 2 3 3 3 3 4 5 5 5 5 5
     #> 
     #> [[5]]
    -#>  [1]  1  1  1  2  3  5  6  6  6  6  6  7  7  8  9  9  9  9 10 10 10 12 13 13 14
    -#> [26] 14 15
    +#>  [1]  1  1  1  1  1  1  2  2  4  4  4  4  6  6  7  7  7  8  9 11 12 12
     #> 
     replicate(5, sim_test_data_patient(
         .f_sample_ae_per_visit = function(x) rpois(x, 1.2))
       )
     #> [[1]]
    -#>  [1]  1  2  2  2  6  7  7  9  9  9 11 13 14 14 16 20 21 21 22 23 25
    +#>  [1]  1  3  3  5  6  6  6  6  8  8  9 13 15 15 16 16
     #> 
     #> [[2]]
    -#>  [1]  0  1  1  1  1  3  6  7  9 11 12 14 14 15 17 19
    +#>  [1]  2  3  4  4  4  8  9  9 11 11 11 13 15 16 16 18 22 23 23 24 25
     #> 
     #> [[3]]
    -#>  [1]  0  1  4  4  6  8  9  9 13 13 14 15 16 20 20 24 25 26 28 29 31 33 35
    +#>  [1]  2  2  3  3  3  3  5  8  9 11 13 14 16 16 17 19 21 23 23 23 24
     #> 
     #> [[4]]
    -#>  [1]  1  5  5  9 11 13 15 15 16 16 17 18 18 18 19 19 20 20 20 20 22 24 26 28 30
    -#> [26] 31
    +#>  [1]  2  4  5  5  9  9 10 11 12 16 16 20 21 22 24 25 27 29 31 34 36 37 41 41 45
    +#> [26] 47
     #> 
     #> [[5]]
    -#>  [1]  1  2  4  5  8  9 12 13 13 13 17 19 20 20 20 20
    +#>  [1]  0  1  1  2  3  3  3  4  4  5  5  5  5  7  9 11 13 15 16 16 16 17
     #> 
     replicate(5, sim_test_data_patient(
         .f_sample_max_visit = function() rnorm(1, mean = 5, sd = 5))
       )
     #> [[1]]
    -#> [1] 0 3
    +#> [1] 0 2 2 4 4 4
     #> 
     #> [[2]]
    -#> [1] 0
    +#> [1] 1
     #> 
     #> [[3]]
    -#> [1] 0 0 0 0 3 3 4 4
    +#> [1] 0 0 0
     #> 
     #> [[4]]
    -#> [1] 0 0 0 0 0 0 0 1 1
    +#>  [1] 3 3 3 3 4 4 4 4 4 4
     #> 
     #> [[5]]
    -#> [1] 0 2
    +#>  [1] 1 1 2 3 3 3 3 3 3 3 3 4 4 4 4 4 6
     #> 
     
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_test_data_portfolio.html b/docs/reference/sim_test_data_portfolio.html index 7dfc62f..d41a5df 100644 --- a/docs/reference/sim_test_data_portfolio.html +++ b/docs/reference/sim_test_data_portfolio.html @@ -1,107 +1,63 @@ -Simulate Portfolio Test Data — sim_test_data_portfolio • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Simulate visit level data from a portfolio configuration.

    -
    +
    +

    Usage

    sim_test_data_portfolio(
       df_config,
       df_ae_rates = NULL,
    @@ -110,29 +66,29 @@ 

    Simulate Portfolio Test Data

    )
    -
    -

    Arguments

    -
    df_config
    +
    +

    Arguments

    + + +
    df_config

    dataframe as returned by get_config

    -
    df_ae_rates
    +
    df_ae_rates

    dataframe with ae rates. Default: NULL

    -
    parallel
    +
    parallel

    logical activate parallel processing, see details, Default: FALSE

    -
    progress
    +
    progress

    logical, Default: TRUE

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    ae_per_visit_mean

    mean @@ -157,16 +113,16 @@

    Value

    -
    -

    Details

    +
    +

    Details

    uses sim_test_data_study. We use the furrr package to implement parallel processing as these simulations can take a long time to run. For this to work we need to specify the plan for how the code should run, e.g. `plan(multisession, workers = 3)

    -
    -

    See also

    +
    +

    See also

    sim_test_data_study get_config sim_test_data_portfolio @@ -174,8 +130,8 @@

    See also

    get_portf_perf

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10,
                                      frac_site_with_ur = 0.4, ur_rate = 0.6)
    @@ -253,22 +209,23 @@ 

    Examples

    df_scen #> # A tibble: 140 × 14 -#> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> -#> 1 0001 0001 10 8 21 6.25 -#> 2 0001 0001 10 8 21 3.12 -#> 3 0001 0001 10 8 21 0 -#> 4 0001 0001 10 8 21 3.12 -#> 5 0001 0001 10 8 21 0 -#> 6 0001 0001 10 8 21 3.12 -#> 7 0001 0001 10 8 21 0 -#> 8 0001 0002 10 10 14 3.8 -#> 9 0001 0002 10 10 14 1.9 -#> 10 0001 0002 10 10 14 0 +#> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat +#> <chr> <chr> <dbl> <dbl> <dbl> <int> +#> 1 0001 0001 0 0 0 10 +#> 2 0001 0001 0 0.222 0.5 10 +#> 3 0001 0001 0 0.222 1 10 +#> 4 0001 0001 1 0.333 0.5 10 +#> 5 0001 0001 1 0.333 1 10 +#> 6 0001 0001 2 0.444 0.5 10 +#> 7 0001 0001 2 0.444 1 10 +#> 8 0001 0002 0 0 0 10 +#> 9 0001 0002 0 0.119 0.5 10 +#> 10 0001 0002 0 0.119 1 10 #> # ℹ 130 more rows -#> # ℹ 8 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>, -#> # extra_ur_sites <dbl>, frac_pat_with_ur <dbl>, ur_rate <dbl>, -#> # prob_low <dbl>, prob_low_adj <dbl>, prob_low_prob_ur <dbl> +#> # ℹ 8 more variables: n_pat_with_med75 <dbl>, visit_med75 <dbl>, +#> # mean_ae_site_med75 <dbl>, mean_ae_study_med75 <dbl>, +#> # n_pat_with_med75_study <int>, prob_low <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl> df_perf <- get_portf_perf(df_scen) @@ -276,41 +233,37 @@

    Examples

    #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> <dbl> <dbl> <dbl> <dbl> <dbl> -#> 1 0.001 0.916 0 0 0.05 -#> 2 0.001 0.916 1 0 0.05 -#> 3 0.001 0.916 2 0 0.05 -#> 4 0.001 0.916 0 0.5 1 -#> 5 0.001 0.916 1 0.5 1 -#> 6 0.001 0.916 2 0.5 1 -#> 7 0.001 0.916 0 1 1 -#> 8 0.001 0.916 1 1 1 -#> 9 0.001 0.916 2 1 1 -#> 10 0.01 0.913 0 0 0.05 +#> 1 0.001 0.555 0 0 0.1 +#> 2 0.001 0.555 1 0 0.1 +#> 3 0.001 0.555 2 0 0.1 +#> 4 0.001 0.555 0 0.5 1 +#> 5 0.001 0.555 1 0.5 1 +#> 6 0.001 0.555 2 0.5 0.9 +#> 7 0.001 0.555 0 1 1 +#> 8 0.001 0.555 1 1 1 +#> 9 0.001 0.555 2 1 1 +#> 10 0.01 0.555 0 0 0.1 #> # ℹ 17 more rows # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_test_data_study.html b/docs/reference/sim_test_data_study.html index 90d8a4b..83c5e2a 100644 --- a/docs/reference/sim_test_data_study.html +++ b/docs/reference/sim_test_data_study.html @@ -1,113 +1,72 @@ -simulate study test data — sim_test_data_study • simaerepsimulate study test data — sim_test_data_study • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    evenly distributes a number of given patients across a number of given sites. Then simulates ae development of each patient reducing the number of reported AEs for patients distributed to AE-under-reporting sites.

    -
    +
    +

    Usage

    sim_test_data_study(
       n_pat = 1000,
       n_sites = 20,
    @@ -120,61 +79,61 @@ 

    simulate study test data

    )
    -
    -

    Arguments

    -
    n_pat
    +
    +

    Arguments

    + + +
    n_pat

    integer, number of patients, Default: 1000

    -
    n_sites
    +
    n_sites

    integer, number of sites, Default: 20

    -
    frac_site_with_ur
    +
    frac_site_with_ur

    fraction of AE under-reporting sites, Default: 0

    -
    ur_rate
    +
    ur_rate

    AE under-reporting rate, will lower mean ae per visit used to simulate patients at sites flagged as AE-under-reporting. Negative Values will simulate over-reporting., Default: 0

    -
    max_visit_mean
    +
    max_visit_mean

    mean of the maximum number of visits of each patient, Default: 20

    -
    max_visit_sd
    +
    max_visit_sd

    standard deviation of maximum number of visits of each patient, Default: 4

    -
    ae_per_visit_mean
    +
    ae_per_visit_mean

    mean ae per visit per patient, Default: 0.5

    -
    ae_rates
    +
    ae_rates

    vector with visit-specific ae rates, Default: Null

    -
    -

    Value

    - - -

    tibble with columns site_number, patnum, is_ur, max_visit_mean, +

    +

    Value

    +

    tibble with columns site_number, patnum, is_ur, max_visit_mean, max_visit_sd, ae_per_visit_mean, visit, n_ae

    -
    -

    Details

    +
    +

    Details

    maximum visit number will be sampled from normal distribution with characteristics derived from max_visit_mean and max_visit_sd, while the ae per visit will be sampled from a poisson distribution described by ae_per_visit_mean.

    -
    -

    Examples

    +
    +

    Examples

    set.seed(1)
     df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5)
     df_visit[which(df_visit$patnum == "P000001"),]
    @@ -236,27 +195,23 @@ 

    Examples

    #> # ℹ 1 more variable: n_ae <int>
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/sim_ur.html b/docs/reference/sim_ur.html new file mode 100644 index 0000000..d397d38 --- /dev/null +++ b/docs/reference/sim_ur.html @@ -0,0 +1,129 @@ + +simulate under-reporting — sim_ur • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    we remove a fraction of AEs from a specific site

    +
    + +
    +

    Usage

    +
    sim_ur(df_visit, study_id, site_number, ur_rate)
    +
    + +
    +

    Arguments

    + + +
    df_visit,
    +

    dataframe

    + + +
    study_id,
    +

    character

    + + +
    site_number,
    +

    character

    + + +
    ur_rate,
    +

    double

    + +
    +
    +

    Details

    +

    we determine the absolute number of AEs per patient for removal. +Then them remove them at the first visit. +We intentionally allow fractions

    +
    + +
    +

    Examples

    +
    df_visit <- sim_test_data_study(n_pat = 100, n_sites = 10,
    +                                 frac_site_with_ur = 0.4, ur_rate = 0.6)
    +
    +df_visit$study_id <- "A"
    +
    +df_ur <- sim_ur(df_visit, "A", site_number = "S0001", ur_rate = 0.35)
    +
    +# Example cumulated AE for first patient with 35% under-reporting
    +df_ur[df_ur$site_number == "S0001" & df_ur$patnum == "P000001",]$n_ae
    +#>  [1] 0.0 0.6 0.6 1.6 1.6 1.6 1.6 1.6 1.6 2.6 2.6 2.6 2.6 2.6 2.6 2.6 2.6
    +
    +# Example cumulated AE for first patient with no under-reporting
    +df_visit[df_visit$site_number == "S0001" & df_visit$patnum == "P000001",]$n_ae
    +#>  [1] 1 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4
    +
    +
    +
    +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/sim_ur_scenarios.html b/docs/reference/sim_ur_scenarios.html index 2d23385..5b78ad3 100644 --- a/docs/reference/sim_ur_scenarios.html +++ b/docs/reference/sim_ur_scenarios.html @@ -1,109 +1,66 @@ -Simulate Under-Reporting Scenarios — sim_ur_scenarios • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Use with simulated portfolio data to generate under-reporting stats for specified scenarios.

    -
    +
    +

    Usage

    sim_ur_scenarios(
       df_portf,
       extra_ur_sites = 3,
    @@ -114,61 +71,66 @@ 

    Simulate Under-Reporting Scenarios

    parallel = FALSE, progress = TRUE, site_aggr_args = list(), - eval_sites_args = list() + eval_sites_args = list(), + check = TRUE )
    -
    -

    Arguments

    -
    df_portf
    +
    +

    Arguments

    + + +
    df_portf

    dataframe as returned by sim_test_data_portfolio

    -
    extra_ur_sites
    +
    extra_ur_sites

    numeric, set maximum number of additional under-reporting sites, see details Default: 3

    -
    ur_rate
    +
    ur_rate

    numeric vector, set under-reporting rates for scenarios Default: c(0.25, 0.5)

    -
    r
    +
    r

    integer, denotes number of simulations, default = 1000

    -
    poisson_test
    +
    poisson_test

    logical, calculates poisson.test pvalue

    -
    prob_lower
    +
    prob_lower

    logical, calculates probability for getting a lower value

    -
    parallel
    +
    parallel

    logical, use parallel processing see details, Default: FALSE

    -
    progress
    +
    progress

    logical, show progress bar, Default: TRUE

    -
    site_aggr_args
    +
    site_aggr_args

    named list of parameters passed to site_aggr, Default: list()

    -
    eval_sites_args
    +
    eval_sites_args

    named list of parameters passed to eval_sites, Default: list()

    -
    -
    -

    Value

    - -

    dataframe with the following columns:

    study_id
    +
    check
    +

    logical, perform data check and attempt repair with

    + +
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number
    @@ -227,8 +189,8 @@

    Value

    -
    -

    Details

    +
    +

    Details

    The function will apply under-reporting scenarios to each site. Reducing the number of AEs by a given under-reporting (ur_rate) for all patients at the site and add the corresponding under-reporting statistics. @@ -242,8 +204,8 @@

    Details

    run. For this to work we need to specify the plan for how the code should run, e.g. plan(multisession, workers = 18)

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    # \donttest{
     df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10,
                                      frac_site_with_ur = 0.4, ur_rate = 0.6)
    @@ -278,44 +240,44 @@ 

    Examples

    #> # A tibble: 20 × 6 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean n_pat #> <chr> <dbl> <chr> <dbl> <dbl> <int> -#> 1 0001 0.366 0001 4.42 20 10 -#> 2 0001 0.366 0002 4.03 21.4 10 -#> 3 0001 0.366 0003 4.13 20.2 10 -#> 4 0001 0.366 0004 2.58 18.3 10 -#> 5 0001 0.366 0005 4.64 17.8 10 -#> 6 0001 0.366 0006 2.37 17.6 10 -#> 7 0001 0.366 0007 4.80 19.8 10 -#> 8 0001 0.366 0008 2 20 10 -#> 9 0001 0.366 0009 3.17 19.5 10 -#> 10 0001 0.366 0010 6.57 19.9 10 -#> 11 0002 0.489 0001 2.85 19.9 10 -#> 12 0002 0.489 0002 3.31 18.1 10 -#> 13 0002 0.489 0003 3.14 18.1 10 -#> 14 0002 0.489 0004 4.74 20.7 10 -#> 15 0002 0.489 0005 5.20 19.2 10 -#> 16 0002 0.489 0006 3.30 21 10 -#> 17 0002 0.489 0007 4.07 19.9 10 -#> 18 0002 0.489 0008 4.53 18.5 10 -#> 19 0002 0.489 0009 2.95 21.7 10 -#> 20 0002 0.489 0010 3.36 20.2 10 +#> 1 0001 0.381 0001 2.85 19.9 10 +#> 2 0001 0.381 0002 3.31 18.1 10 +#> 3 0001 0.381 0003 3.14 18.1 10 +#> 4 0001 0.381 0004 4.74 20.7 10 +#> 5 0001 0.381 0005 5.20 19.2 10 +#> 6 0001 0.381 0006 3.30 21 10 +#> 7 0001 0.381 0007 4.07 19.9 10 +#> 8 0001 0.381 0008 4.53 18.5 10 +#> 9 0001 0.381 0009 2.95 21.7 10 +#> 10 0001 0.381 0010 3.36 20.2 10 +#> 11 0002 0.491 0001 3.03 20.9 10 +#> 12 0002 0.491 0002 2.44 18.8 10 +#> 13 0002 0.491 0003 3.62 20.3 10 +#> 14 0002 0.491 0004 3.79 19.8 10 +#> 15 0002 0.491 0005 4.19 19.3 10 +#> 16 0002 0.491 0006 5.34 18.5 10 +#> 17 0002 0.491 0007 4.23 18.9 10 +#> 18 0002 0.491 0008 3.28 18.9 10 +#> 19 0002 0.491 0009 5.33 18 10 +#> 20 0002 0.491 0010 3.48 19.1 10 df_portf <- sim_test_data_portfolio(df_config) df_portf -#> # A tibble: 3,834 × 8 +#> # A tibble: 3,746 × 8 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean patnum #> <chr> <dbl> <chr> <dbl> <dbl> <chr> -#> 1 0001 0.366 0001 4.42 20 0001 -#> 2 0001 0.366 0001 4.42 20 0001 -#> 3 0001 0.366 0001 4.42 20 0001 -#> 4 0001 0.366 0001 4.42 20 0001 -#> 5 0001 0.366 0001 4.42 20 0001 -#> 6 0001 0.366 0001 4.42 20 0001 -#> 7 0001 0.366 0001 4.42 20 0001 -#> 8 0001 0.366 0001 4.42 20 0001 -#> 9 0001 0.366 0001 4.42 20 0001 -#> 10 0001 0.366 0001 4.42 20 0001 -#> # ℹ 3,824 more rows +#> 1 0001 0.381 0001 2.85 19.9 0001 +#> 2 0001 0.381 0001 2.85 19.9 0001 +#> 3 0001 0.381 0001 2.85 19.9 0001 +#> 4 0001 0.381 0001 2.85 19.9 0001 +#> 5 0001 0.381 0001 2.85 19.9 0001 +#> 6 0001 0.381 0001 2.85 19.9 0001 +#> 7 0001 0.381 0001 2.85 19.9 0001 +#> 8 0001 0.381 0001 2.85 19.9 0001 +#> 9 0001 0.381 0001 2.85 19.9 0001 +#> 10 0001 0.381 0001 2.85 19.9 0001 +#> # ℹ 3,736 more rows #> # ℹ 2 more variables: visit <int>, n_ae <int> df_scen <- sim_ur_scenarios(df_portf, @@ -330,22 +292,23 @@

    Examples

    df_scen #> # A tibble: 140 × 14 -#> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 -#> <chr> <chr> <int> <int> <dbl> <dbl> -#> 1 0001 0001 10 9 16 5.44 -#> 2 0001 0001 10 9 16 2.72 -#> 3 0001 0001 10 9 16 0 -#> 4 0001 0001 10 9 16 2.72 -#> 5 0001 0001 10 9 16 0 -#> 6 0001 0001 10 9 16 2.72 -#> 7 0001 0001 10 9 16 0 -#> 8 0001 0002 10 9 16 4.89 -#> 9 0001 0002 10 9 16 2.44 -#> 10 0001 0002 10 9 16 0 +#> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat +#> <chr> <chr> <dbl> <dbl> <dbl> <int> +#> 1 0001 0001 0 0 0 10 +#> 2 0001 0001 0 0.122 0.5 10 +#> 3 0001 0001 0 0.122 1 10 +#> 4 0001 0001 1 0.222 0.5 10 +#> 5 0001 0001 1 0.222 1 10 +#> 6 0001 0001 2 0.322 0.5 10 +#> 7 0001 0001 2 0.322 1 10 +#> 8 0001 0002 0 0 0 10 +#> 9 0001 0002 0 0.104 0.5 10 +#> 10 0001 0002 0 0.104 1 10 #> # ℹ 130 more rows -#> # ℹ 8 more variables: mean_ae_study_med75 <dbl>, n_pat_with_med75_study <int>, -#> # extra_ur_sites <dbl>, frac_pat_with_ur <dbl>, ur_rate <dbl>, -#> # prob_low <dbl>, prob_low_adj <dbl>, prob_low_prob_ur <dbl> +#> # ℹ 8 more variables: n_pat_with_med75 <dbl>, visit_med75 <dbl>, +#> # mean_ae_site_med75 <dbl>, mean_ae_study_med75 <dbl>, +#> # n_pat_with_med75_study <int>, prob_low <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl> df_perf <- get_portf_perf(df_scen) @@ -353,41 +316,37 @@

    Examples

    #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> <dbl> <dbl> <dbl> <dbl> <dbl> -#> 1 0.001 0.858 0 0 0.05 -#> 2 0.001 0.858 1 0 0.05 -#> 3 0.001 0.858 2 0 0.05 -#> 4 0.001 0.858 0 0.5 1 -#> 5 0.001 0.858 1 0.5 1 -#> 6 0.001 0.858 2 0.5 1 -#> 7 0.001 0.858 0 1 1 -#> 8 0.001 0.858 1 1 1 -#> 9 0.001 0.858 2 1 1 -#> 10 0.01 0.857 0 0 0.05 +#> 1 0.001 0.529 0 0 0.05 +#> 2 0.001 0.529 1 0 0.05 +#> 3 0.001 0.529 2 0 0.05 +#> 4 0.001 0.529 0 0.5 1 +#> 5 0.001 0.529 1 0.5 1 +#> 6 0.001 0.529 2 0.5 0.95 +#> 7 0.001 0.529 0 1 1 +#> 8 0.001 0.529 1 1 1 +#> 9 0.001 0.529 2 1 1 +#> 10 0.01 0.517 0 0 0.05 #> # ℹ 17 more rows # }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/simaerep.html b/docs/reference/simaerep.html index 676fe58..e809dc7 100644 --- a/docs/reference/simaerep.html +++ b/docs/reference/simaerep.html @@ -1,245 +1,220 @@ -create simaerep object — simaerep • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    -

    simulate AE under-reporting probabilities

    +
    +

    Simulate AE under-reporting probabilities.

    -
    +
    +

    Usage

    simaerep(
       df_visit,
    +  r = 1000,
    +  check = TRUE,
    +  under_only = TRUE,
    +  visit_med75 = TRUE,
    +  inframe = FALSE,
    +  progress = TRUE,
       param_site_aggr = list(method = "med75_adj", min_pat_pool = 0.2),
       param_sim_sites = list(r = 1000, poisson_test = FALSE, prob_lower = TRUE),
       param_eval_sites = list(method = "BH"),
    -  progress = TRUE,
    -  check = TRUE,
    -  env = parent.frame(),
    -  under_only = TRUE
    +  env = parent.frame()
     )
    -
    -

    Arguments

    -
    df_visit
    -

    data frame with columns: study_id, site_number, patnum, visit, -n_ae

    +
    +

    Arguments

    -
    param_site_aggr
    -

    list of parameters passed to site_aggr(), -Default: list(method = "med75_adj", min_pat_pool = 0.2)

    +
    df_visit
    +

    Data frame with columns: study_id, site_number, patnum, visit, +n_ae.

    -
    param_sim_sites
    -

    list of parameters passed to sim_sites(), -Default: list(r = 1000, poisson_test = FALSE, prob_lower = TRUE)

    +
    r
    +

    Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.

    -
    param_eval_sites
    -

    list of parameters passed to -eval_sites(), Default: list(method = "BH")

    +
    check
    +

    Logical, perform data check and attempt repair with +check_df_visit(). Computationally expensive on large data sets. Default: +TRUE.

    -
    progress
    -

    logical, display progress bar, Default = TRUE

    +
    under_only
    +

    Logical, compute under-reporting probabilities only. +Supersedes under_only parameter passed to eval_sites() and sim_sites(). +Default: TRUE.

    -
    check
    -

    logical, perform data check and attempt repair with -check_df_visit(), computationally expensive on large data -sets. Default: TRUE

    +
    visit_med75
    +

    Logical, should evaluation point visit_med75 be used. +Default: TRUE.

    -
    env
    -

    optional, provide environment of original visit data, Default: -parent.frame()

    +
    inframe
    +

    Logical, only table operations to be used; does not require +visit_med75. Compatible with dbplyr supported database backends.

    -
    under_only,
    -

    logical compute under-reporting probabilities only, -superseeds under_only parameter passed to eval_sites() and -sim_sites(), Default: TRUE

    +
    progress
    +

    Logical, display progress bar. Default: TRUE.

    -
    -
    -

    Value

    - -

    simaerep object

    +
    param_site_aggr
    +

    List of parameters passed to site_aggr(). Default: +list(method = "med75_adj", min_pat_pool = 0.2).

    + + +
    param_sim_sites
    +

    List of parameters passed to sim_sites(). Default: +list(r = 1000, poisson_test = FALSE, prob_lower = TRUE).

    + + +
    param_eval_sites
    +

    List of parameters passed to eval_sites(). Default: +list(method = "BH").

    + + +
    env
    +

    Optional, provide environment of original visit data. Default: +parent.frame().

    + +
    +
    +

    Value

    +

    A simaerep object.

    -
    -

    Details

    -

    executes site_aggr(), sim_sites() and -eval_sites() on original visit data and stores all -intermediate results. Stores lazy reference to original visit data for -facilitated plotting using generic plot(x).

    +
    +

    Details

    +

    Executes site_aggr(), sim_sites(), and eval_sites() on original +visit data and stores all intermediate results. Stores lazy reference to +original visit data for facilitated plotting using generic plot(x).

    -
    -

    See also

    -

    site_aggr(), sim_sites(), +

    -
    -

    Examples

    -
    
    -df_visit <- sim_test_data_study(
    +    
    +

    Examples

    +
    df_visit <- sim_test_data_study(
       n_pat = 100,
       n_sites = 5,
       frac_site_with_ur = 0.4,
       ur_rate = 0.6
     )
    -
     df_visit$study_id <- "A"
    -
     aerep <- simaerep(df_visit)
    -
     aerep
     #> simaerep object:
    -#> Check aerep$df_eval prob_low_prob_ur column for AE under-reporting probabilites.
    +#> Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty.
     #> Plot results using plot() generic.
    -
     str(aerep)
    -#> List of 7
    +#> List of 11
     #>  $ visit           :List of 3
    -#>   ..$ dim       : int [1:2] 1969 9
    +#>   ..$ dim       : int [1:2] 1972 9
     #>   ..$ df_summary: tibble [1 × 5] (S3: tbl_df/tbl/data.frame)
     #>   .. ..$ n_studies : int 1
     #>   .. ..$ n_sites   : int 5
     #>   .. ..$ n_patients: int 100
    -#>   .. ..$ n_visits  : int 1969
    -#>   .. ..$ n_aes     : int 703
    +#>   .. ..$ n_visits  : int 1972
    +#>   .. ..$ n_aes     : int 705
     #>   ..$ str_call  : chr "df_visit"
     #>   ..- attr(*, "class")= chr "orivisit"
     #>  $ df_site         : tibble [5 × 6] (S3: tbl_df/tbl/data.frame)
     #>   ..$ study_id          : chr [1:5] "A" "A" "A" "A" ...
     #>   ..$ site_number       : chr [1:5] "S0001" "S0002" "S0003" "S0004" ...
     #>   ..$ n_pat             : int [1:5] 20 20 20 20 20
    -#>   ..$ n_pat_with_med75  : int [1:5] 17 18 17 20 16
    +#>   ..$ n_pat_with_med75  : num [1:5] 18 18 17 20 16
     #>   ..$ visit_med75       : Named num [1:5] 17 15 16 15 16
     #>   .. ..- attr(*, "names")= chr [1:5] "80%" "80%" "80%" "80%" ...
    -#>   ..$ mean_ae_site_med75: num [1:5] 3.06 2.56 8.53 6.4 8.38
    +#>   ..$ mean_ae_site_med75: num [1:5] 3.11 2.56 8.53 6.4 8.38
     #>  $ df_sim_sites    : tibble [5 × 9] (S3: tbl_df/tbl/data.frame)
     #>   ..$ study_id              : chr [1:5] "A" "A" "A" "A" ...
     #>   ..$ site_number           : chr [1:5] "S0001" "S0002" "S0003" "S0004" ...
     #>   ..$ n_pat                 : int [1:5] 20 20 20 20 20
    -#>   ..$ n_pat_with_med75      : int [1:5] 17 18 17 20 16
    +#>   ..$ n_pat_with_med75      : num [1:5] 18 18 17 20 16
     #>   ..$ visit_med75           : Named num [1:5] 17 15 16 15 16
     #>   .. ..- attr(*, "names")= chr [1:5] "80%" "80%" "80%" "80%" ...
    -#>   ..$ mean_ae_site_med75    : num [1:5] 3.06 2.56 8.53 6.4 8.38
    -#>   ..$ mean_ae_study_med75   : num [1:5] 7.03 6.18 5.23 5.19 5.31
    +#>   ..$ mean_ae_site_med75    : num [1:5] 3.11 2.56 8.53 6.4 8.38
    +#>   ..$ mean_ae_study_med75   : num [1:5] 7.03 6.21 5.26 5.21 5.34
     #>   ..$ n_pat_with_med75_study: int [1:5] 59 72 66 70 67
     #>   ..$ prob_low              : num [1:5] 0 0 1 1 1
     #>  $ df_eval         : tibble [5 × 11] (S3: tbl_df/tbl/data.frame)
     #>   ..$ study_id              : chr [1:5] "A" "A" "A" "A" ...
     #>   ..$ site_number           : chr [1:5] "S0001" "S0002" "S0003" "S0004" ...
     #>   ..$ n_pat                 : int [1:5] 20 20 20 20 20
    -#>   ..$ n_pat_with_med75      : int [1:5] 17 18 17 20 16
    +#>   ..$ n_pat_with_med75      : num [1:5] 18 18 17 20 16
     #>   ..$ visit_med75           : Named num [1:5] 17 15 16 15 16
     #>   .. ..- attr(*, "names")= chr [1:5] "80%" "80%" "80%" "80%" ...
    -#>   ..$ mean_ae_site_med75    : num [1:5] 3.06 2.56 8.53 6.4 8.38
    -#>   ..$ mean_ae_study_med75   : num [1:5] 7.03 6.18 5.23 5.19 5.31
    +#>   ..$ mean_ae_site_med75    : num [1:5] 3.11 2.56 8.53 6.4 8.38
    +#>   ..$ mean_ae_study_med75   : num [1:5] 7.03 6.21 5.26 5.21 5.34
     #>   ..$ n_pat_with_med75_study: int [1:5] 59 72 66 70 67
     #>   ..$ prob_low              : num [1:5] 0 0 1 1 1
     #>   ..$ prob_low_adj          : num [1:5] 0 0 1 1 1
     #>   ..$ prob_low_prob_ur      : num [1:5] 1 1 0 0 0
    +#>  $ r               : num 1000
    +#>  $ visit_med75     : logi TRUE
    +#>  $ inframe         : logi FALSE
    +#>  $ under_only      : logi TRUE
     #>  $ param_site_aggr :List of 2
     #>   ..$ method      : chr "med75_adj"
     #>   ..$ min_pat_pool: num 0.2
    @@ -252,30 +227,88 @@ 

    Examples

    #> ..$ method : chr "BH" #> ..$ under_only: logi TRUE #> - attr(*, "class")= chr "simaerep" - +# \donttest{ + # In-frame table operations + simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval +#> # A tibble: 5 × 13 +#> study_id site_number events_per_visit_site events visits n_pat prob_low +#> <chr> <chr> <dbl> <dbl> <dbl> <int> <dbl> +#> 1 A S0001 0.183 76 415 20 0 +#> 2 A S0002 0.186 75 403 20 0 +#> 3 A S0003 0.496 187 377 20 0.999 +#> 4 A S0004 0.453 180 397 20 0.987 +#> 5 A S0005 0.492 187 380 20 0.996 +#> # ℹ 6 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl>, prob_high <dbl>, prob_high_adj <dbl>, +#> # prob_high_prob_or <dbl> + simaerep(df_visit, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval +#> # A tibble: 5 × 15 +#> study_id site_number events_per_visit_site events visits n_pat prob_low +#> <chr> <chr> <dbl> <dbl> <dbl> <int> <dbl> +#> 1 A S0001 0.183 56 306 18 0 +#> 2 A S0002 0.170 46 270 18 0 +#> 3 A S0003 0.533 145 272 17 1 +#> 4 A S0004 0.427 128 300 20 0.892 +#> 5 A S0005 0.523 134 256 16 0.996 +#> # ℹ 8 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl>, prob_high <dbl>, prob_high_adj <dbl>, +#> # prob_high_prob_or <dbl>, n_pat_with_med75 <dbl>, visit_med75 <dbl> + # Database example + con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") + df_r <- tibble::tibble(rep = seq(1, 1000)) + dplyr::copy_to(con, df_visit, "visit") + dplyr::copy_to(con, df_r, "r") + tbl_visit <- dplyr::tbl(con, "visit") + tbl_r <- dplyr::tbl(con, "r") + simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval +#> # Source: SQL [5 x 13] +#> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] +#> # Ordered by: study_id, site_number +#> study_id site_number events_per_visit_site events visits n_pat prob_low +#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> +#> 1 A S0005 0.492 187 380 20 0.998 +#> 2 A S0003 0.496 187 377 20 0.998 +#> 3 A S0004 0.453 180 397 20 0.987 +#> 4 A S0001 0.183 76 415 20 0 +#> 5 A S0002 0.186 75 403 20 0 +#> # ℹ 6 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl>, prob_high <dbl>, prob_high_adj <dbl>, +#> # prob_high_prob_or <dbl> + simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval +#> # Source: SQL [5 x 15] +#> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] +#> # Ordered by: study_id, site_number +#> study_id site_number events_per_visit_site events visits n_pat prob_low +#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> +#> 1 A S0003 0.533 145 272 17 1 +#> 2 A S0005 0.523 134 256 16 1 +#> 3 A S0004 0.427 128 300 20 0.908 +#> 4 A S0002 0.170 46 270 18 0 +#> 5 A S0001 0.183 56 306 18 0 +#> # ℹ 8 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>, +#> # prob_low_prob_ur <dbl>, prob_high <dbl>, prob_high_adj <dbl>, +#> # prob_high_prob_or <dbl>, n_pat_with_med75 <dbl>, visit_med75 <int> + DBI::dbDisconnect(con) +# }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/simaerep_inframe.html b/docs/reference/simaerep_inframe.html new file mode 100644 index 0000000..99ebe2b --- /dev/null +++ b/docs/reference/simaerep_inframe.html @@ -0,0 +1,207 @@ + +simulate in dataframe — simaerep_inframe • simaerep + Skip to contents + + +
    +
    +
    + +
    +

    simulate in dataframe

    +
    + +
    +

    Usage

    +
    simaerep_inframe(
    +  df_visit,
    +  r = 1000,
    +  under_only = FALSE,
    +  visit_med75 = FALSE,
    +  check = TRUE,
    +  param_site_aggr = list(method = "med75_adj", min_pat_pool = 0.2),
    +  param_eval_sites = list(method = "BH"),
    +  env = parent.frame()
    +)
    +
    + +
    +

    Arguments

    + + +
    df_visit
    +

    Data frame with columns: study_id, site_number, patnum, visit, +n_ae.

    + + +
    r
    +

    Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.

    + + +
    under_only
    +

    Logical, compute under-reporting probabilities only. +Supersedes under_only parameter passed to eval_sites() and sim_sites(). +Default: TRUE.

    + + +
    visit_med75
    +

    Logical, should evaluation point visit_med75 be used. +Default: TRUE.

    + + +
    check
    +

    Logical, perform data check and attempt repair with +check_df_visit(). Computationally expensive on large data sets. Default: +TRUE.

    + + +
    param_site_aggr
    +

    List of parameters passed to site_aggr(). Default: +list(method = "med75_adj", min_pat_pool = 0.2).

    + + +
    param_eval_sites
    +

    List of parameters passed to eval_sites(). Default: +list(method = "BH").

    + + +
    env
    +

    Optional, provide environment of original visit data. Default: +parent.frame().

    + +
    + +
    +

    Examples

    +
    df_visit <- sim_test_data_study(
    + n_pat = 100,
    + n_sites = 5,
    + frac_site_with_ur = 0.4,
    + ur_rate = 0.6
    +)
    +df_visit$study_id <- "A"
    +
    +simaerep_inframe(df_visit)
    +#> simaerep object:
    +#> Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty.
    +#> Plot results using plot() generic.
    +simaerep_inframe(df_visit, visit_med75 = TRUE)$df_eval
    +#> # A tibble: 5 × 12
    +#>   study_id site_number events_per_visit_site events visits n_pat prob_low
    +#>   <chr>    <chr>                       <dbl>  <dbl>  <dbl> <int>    <dbl>
    +#> 1 A        S0001                       0.261     61    234    18    0.006
    +#> 2 A        S0002                       0.201     61    304    19    0    
    +#> 3 A        S0003                       0.426    116    272    17    0.894
    +#> 4 A        S0004                       0.489    130    266    19    0.994
    +#> 5 A        S0005                       0.481    139    289    17    0.989
    +#> # ℹ 5 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
    +#> #   prob_low_prob_ur <dbl>, n_pat_with_med75 <dbl>, visit_med75 <dbl>
    +# \donttest{
    +# Database
    +con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:")
    +df_r <- tibble::tibble(rep = seq(1, 1000))
    +
    +dplyr::copy_to(con, df_visit, "visit")
    +dplyr::copy_to(con, df_r, "r")
    +
    +tbl_visit <- dplyr::tbl(con, "visit")
    +tbl_r <- dplyr::tbl(con, "r")
    +
    +simaerep_inframe(tbl_visit, r = tbl_r)$df_eval
    +#> # Source:     SQL [5 x 10]
    +#> # Database:   DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:]
    +#> # Ordered by: study_id, site_number
    +#>   study_id site_number events_per_visit_site events visits n_pat prob_low
    +#>   <chr>    <chr>                       <dbl>  <dbl>  <dbl> <dbl>    <dbl>
    +#> 1 A        S0003                       0.433    165    381    20    0.821
    +#> 2 A        S0001                       0.239     84    352    20    0    
    +#> 3 A        S0002                       0.210     83    396    20    0    
    +#> 4 A        S0004                       0.480    184    383    20    0.985
    +#> 5 A        S0005                       0.470    181    385    20    0.962
    +#> # ℹ 3 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
    +#> #   prob_low_prob_ur <dbl>
    +simaerep_inframe(tbl_visit, r = tbl_r, visit_med75 = TRUE)$df_eval
    +#> # Source:     SQL [5 x 12]
    +#> # Database:   DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:]
    +#> # Ordered by: study_id, site_number
    +#>   study_id site_number events_per_visit_site events visits n_pat prob_low
    +#>   <chr>    <chr>                       <dbl>  <dbl>  <dbl> <dbl>    <dbl>
    +#> 1 A        S0004                       0.489    130    266    19    0.986
    +#> 2 A        S0002                       0.201     61    304    19    0    
    +#> 3 A        S0005                       0.481    139    289    17    0.987
    +#> 4 A        S0003                       0.426    116    272    17    0.894
    +#> 5 A        S0001                       0.261     61    234    18    0.011
    +#> # ℹ 5 more variables: events_per_visit_study <dbl>, prob_low_adj <dbl>,
    +#> #   prob_low_prob_ur <dbl>, n_pat_with_med75 <dbl>, visit_med75 <int>
    +
    +DBI::dbDisconnect(con)
    +# }
    +
    +
    +
    + + +
    + + + +
    + + + + + + + diff --git a/docs/reference/site_aggr.html b/docs/reference/site_aggr.html index a771302..45d2aeb 100644 --- a/docs/reference/site_aggr.html +++ b/docs/reference/site_aggr.html @@ -1,138 +1,94 @@ -Aggregate from visit to site level. — site_aggr • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Calculates visit_med75, n_pat_with_med75 and mean_ae_site_med75

    -
    +
    +

    Usage

    site_aggr(df_visit, method = "med75_adj", min_pat_pool = 0.2, check = TRUE)
    -
    -

    Arguments

    -
    df_visit
    +
    +

    Arguments

    + + +
    df_visit

    dataframe with columns: study_id, site_number, patnum, visit, n_ae

    -
    method
    +
    method

    character, one of c("med75", "med75_adj") defining method for defining evaluation point visit_med75 (see details), Default: "med75_adj"

    -
    min_pat_pool,
    +
    min_pat_pool,

    double, minimum ratio of available patients available for sampling. Determines maximum visit_med75 value see Details. Default: 0.2

    -
    check,
    +
    check,

    logical, perform data check and attempt repair with check_df_visit(), computationally expensive on large data sets. Default: TRUE

    -
    -

    Value

    - - -

    dataframe with the following columns:

    study_id
    +
    +

    Value

    +

    dataframe with the following columns:

    study_id

    study identification

    site_number

    site @@ -149,10 +105,10 @@

    Value

    mean_ae_site_med75

    mean AE at visit_med75, site level

    - +
    -
    -

    Details

    +
    +

    Details

    For determining the visit number at which we are going to evaluate AE reporting we take the maximum visit of each patient at the site and take the median. Then we multiply with 0.75 which will give us a cut-off point @@ -164,8 +120,8 @@

    Details

    of all patients in the study.

    -
    -

    Examples

    +
    +

    Examples

    df_visit <- sim_test_data_study(
       n_pat = 100,
       n_sites = 5,
    @@ -183,34 +139,30 @@ 

    Examples

    #> #> |study_id |site_number | n_pat| n_pat_with_med75| visit_med75| mean_ae_site_med75| #> |:--------|:-----------|-----:|----------------:|-----------:|------------------:| -#> |A |S0001 | 20| 19| 15| 2.74| -#> |A |S0002 | 20| 18| 15| 2.56| -#> |A |S0003 | 20| 17| 16| 8.53| -#> |A |S0004 | 20| 20| 15| 6.40| -#> |A |S0005 | 20| 16| 16| 8.38| +#> |A |S0001 | 20| 18| 15| 2.39| +#> |A |S0002 | 20| 19| 13| 2.68| +#> |A |S0003 | 20| 19| 16| 7.74| +#> |A |S0004 | 20| 16| 17| 8.69| +#> |A |S0005 | 20| 16| 18| 8.19|
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/reference/with_progress_cnd.html b/docs/reference/with_progress_cnd.html index b142011..4cf8f71 100644 --- a/docs/reference/with_progress_cnd.html +++ b/docs/reference/with_progress_cnd.html @@ -1,143 +1,101 @@ -Conditional with_progress. — with_progress_cnd • simaerepConditional with_progress. — with_progress_cnd • simaerep - - -
    -
    -
    - + + +
    +
    +
    +
    -
    +

    Internal function. Use instead of with_progress within custom functions with progress bars.

    -
    +
    +

    Usage

    with_progress_cnd(ex, progress = TRUE)
    -
    -

    Arguments

    -
    ex
    +
    +

    Arguments

    + + +
    ex

    expression

    -
    progress
    +
    progress

    logical, Default: TRUE

    -
    -

    Value

    - - -

    No return value, called for side effects

    +
    +

    Value

    +

    No return value, called for side effects

    -
    -

    Details

    +
    +

    Details

    This wrapper adds a progress parameter to with_progress so that we can control the progress bar in the user facing functions. The progressbar only shows in interactive mode.

    -
    -

    See also

    +
    +

    See also

    -
    -

    Examples

    +
    +

    Examples

    if (interactive()) {
     
      with_progress_cnd(
    @@ -177,27 +135,23 @@ 

    Examples

    }
    -
    - -
    +
    -
    +
    + - - diff --git a/docs/search.json b/docs/search.json index fe51488..a79cacd 100644 --- a/docs/search.json +++ b/docs/search.json @@ -1 +1 @@ -[] +[{"path":"https://openpharma.github.io/simaerep/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2020 F. Hoffmann-La Roche Ltd simaerep authors Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Check Poisson Test Applicability","text":"","code":"suppressPackageStartupMessages( library(tidyverse) ) suppressPackageStartupMessages( library(knitr) ) suppressPackageStartupMessages( library(furrr) ) suppressPackageStartupMessages( library(future) ) suppressPackageStartupMessages( library(simaerep) )"},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Check Poisson Test Applicability","text":"Perhaps might scenarios one prefers use parametric test vs non-parametric bootstrap-based resampling method calculate AE -reporting. simaerep provides function bootstrap resampling entire study preserving actual site parameters number patients visit_med75. Using pool can check whether p-values calculated poisson.test() represent accurate probabilities.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"test-data-and-standard-processing","dir":"Articles","previous_headings":"Introduction","what":"Test Data and Standard Processing","title":"Check Poisson Test Applicability","text":"simulate three studies varying ae per visit rate use functions already introduced.","code":"df_visit1 <- sim_test_data_study( n_pat = 1000, n_sites = 100, frac_site_with_ur = 0.2, ur_rate = 0.5, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = 0.5 ) df_visit1$study_id <- \"ae_per_visit: 0.5\" df_visit2 <- sim_test_data_study( n_pat = 1000, n_sites = 100, frac_site_with_ur = 0.2, ur_rate = 0.5, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = 0.2 ) df_visit2$study_id <- \"ae_per_visit: 0.2\" df_visit3 <- sim_test_data_study( n_pat = 1000, n_sites = 100, frac_site_with_ur = 0.2, ur_rate = 0.5, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = 0.05 ) df_visit3$study_id <- \"ae_per_visit: 0.05\" df_visit <- bind_rows(df_visit1, df_visit2, df_visit3) df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit) df_visit ## # A tibble: 58,505 × 9 ## patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit ## ## 1 P000001 S0001 TRUE 20 4 0.25 1 ## 2 P000001 S0001 TRUE 20 4 0.25 2 ## 3 P000001 S0001 TRUE 20 4 0.25 3 ## 4 P000001 S0001 TRUE 20 4 0.25 4 ## 5 P000001 S0001 TRUE 20 4 0.25 5 ## 6 P000001 S0001 TRUE 20 4 0.25 6 ## 7 P000001 S0001 TRUE 20 4 0.25 7 ## 8 P000001 S0001 TRUE 20 4 0.25 8 ## 9 P000001 S0001 TRUE 20 4 0.25 9 ## 10 P000001 S0001 TRUE 20 4 0.25 10 ## # ℹ 58,495 more rows ## # ℹ 2 more variables: n_ae , study_id df_site ## # A tibble: 300 × 6 ## study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 ## ## 1 ae_per_vis… S0001 10 10 15 0.3 ## 2 ae_per_vis… S0002 10 9 15 0.667 ## 3 ae_per_vis… S0003 10 8 17 0.375 ## 4 ae_per_vis… S0004 10 10 15 0.3 ## 5 ae_per_vis… S0005 10 9 18 0.222 ## 6 ae_per_vis… S0006 10 9 16 0.556 ## 7 ae_per_vis… S0007 10 9 17 0.667 ## 8 ae_per_vis… S0008 10 9 16 0.333 ## 9 ae_per_vis… S0009 10 10 14 0.3 ## 10 ae_per_vis… S0010 10 10 17 0.2 ## # ℹ 290 more rows df_sim_sites ## # A tibble: 300 × 10 ## study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 ## ## 1 ae_per_vis… S0001 10 10 15 0.3 ## 2 ae_per_vis… S0002 10 9 15 0.667 ## 3 ae_per_vis… S0003 10 8 17 0.375 ## 4 ae_per_vis… S0004 10 10 15 0.3 ## 5 ae_per_vis… S0005 10 9 18 0.222 ## 6 ae_per_vis… S0006 10 9 16 0.556 ## 7 ae_per_vis… S0007 10 9 17 0.667 ## 8 ae_per_vis… S0008 10 9 16 0.333 ## 9 ae_per_vis… S0009 10 10 14 0.3 ## 10 ae_per_vis… S0010 10 10 17 0.2 ## # ℹ 290 more rows ## # ℹ 4 more variables: mean_ae_study_med75 , n_pat_with_med75_study , ## # pval , prob_low "},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"simulate-studies","dir":"Articles","previous_headings":"Introduction","what":"Simulate Studies","title":"Check Poisson Test Applicability","text":"sim_studies() reproduces study hundred times using bootstrap resampling maintaining number patients visit_med75 site.","code":"set.seed(1) plan(multisession, workers = 6) df_sim_studies <- sim_studies(df_visit, df_site, r = 100, parallel = TRUE, poisson_test = TRUE, prob_lower = TRUE, .progress = FALSE) df_sim_studies ## # A tibble: 30,000 × 8 ## r study_id site_number visit_med75 n_pat_with_med75 n_pat_study pval ## ## 1 1 ae_per_visi… S0001 15 10 905 1 ## 2 1 ae_per_visi… S0002 15 9 906 0.673 ## 3 1 ae_per_visi… S0003 17 8 760 1 ## 4 1 ae_per_visi… S0004 15 10 905 1 ## 5 1 ae_per_visi… S0005 18 9 678 0.850 ## 6 1 ae_per_visi… S0006 16 9 828 1 ## 7 1 ae_per_visi… S0007 17 9 759 1 ## 8 1 ae_per_visi… S0008 16 9 828 1 ## 9 1 ae_per_visi… S0009 14 10 942 1 ## 10 1 ae_per_visi… S0010 17 10 758 0.714 ## # ℹ 29,990 more rows ## # ℹ 1 more variable: prob_low "},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"check-p-value-probabilities","dir":"Articles","previous_headings":"Introduction","what":"Check p-value Probabilities","title":"Check Poisson Test Applicability","text":"get_ecd_values() uses p-value distribution dataframe returned sim_studies() train empirical cumulative distribution function study used calculate probability specific p-value lower poisson.test p-value returned sim_sites() Note: Dots edge graph cut zero value corresponding axis. can see p-values poisson.test (x-axis) less match probability represented resampled studies (y-axis), skewness relationship.","code":"df_check_pval <- get_ecd_values(df_sim_studies, df_sim_sites, val_str = \"pval\") df_check_pval ## # A tibble: 300 × 11 ## study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 ## ## 1 ae_per_vis… S0001 10 10 15 0.3 ## 2 ae_per_vis… S0002 10 9 15 0.667 ## 3 ae_per_vis… S0003 10 8 17 0.375 ## 4 ae_per_vis… S0004 10 10 15 0.3 ## 5 ae_per_vis… S0005 10 9 18 0.222 ## 6 ae_per_vis… S0006 10 9 16 0.556 ## 7 ae_per_vis… S0007 10 9 17 0.667 ## 8 ae_per_vis… S0008 10 9 16 0.333 ## 9 ae_per_vis… S0009 10 10 14 0.3 ## 10 ae_per_vis… S0010 10 10 17 0.2 ## # ℹ 290 more rows ## # ℹ 5 more variables: mean_ae_study_med75 , n_pat_with_med75_study , ## # pval , prob_low , pval_ecd df_check_pval %>% ggplot(aes(log(pval, base = 10), log(pval_ecd, base = 10))) + geom_point(alpha = 0.5, size = 2) + facet_wrap(~ study_id) + geom_abline(slope = 1, linetype = 2) + coord_cartesian( xlim = c(-5,0), ylim = c(-5,0) ) + theme(aspect.ratio = 1)"},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"perform-same-check-on-bootstrapped-probabilities","dir":"Articles","previous_headings":"Introduction","what":"Perform Same Check on Bootstrapped Probabilities","title":"Check Poisson Test Applicability","text":"Note: Dots edge graph cut zero value corresponding axis. tie simulations using 1000 repeats smallest value greater zero get prob_low 0.001 (1e-3). see probabilities site simulations (x-axis) perfectly match probability represented resampled studies (y-axis).","code":"df_check_prob <- get_ecd_values(df_sim_studies, df_sim_sites, val_str = \"prob_low\") df_check_prob ## # A tibble: 300 × 11 ## study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 ## ## 1 ae_per_vis… S0001 10 10 15 0.3 ## 2 ae_per_vis… S0002 10 9 15 0.667 ## 3 ae_per_vis… S0003 10 8 17 0.375 ## 4 ae_per_vis… S0004 10 10 15 0.3 ## 5 ae_per_vis… S0005 10 9 18 0.222 ## 6 ae_per_vis… S0006 10 9 16 0.556 ## 7 ae_per_vis… S0007 10 9 17 0.667 ## 8 ae_per_vis… S0008 10 9 16 0.333 ## 9 ae_per_vis… S0009 10 10 14 0.3 ## 10 ae_per_vis… S0010 10 10 17 0.2 ## # ℹ 290 more rows ## # ℹ 5 more variables: mean_ae_study_med75 , n_pat_with_med75_study , ## # pval , prob_low , prob_low_ecd df_check_prob %>% ggplot(aes(log(prob_low, base = 10), log(prob_low_ecd, base = 10))) + geom_point(alpha = 0.5, size = 2) + facet_wrap(~ study_id) + geom_abline(slope = 1, linetype = 2) + coord_cartesian( xlim = c(-5,0), ylim = c(-5,0) ) + theme(aspect.ratio = 1)"},{"path":"https://openpharma.github.io/simaerep/articles/check_poisson.html","id":"conclusion","dir":"Articles","previous_headings":"Introduction","what":"Conclusion","title":"Check Poisson Test Applicability","text":"see bootstrap method gives us accurate probabilities p-values derived poisson.test even though AE generation sample data follows strict poisson process. real clinical trial data different types studies clear whether AE generation truly based pure poisson process recommend use bootstrap method. poisson.test shall used recommend checking applicability just demonstrated clinical data set.","code":"plan(sequential)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(furrr)) suppressPackageStartupMessages(library(future)) suppressPackageStartupMessages(library(simaerep)) plan(multisession, workers = 6)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"install-gsm","dir":"Articles","previous_headings":"","what":"Install {gsm}","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"devtools::install_github(\"Gilead-BioStats/gsm@v1.9.2\", ref = \"main\")"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Comparing {simaerep} and Funnel Plot Performance","text":"{gsm} R package provides standardized Risk Based Quality Monitoring (RBQM) framework clinical trials pairs flexible data pipeline robust reports. also uses Funnel Plots flag outliers provide broader tolerance limits sites low exposure narrower limits sites higher exposure. method different event rate based limits used previous heuristics measure {simaerep} performance. Funnel plots discussed greater detail Zink et al. 2018 One draw backs using funnel plots flagging assume AE rate remains constant course study.","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"load-portfolio-configurations","dir":"Articles","previous_headings":"Prepare Data","what":"Load Portfolio Configurations","title":"Comparing {simaerep} and Funnel Plot Performance","text":"prepared snapshot AE reporting configuration current portfolio. study also measured visit-specific AE rate allows us generate synthetic portfolio flexible AE rates across study.","code":"df_config <- readr::read_csv(\"ae_conf_20240220.csv\") df_ae_rates <- readr::read_csv(\"ae_rates_20240220.csv\") df_config %>% head(25) %>% knitr::kable() df_ae_rates %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"simulate-portfolio","dir":"Articles","previous_headings":"Prepare Data","what":"Simulate Portfolio","title":"Comparing {simaerep} and Funnel Plot Performance","text":"generate two synthetic portfolios AE -reporting sites. One portfolio fixed AE rate visits another one flexible visit-specific AE rate.","code":"df_portf_fix <- sim_test_data_portfolio(df_config, parallel = TRUE, progress = TRUE) df_portf_fix %>% head(25) %>% knitr::kable() df_portf_flex <- sim_test_data_portfolio(df_config, df_ae_rates = df_ae_rates, parallel = TRUE, progress = TRUE)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"compare-ae-rates","dir":"Articles","previous_headings":"Prepare Data","what":"Compare AE rates","title":"Comparing {simaerep} and Funnel Plot Performance","text":"Next confirm different AE rates two synthetic portfolios. can confirm AE rates “flexible” portfolio constant. Moreover see AE rate profile unique study.","code":"df_rate_fix <- df_portf_fix %>% mutate(ae_rate = coalesce(n_ae - lag(n_ae), n_ae), .by = c(\"study_id\", \"patnum\")) %>% summarise(ae_rate = mean(ae_rate), .by = c(\"study_id\", \"visit\")) %>% mutate(rate = \"fix\") df_rate_flex <- df_portf_flex %>% mutate(ae_rate = coalesce(n_ae - lag(n_ae), n_ae), .by = c(\"study_id\", \"patnum\")) %>% summarise(ae_rate = mean(ae_rate), .by = c(\"study_id\", \"visit\")) %>% mutate(rate = \"flex\") bind_rows(df_rate_flex, df_rate_fix) %>% ggplot(aes(visit, ae_rate)) + geom_line(aes(group = study_id), alpha = 0.2) + geom_smooth() + facet_wrap(~ rate) + labs(title = \"Average AE rates per Study\") bind_rows(df_rate_flex, df_rate_fix) %>% filter(dense_rank(study_id) <= 16) %>% ggplot(aes(visit, ae_rate)) + geom_line(aes(group = rate, color = rate)) + facet_wrap(~ study_id, scales = \"free\") + labs(title = \"Average AE rates for Selected Studies\")"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"example","dir":"Articles","previous_headings":"Funnel Plots {gsm}","what":"Example","title":"Comparing {simaerep} and Funnel Plot Performance","text":"demonstrate use {gsm} package simulated portfolios can get good visualisation funnel plot.","code":"get_SUBJ <- function(df_portf) { df_portf %>% select(study_id, siteid = site_number, subjid = patnum, timeonstudy = visit) %>% summarise(timeonstudy = max(timeonstudy), .by = c(study_id, siteid, subjid)) %>% group_by(study_id) %>% nest() } get_AE <- function(df_portf) { df_portf %>% select(study_id, subjid = patnum, n_ae) %>% summarise(n_ae = max(n_ae), .by = c(study_id, subjid)) %>% filter(n_ae > 0) %>% mutate(n_ae = map(n_ae, ~ tibble(n = seq(1, .)), .progress = TRUE)) %>% unnest(n_ae) %>% select(- n) %>% group_by(study_id) %>% nest() } dfSUBJ_fix <- get_SUBJ(df_portf_fix) dfAE_fix <- get_AE(df_portf_fix) dfInput <- gsm::AE_Map_Raw(list(dfSUBJ = dfSUBJ_fix$data[[1]], dfAE = dfAE_fix$data[[1]])) dfInput ## # A tibble: 113 × 5 ## SubjectID SiteID Exposure Count Rate ## ## 1 0001 4746 16 5 0.312 ## 2 0002 4746 14 3 0.214 ## 3 0003 4747 26 6 0.231 ## 4 0004 4747 26 11 0.423 ## 5 0005 4750 19 5 0.263 ## 6 0006 4750 19 5 0.263 ## 7 0007 4750 20 6 0.3 ## 8 0008 4750 18 3 0.167 ## 9 0009 4815 25 3 0.12 ## 10 0010 4815 28 8 0.286 ## # ℹ 103 more rows dfTransformed <- gsm::Transform_Rate( dfInput, strNumeratorCol = \"Count\", strDenominatorCol = \"Exposure\" ) dfTransformed ## # A tibble: 44 × 4 ## GroupID Numerator Denominator Metric ## ## 1 4746 8 30 0.267 ## 2 4747 17 52 0.327 ## 3 4750 19 76 0.25 ## 4 4815 40 153 0.261 ## 5 4816 9 27 0.333 ## 6 4817 5 31 0.161 ## 7 4818 9 30 0.3 ## 8 4891 48 151 0.318 ## 9 4893 15 40 0.375 ## 10 4932 33 137 0.241 ## # ℹ 34 more rows dfAnalyzed <- gsm::Analyze_NormalApprox(dfTransformed) dfAnalyzed ## # A tibble: 44 × 7 ## GroupID Numerator Denominator Metric OverallMetric Factor Score ## ## 1 4942 6 47 0.128 0.288 1.08 -2.34 ## 2 5166 17 95 0.179 0.288 1.08 -2.27 ## 3 4817 5 31 0.161 0.288 1.08 -1.51 ## 4 5229 12 58 0.207 0.288 1.08 -1.32 ## 5 4932 33 137 0.241 0.288 1.08 -1.18 ## 6 4986 13 58 0.224 0.288 1.08 -1.04 ## 7 4985 28 114 0.246 0.288 1.08 -0.972 ## 8 5084 4 21 0.190 0.288 1.08 -0.954 ## 9 5082 9 41 0.220 0.288 1.08 -0.938 ## 10 4969 14 60 0.233 0.288 1.08 -0.907 ## # ℹ 34 more rows dfFlagged <- gsm::Flag_NormalApprox(dfAnalyzed, vThreshold = c(-3, -2, 2, 3)) dfFlagged ## # A tibble: 44 × 8 ## GroupID Numerator Denominator Metric OverallMetric Factor Score Flag ## ## 1 5194 66 182 0.363 0.288 1.08 2.13 1 ## 2 4942 6 47 0.128 0.288 1.08 -2.34 -1 ## 3 5166 17 95 0.179 0.288 1.08 -2.27 -1 ## 4 4817 5 31 0.161 0.288 1.08 -1.51 0 ## 5 5229 12 58 0.207 0.288 1.08 -1.32 0 ## 6 4932 33 137 0.241 0.288 1.08 -1.18 0 ## 7 4986 13 58 0.224 0.288 1.08 -1.04 0 ## 8 4985 28 114 0.246 0.288 1.08 -0.972 0 ## 9 5084 4 21 0.190 0.288 1.08 -0.954 0 ## 10 5082 9 41 0.220 0.288 1.08 -0.938 0 ## # ℹ 34 more rows dfSummary <- gsm::Summarize(dfFlagged) dfSummary ## # A tibble: 44 × 6 ## GroupID Numerator Denominator Metric Score Flag ## ## 1 5194 66 182 0.363 2.13 1 ## 2 5166 17 95 0.179 -2.27 -1 ## 3 4942 6 47 0.128 -2.34 -1 ## 4 4968 10 24 0.417 1.34 0 ## 5 4988 16 39 0.410 1.62 0 ## 6 5168 11 27 0.407 1.31 0 ## 7 5311 8 20 0.4 1.06 0 ## 8 5273 16 40 0.4 1.50 0 ## 9 4893 15 40 0.375 1.16 0 ## 10 5083 16 44 0.364 1.06 0 ## # ℹ 34 more rows dfBounds <- gsm::Analyze_NormalApprox_PredictBounds(dfTransformed, vThreshold = c(-3, -2, 2, 3)) dfBounds ## # A tibble: 1,254 × 5 ## Threshold Denominator LogDenominator Numerator Metric ## ## 1 -3 24.6 3.20 0.0927 0.00377 ## 2 -3 25.2 3.23 0.189 0.00750 ## 3 -3 25.9 3.25 0.287 0.0111 ## 4 -3 26.5 3.28 0.386 0.0145 ## 5 -3 27.2 3.30 0.485 0.0179 ## 6 -3 27.8 3.33 0.586 0.0211 ## 7 -3 28.5 3.35 0.688 0.0242 ## 8 -3 29.2 3.37 0.792 0.0272 ## 9 -3 29.8 3.39 0.895 0.0300 ## 10 -3 30.5 3.42 1.00 0.0328 ## # ℹ 1,244 more rows chart <- gsm::Visualize_Scatter(dfFlagged, dfBounds) chart"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"ur-funnel","dir":"Articles","previous_headings":"","what":"UR Funnel","title":"Comparing {simaerep} and Funnel Plot Performance","text":"write funnel function adapted {gsm}","code":"funnel_ur <- function(df, site, ur_rate) { df %>% filter(visit == max(visit), .by = patnum) %>% summarise( Metric = sum(.data$n_ae) / sum(.data$visit), n_ae = sum(n_ae), visit = sum(visit), .by = \"site_number\" ) %>% mutate( n_ae = ifelse(site_number == site, n_ae * (1 - ur_rate), n_ae), Metric = n_ae / visit ) %>% mutate( vMu = sum(.data$n_ae) / sum(.data$visit), z_0 = ifelse(.data$vMu == 0, 0, (.data$Metric - .data$vMu) / sqrt(.data$vMu / .data$visit) ), phi = mean(.data$z_0^2), z_i = ifelse(.data$vMu == 0 | .data$phi == 0, 0, (.data$Metric - .data$vMu) / sqrt(.data$phi * .data$vMu / .data$visit) ) ) %>% filter(site_number == site) %>% pull(z_i) } sim_ur_funnel <- function(df) { df %>% group_by(study_id) %>% nest() %>% ungroup() %>% mutate( sites = map(data, ~ distinct(., site_number)) ) %>% unnest(sites) %>% mutate(ur = list(tibble(ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1)))) %>% unnest(ur) %>% mutate( score = pmap_dbl(list(data, site_number, ur_rate), funnel_ur, .progress = TRUE) ) } df_sim_ur_funnel_flex <- sim_ur_funnel(df_portf_flex) df_sim_ur_funnel_fix <- sim_ur_funnel(df_portf_fix)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"ur-simaerep","dir":"Articles","previous_headings":"","what":"UR {simaerep}","title":"Comparing {simaerep} and Funnel Plot Performance","text":"simulate -reporting portfolios using {simaerep} using sim_ur_scenarios().","code":"df_sim_simaerep_fix <- sim_ur_scenarios( df_portf_fix, extra_ur_sites = 0, ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1), parallel = TRUE, poisson = TRUE, prob_lower = TRUE, progress = TRUE ) df_sim_simaerep_flex <- sim_ur_scenarios( df_portf_flex, extra_ur_sites = 0, ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1), parallel = TRUE, poisson = TRUE, prob_lower = TRUE, progress = TRUE )"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"combine-results","dir":"Articles","previous_headings":"Evaluate","what":"Combine Results","title":"Comparing {simaerep} and Funnel Plot Performance","text":"funnel plot score use multiplicity correction, also compare funnel plot score {simaerep} score w/o multiplicity correction.","code":"df_sim_simaerep_fix$ae_rate <- \"AE rate: fix\" df_sim_simaerep_flex$ae_rate <- \"AE rate: flexible\" df_sim_ur_funnel_fix$ae_rate <- \"AE rate: fix\" df_sim_ur_funnel_flex$ae_rate <- \"AE rate: flexible\" df_sim_fun_thresh2 <- bind_rows(df_sim_ur_funnel_fix, df_sim_ur_funnel_flex) %>% mutate( type = \"funnel\", ) %>% select(type, ae_rate, study_id, site_number, ur_rate, score) df_sim_simaerep_threshp95 <- bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>% mutate( type = \"{simaerep}\" ) %>% select(type, ae_rate, study_id, site_number, ur_rate, score = prob_low_prob_ur) df_sim_simaerep_threshp95_no_mult <- bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>% mutate( type = \"{simaerep} no mult\" ) %>% mutate( score = 1 - prob_low ) %>% select(type, ae_rate, study_id, site_number, ur_rate, score) df_eval <- bind_rows( df_sim_simaerep_threshp95, df_sim_simaerep_threshp95_no_mult, df_sim_fun_thresh2 )"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"auc","dir":"Articles","previous_headings":"Evaluate","what":"AUC","title":"Comparing {simaerep} and Funnel Plot Performance","text":"continue comparing ROC-AUC.","code":"get_roc <- function(df_ur, df_nr) { df <- bind_rows(df_ur, df_nr) pROC::roc(df, response = \"is_ur\", predictor = \"score\", quiet = TRUE) } # use 0 scenario to mix with ur scenario and calculate auc from scores df_nr <- df_eval %>% filter(ur_rate == 0) %>% mutate(is_ur = \"no\") %>% select(- ur_rate) %>% group_by(type, study_id, ae_rate) %>% nest() %>% ungroup() %>% rename(data_nr = data) df_ur <- df_eval %>% filter(ur_rate > 0) %>% mutate(is_ur = \"yes\") %>% group_by(type, study_id, ur_rate, ae_rate) %>% nest() %>% ungroup() %>% rename(data_ur = data) df_auc <- df_ur %>% left_join( df_nr, by = c(\"type\", \"study_id\", \"ae_rate\") ) %>% mutate( roc = map2(data_ur, data_nr, get_roc, .progress = TRUE), auc = map_dbl(roc, pROC::auc, .progress = TRUE) ) %>% select(type, study_id, ur_rate, ae_rate, roc, auc)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"table","dir":"Articles","previous_headings":"Evaluate > AUC","what":"Table","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"df_auc %>% summarise( sd_auc = sd(.data$auc), auc = mean(.data$auc), .by = c(type, ur_rate, ae_rate) ) %>% knitr::kable(digit = 3)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"plot","dir":"Articles","previous_headings":"Evaluate > AUC","what":"Plot","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"df_auc %>% ggplot(aes(type, auc)) + geom_boxplot(aes(fill = type)) + facet_grid(ae_rate ~ ur_rate) + scale_fill_brewer(palette = \"Dark2\") + theme(axis.text.x = element_blank())"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"thresholds","dir":"Articles","previous_headings":"Evaluate > Metrics","what":"Thresholds","title":"Comparing {simaerep} and Funnel Plot Performance","text":"set thresholds funnel simaerep w/o multiplicity correction get fpr simaerep multiplicity correction established threshold 0.95","code":"thresh_default <- 0.95 target_fpr <- df_eval %>% filter(ur_rate == 0, type == \"{simaerep}\") %>% summarise(fpr = sum(score >= thresh_default) / n()) %>% pull(fpr) thresh_no_mult <- df_eval %>% filter(ur_rate == 0, type == \"{simaerep} no mult\") %>% pull(score) %>% quantile(1 - target_fpr) thresh_funnel <- df_eval %>% filter(ur_rate == 0, type == \"funnel\") %>% pull(score) %>% quantile(target_fpr) target_fpr ## [1] 0.0005309081 thresh_no_mult ## 99.94691% ## 0.999 thresh_funnel ## 0.05309081% ## -2.979981"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"aggregate","dir":"Articles","previous_headings":"Evaluate > Metrics","what":"Aggregate","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"get_prop_test_ci95 <- function(..., ix) { stopifnot(ix %in% c(1, 2)) tryCatch( prop.test(...)$conf.int[ix], error = function(cnd) c(NA, NA)[ix] ) } aggr_results <- function(df_eval) { df_perf <- df_eval %>% mutate( is_ur = case_when( type == \"{simaerep}\" ~ score >= thresh_default, type == \"{simaerep} no mult\" ~ score >= thresh_no_mult, type == \"funnel\" ~ score <= thresh_funnel ) ) %>% summarise( n = n(), .by = c(type, ae_rate, ur_rate, is_ur) ) %>% pivot_wider( names_from = is_ur, values_from = n, names_prefix = \"is_ur_\", values_fill = 0 ) %>% mutate( n_sites = is_ur_TRUE + is_ur_FALSE, ratio = is_ur_TRUE / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)), ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)) ) } df_perf <- aggr_results(df_eval)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"table-1","dir":"Articles","previous_headings":"Evaluate > Metrics","what":"Table","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"df_perf %>% knitr::kable(digits = 4)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"plot-1","dir":"Articles","previous_headings":"Evaluate > Metrics","what":"Plot","title":"Comparing {simaerep} and Funnel Plot Performance","text":"","code":"plot_perf <- function(df_perf) { df_perf %>% mutate(ur_rate = paste0(\"under-reporting rate: \", ur_rate, \" - \", ratio_type), ur_rate = ifelse(str_detect(ur_rate, \"fpr\"), \"fpr\", ur_rate), ae_rate = forcats::fct_rev(factor(ae_rate))) %>% group_by(ur_rate) %>% ggplot(aes(type, ratio)) + geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type, linetype = ae_rate), linewidth = 1) + facet_wrap(~ ur_rate, ncol = 1) + coord_flip() + theme(legend.position = \"bottom\") + labs( x = \"\", y = \"CI95 Performance Ratio\", title = \"{simaerep} vs Funnel-Plot Performance\" ) + scale_color_brewer(palette = \"Dark2\") } plot_perf(df_perf)"},{"path":"https://openpharma.github.io/simaerep/articles/funnel_perf.html","id":"summary","dir":"Articles","previous_headings":"","what":"Summary","title":"Comparing {simaerep} and Funnel Plot Performance","text":"Funnel plot expects constant event rates time. Performance decrease event rates flexible. {simaerep} performance uneffected flexible event rates. {simaerep} detection rates comparable false positive rates greater funnel plot detection rates event rates flexible.","code":"plan(sequential)"},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Inframe Simulation Using Table Operations","text":"","code":"suppressPackageStartupMessages(library(dplyr)) suppressPackageStartupMessages(library(tibble)) suppressPackageStartupMessages(library(tidyr)) suppressPackageStartupMessages(library(simaerep))"},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Inframe Simulation Using Table Operations","text":"latest 0.6.0 release added alternative version {simaerep} algorithm coded using solely dbplyr compatible table operations. expand patients site r times join patient random eligible patient study replicate calculate event per visit rate per site calculate ratio lower event per visit rate actually observed comes following advantages disadvantages: Patients individually matched patients reached visit study. need pick visit_med75 evaluation point. dbplyr compatibility means code execution can done database back-end opposed -memory. Matching patients individually costly, increases -memory computation time Limited patient sample pool patients visits patients study.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"sample-data","dir":"Articles","previous_headings":"","what":"Sample Data","title":"Inframe Simulation Using Table Operations","text":"","code":"set.seed(1) df_visit <- sim_test_data_study( n_pat = 1000, # number of patients in study n_sites = 100, # number of sites in study frac_site_with_ur = 0.05, # fraction of sites under-reporting ur_rate = 0.4, # rate of under-reporting ae_per_visit_mean = 0.5 # mean AE per patient visit ) df_visit$study_id <- \"A\""},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"patient-level-matching","dir":"Articles","previous_headings":"","what":"Patient-Level Matching","title":"Inframe Simulation Using Table Operations","text":"use standard version algorithm. use patient-level matching algorithm set inframe=TRUE visit_med75=FALSE. original algorithm uses fixed seeds sampling inframe method . order obtain consistent results need manually set seed. plot shows sites 10/10 patients used none excluded. also observe site average become noisy less patients used calculate averages higher visit numbers. inframe method includes noisier data compare average event counts event per visit rates. can find events_per_visit_site events_per_visit_study df_eval. latter average event rate obtained simulation patient resampled according maximum visit. can also force inframe method use visit_med75 prefilter df_visit, adds extra step decreases performance.","code":"aerep_trad <- simaerep(df_visit) plot(aerep_trad) set.seed(1) aerep_inframe <- simaerep( df_visit, inframe = TRUE, visit_med75 = FALSE ) plot(aerep_inframe) aerep_inframe$df_eval ## # A tibble: 100 × 10 ## study_id site_number events_per_visit_site events visits n_pat prob_low ## ## 1 A S0001 0.279 50 179 10 0 ## 2 A S0002 0.281 59 210 10 0 ## 3 A S0003 0.291 55 189 10 0 ## 4 A S0004 0.312 59 189 10 0 ## 5 A S0005 0.297 58 195 10 0 ## 6 A S0006 0.493 100 203 10 0.569 ## 7 A S0007 0.408 89 218 10 0.044 ## 8 A S0008 0.559 95 170 10 0.922 ## 9 A S0009 0.518 101 195 10 0.761 ## 10 A S0010 0.470 87 185 10 0.385 ## # ℹ 90 more rows ## # ℹ 3 more variables: events_per_visit_study , prob_low_adj , ## # prob_low_prob_ur set.seed(1) aerep_inframe_visit_med75 <- simaerep( df_visit, inframe = TRUE, visit_med75 = TRUE ) plot(aerep_inframe_visit_med75)"},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"db","dir":"Articles","previous_headings":"","what":"DB","title":"Inframe Simulation Using Table Operations","text":"can demonstrate database-backend compatibility using connection memory duckdb database. order set number replications need create new table back-end one column many rows desired replications. lazy reference table can passed r parameter. inspecting df_eval see still lazy table object. can convert sql code. cte option makes sql code readable. can take code wrap CREATE TABLE statement Retrieve new table database. plot results {simaerep} object. efficiently using plot_study() already written simaerep results database. avoid results recalculated just sake creating plot. However requires save df_site database well.","code":"con <- DBI::dbConnect(duckdb::duckdb(), dbdir = \":memory:\") df_r <- tibble(rep = seq(1, 1000)) dplyr::copy_to(con, df_visit, \"visit\") dplyr::copy_to(con, df_r, \"r\") tbl_visit <- tbl(con, \"visit\") tbl_r <- tbl(con, \"r\") aerep <- simaerep(tbl_visit, r = tbl_r, visit_med75 = FALSE) aerep$df_eval ## # Source: SQL [?? x 10] ## # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] ## # Ordered by: study_id, site_number ## study_id site_number events_per_visit_site events visits n_pat prob_low ## ## 1 A S0040 0.477 95 199 10 0.43 ## 2 A S0082 0.470 87 185 10 0.382 ## 3 A S0084 0.513 101 197 10 0.698 ## 4 A S0030 0.538 106 197 10 0.828 ## 5 A S0037 0.429 85 198 10 0.143 ## 6 A S0034 0.432 79 183 10 0.148 ## 7 A S0095 0.452 80 177 10 0.255 ## 8 A S0089 0.481 91 189 10 0.467 ## 9 A S0031 0.49 98 200 10 0.527 ## 10 A S0069 0.503 88 175 10 0.644 ## # ℹ more rows ## # ℹ 3 more variables: events_per_visit_study , prob_low_adj , ## # prob_low_prob_ur sql_eval <- dbplyr::sql_render(aerep$df_eval, sql_options = dbplyr::sql_options(cte = TRUE)) stringr::str_trunc(sql_eval, 500) ## WITH q01 AS ( ## SELECT ## patnum, ## site_number, ## is_ur, ## max_visit_mean, ## max_visit_sd, ## ae_per_visit_mean, ## CAST(visit AS NUMERIC) AS visit, ## CAST(n_ae AS NUMERIC) AS n_ae, ## study_id ## FROM visit ## ), ## q02 AS ( ## SELECT DISTINCT study_id, site_number ## FROM q01 ## GROUP BY study_id, site_number, patnum ## ), ## q03 AS ( ## SELECT study_id, site_number, patnum, MAX(visit) AS max_visit_per_pat ## FROM q01 ## GROUP BY study_id, site_number, patnum ## ), ## q04 AS ( ## SELECT DISTINCT study... sql_create <- glue::glue(\"CREATE TABLE eval AS ({sql_eval})\") DBI::dbExecute(con, sql_create) ## [1] 100 tbl_eval <- tbl(con, \"eval\") tbl_eval ## # Source: table [?? x 10] ## # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] ## study_id site_number events_per_visit_site events visits n_pat prob_low ## ## 1 A S0039 0.472 92 195 10 0.389 ## 2 A S0010 0.470 87 185 10 0.385 ## 3 A S0024 0.495 91 184 10 0.579 ## 4 A S0014 0.505 103 204 10 0.629 ## 5 A S0043 0.503 91 181 10 0.629 ## 6 A S0036 0.506 89 176 10 0.666 ## 7 A S0067 0.428 83 194 10 0.127 ## 8 A S0019 0.448 74 165 10 0.256 ## 9 A S0042 0.470 85 181 10 0.379 ## 10 A S0094 0.469 99 211 10 0.379 ## # ℹ more rows ## # ℹ 3 more variables: events_per_visit_study , prob_low_adj , ## # prob_low_prob_ur plot(aerep) sql_site <- dbplyr::sql_render(aerep$df_site) DBI::dbExecute(con, glue::glue(\"CREATE TABLE site AS ({sql_site})\")) ## [1] 100 tbl_site <- tbl(con, \"site\") plot_study(tbl_visit, tbl_site, tbl_eval, study = \"A\") DBI::dbDisconnect(con)"},{"path":"https://openpharma.github.io/simaerep/articles/inframe.html","id":"in-memory-calculation-times","dir":"Articles","previous_headings":"","what":"In Memory Calculation Times","title":"Inframe Simulation Using Table Operations","text":"perform examplary tests illustrate increase -memory calculation time inframe calculation method. calculation time default settings inframe calculation time higher","code":"system.time({simaerep(df_visit, inframe = FALSE, visit_med75 = TRUE, under_only = TRUE, progress = FALSE)}) ## user system elapsed ## 0.793 0.010 0.802 system.time({simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = TRUE)}) ## user system elapsed ## 1.968 0.044 2.013"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Introduction","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(simaerep))"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Introduction","text":"Simulate adverse event reporting clinical trials goal detecting -reporting sites. Monitoring Adverse Event (AE) reporting clinical trials important patient safety. use bootstrap-based simulation assign AE -reporting probability site clinical trial. method inspired ‘infer’ R package Allen Downey’s blog article: “one test!”.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"adverse-events","dir":"Articles","previous_headings":"Introduction","what":"Adverse Events","title":"Introduction","text":"adverse event (AE) untoward medical occurrence patient clinical investigation subject administered pharmaceutical product necessarily causal relationship treatment. important patient safety AEs reported back sponsor. important part quality monitoring detect clinical trial sites propagating AEs reported patients sponsor. clinical trial patients follow strict visiting schedule times treatments given exams performed. Typically AEs get reported patient -site visit clinic. total number AEs reported site depends number patients enrolled site total number visits.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"algorithm","dir":"Articles","previous_headings":"Introduction","what":"Algorithm","title":"Introduction","text":"nutshell perform following steps. Record visit_med75, number patients reached visit_med75, mean cumulative AE count visit_med75 clinical trial site. Create patient pool patients study reached visit_med75 determined 1) cumulative AE count visit_med75. Draw replacement many patients 2) determined 1) calculate mean cumulative AE count draw (figure 1b). Repeat 3) 1000 times. Calculate probability obtaining mean cumulative AE count lower obtained 1) based results 4). Repeat 1-5) sites trial. Adjust probabilities using Benjamin Hochberg Procedure order correct alpha error.","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"patient","dir":"Articles","previous_headings":"Introduction > Sample Data","what":"Patient","title":"Introduction","text":"Patient level AE data characterized number consecutive visits number AEs reported time. maximum consecutive visit sample normal distribution AEs reported visit sample poisson distribution. simulate AEs generated 3 patients","code":"set.seed(1) replicate( 3, sim_test_data_patient( .f_sample_max_visit = function() rnorm(1, mean = 20, sd = 4), .f_sample_ae_per_visit = function(max_visit) rpois(max_visit, 0.5) ) ) ## [[1]] ## [1] 0 1 1 2 4 5 6 6 6 6 7 7 8 8 9 12 12 ## ## [[2]] ## [1] 0 1 1 1 1 1 1 2 2 2 2 2 2 3 4 5 5 6 6 7 8 9 9 ## ## [[3]] ## [1] 0 0 1 2 2 3 3 3 3 3 3 3 4 4 6 6 6 6 7 7"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"study","dir":"Articles","previous_headings":"Introduction > Sample Data","what":"Study","title":"Introduction","text":"order simulate patient data entire study assume make simplification sites number patients. specify fraction sites -reporting AEs. sample data 2 sites (S0001 S0002) -reporting AEs","code":"df_visit <- sim_test_data_study( n_pat = 120, n_sites = 6, frac_site_with_ur = 0.4, ur_rate = 0.6, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = 0.5 ) df_visit$study_id <- \"A\" df_visit %>% head(10) %>% knitr::kable() df_visit %>% select(site_number, is_ur) %>% distinct() %>% knitr::kable()"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"s3-interface","dir":"Articles","previous_headings":"Introduction > Algorithm Execution","what":"S3 interface","title":"Introduction","text":"describe internal details algorithm next. However, recommended use S3 interface, manages execution internal functions stores intermediate results.","code":"aerep <- simaerep(df_visit) aerep ## simaerep object: ## Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty. ## Plot results using plot() generic."},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"specifying-the-evaluation-point-visit_med75","dir":"Articles","previous_headings":"Introduction > Algorithm Execution","what":"Specifying the Evaluation Point visit_med75","title":"Introduction","text":"ongoing trial patients different number consecutive visits. find cut-visit normalize data specify single evaluation point given site based number visits patient population. determining visit number going evaluate AE reporting take maximum visit patient site take median. multiply 0.75 give us cut-point determining patient evaluated. patients evaluate take minimum maximum visits hence ensuring take highest visit number possible without excluding patients analysis. order ensure sampling pool visit large enough ensure least 20% patients study available sampling. limit visit_med75 80% quantile patient maximum visits entire study. use site_aggr aggregate simulated visit level data site level. Adjusting min_pat_pool change minimum ratio patients available sampling determines maximum values visit_med75. Using adjusted visit_med75 drop patients (dashed lines) definition least include 50% patients favoring patients higher number visits. looking mean ae development (purple line) can already easily spot two -reporting sites. goes looking mean_ae_site_med75 column df_site. Next use bootstrap simulations determine probability obtaining mean AE value lower visit_med75 chance.","code":"df_site <- site_aggr(df_visit, method = \"med75_adj\", min_pat_pool = 0.2) df_site %>% kable() plot_visit_med75(df_visit, df_site, study_id_str = \"A\", n_site = 6) ## purple line: mean site ae of patients with visit_med75 ## grey line: patient included ## black dashed line: patient excluded ## dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site ## solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients ## dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"advantage-over-classic-statistical-tests","dir":"Articles","previous_headings":"Introduction > Algorithm Execution > Bootstrap Simulations","what":"Advantage Over Classic Statistical Tests","title":"Introduction","text":"use classical parametric test calculate probability can reject NULL hypothesis AE counts observe visit_med75. sample AEs poisson distribution R implementation poisson.test appropriate. four major problems often encounter try describe real life count data poisson distribution described single parameter: underdispersion skewness (long right tail) inflated zeros variance mean relationship might fixed (see Distribution Modelling Location Scale Shape 5.1.2) true distribution AE counts vary study study degree influence 4 problems unknown unless thoroughly investigated. non-parametric approach propose need worry statistical assumptions. Since distribution AE count simulated patient pool draw close unknown true distribution underlying AE generating process.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"disadvantages-over-classic-statistical-tests","dir":"Articles","previous_headings":"Introduction > Algorithm Execution > Bootstrap Simulations","what":"Disadvantages Over Classic Statistical Tests","title":"Introduction","text":"Upper Limit Uncompliant Sites. simulate underlying compliant patient population using data given. fraction uncompliant examples becomes high, detection rates decrease. find detection rates start decreasing 30-50% -reporting sites usable anymore majority > 50% sites -reporting see article usability limits. Lower Probability Limit number repeats determines smallest probability greater zero, example r=1000 smallest value greater zero 0.001 Computationally Expensive","code":""},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"methodology","dir":"Articles","previous_headings":"Introduction > Algorithm Execution > Bootstrap Simulations","what":"Methodology","title":"Introduction","text":"likely get mean AE value equal lower observe site C number patients? illustration purposes start simulate 10 hypothetical patient groups site C drawing (replacement) patients study marking obtained equal lower mean AE value initially observed (middle). Instead 10 times simulate 1000 times count many times observed equal lower mean AE value initially observed convert percentage (right). illustrate effect -reporting repeat process removed 2 AEs per patient site C. see probability obtaining equal lower mean AE value initially observed decreases (bottom).","code":"plot_sim_examples(size_dot = 4, size_raster_label = 10)"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"application","dir":"Articles","previous_headings":"Introduction > Algorithm Execution > Bootstrap Simulations","what":"Application","title":"Introduction","text":"can run described simulation benchmark also perform poisson test. find probability getting mean ae visit_med75 sites S0001 S0002 0 near zero.","code":"df_sim_sites <- sim_sites(df_site, df_visit, r = 1000, poisson_test = TRUE) df_sim_sites %>% kable()"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"alpha-error-correction","dir":"Articles","previous_headings":"Introduction","what":"Alpha Error Correction","title":"Introduction","text":"simulated test data set consists just 6 sites. However, uncommon 100 sites participate clinical trial. mean need perform 100 statistical tests, applying 5% significance threshold lead average 5 False Positives (FP). therefore need adjust calculated p-values bootstrapped probabilities using stats::p.adjust(p, method = \"BH\"), applies Benjamin Hochberg Procedure. eval_sites() uses inverted adjusted values calculates final bootstrapped AE -reporting probability (prob_low_prob_ur) includes poisson test derived -reporting probability reference (pval_prob_ur).","code":"df_eval <- eval_sites(df_sim_sites, method = \"BH\") df_eval %>% kable()"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"plot-results","dir":"Articles","previous_headings":"Introduction","what":"Plot Results","title":"Introduction","text":"plot_study plot mean ae development sites visit level data flagged site AE -reporting threshold 95%.","code":"plot_study(df_visit, df_site, df_eval, study = \"A\")"},{"path":"https://openpharma.github.io/simaerep/articles/intro.html","id":"applying-simaerep","dir":"Articles","previous_headings":"","what":"Applying {simaerep}","title":"Introduction","text":"Instead executing previous steps manually can also use S3 interface manages intermediate results provides convenient plotting using plot() generic function. modifying default parameters please check simaerep() documentation.","code":"aerep <- simaerep(df_visit) aerep ## simaerep object: ## Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty. ## Plot results using plot() generic. str(aerep) ## List of 11 ## $ visit :List of 3 ## ..$ dim : int [1:2] 2336 9 ## ..$ df_summary: tibble [1 × 5] (S3: tbl_df/tbl/data.frame) ## .. ..$ n_studies : int 1 ## .. ..$ n_sites : int 6 ## .. ..$ n_patients: int 120 ## .. ..$ n_visits : int 2336 ## .. ..$ n_aes : int 942 ## ..$ str_call : chr \"df_visit\" ## ..- attr(*, \"class\")= chr \"orivisit\" ## $ df_site : tibble [6 × 6] (S3: tbl_df/tbl/data.frame) ## ..$ study_id : chr [1:6] \"A\" \"A\" \"A\" \"A\" ... ## ..$ site_number : chr [1:6] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 ## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, \"names\")= chr [1:6] \"80%\" \"80%\" \"80%\" \"80%\" ... ## ..$ mean_ae_site_med75: num [1:6] 3 3.35 8.5 8.06 7.56 ... ## $ df_sim_sites : tibble [6 × 9] (S3: tbl_df/tbl/data.frame) ## ..$ study_id : chr [1:6] \"A\" \"A\" \"A\" \"A\" ... ## ..$ site_number : chr [1:6] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 ## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, \"names\")= chr [1:6] \"80%\" \"80%\" \"80%\" \"80%\" ... ## ..$ mean_ae_site_med75 : num [1:6] 3 3.35 8.5 8.06 7.56 ... ## ..$ mean_ae_study_med75 : num [1:6] 6.71 6.02 6.51 6.26 6.35 ... ## ..$ n_pat_with_med75_study: int [1:6] 87 92 74 84 83 84 ## ..$ prob_low : num [1:6] 0 0 1 1 1 1 ## $ df_eval : tibble [6 × 11] (S3: tbl_df/tbl/data.frame) ## ..$ study_id : chr [1:6] \"A\" \"A\" \"A\" \"A\" ... ## ..$ site_number : chr [1:6] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... ## ..$ n_pat : int [1:6] 20 20 20 20 20 20 ## ..$ n_pat_with_med75 : num [1:6] 17 17 16 17 18 20 ## ..$ visit_med75 : Named num [1:6] 15 14 17 16 16 15 ## .. ..- attr(*, \"names\")= chr [1:6] \"80%\" \"80%\" \"80%\" \"80%\" ... ## ..$ mean_ae_site_med75 : num [1:6] 3 3.35 8.5 8.06 7.56 ... ## ..$ mean_ae_study_med75 : num [1:6] 6.71 6.02 6.51 6.26 6.35 ... ## ..$ n_pat_with_med75_study: int [1:6] 87 92 74 84 83 84 ## ..$ prob_low : num [1:6] 0 0 1 1 1 1 ## ..$ prob_low_adj : num [1:6] 0 0 1 1 1 1 ## ..$ prob_low_prob_ur : num [1:6] 1 1 0 0 0 0 ## $ r : num 1000 ## $ visit_med75 : logi TRUE ## $ inframe : logi FALSE ## $ under_only : logi TRUE ## $ param_site_aggr :List of 2 ## ..$ method : chr \"med75_adj\" ## ..$ min_pat_pool: num 0.2 ## $ param_sim_sites :List of 4 ## ..$ r : num 1000 ## ..$ poisson_test: logi FALSE ## ..$ prob_lower : logi TRUE ## ..$ under_only : logi TRUE ## $ param_eval_sites:List of 2 ## ..$ method : chr \"BH\" ## ..$ under_only: logi TRUE ## - attr(*, \"class\")= chr \"simaerep\" plot(aerep) ## study = NULL, defaulting to study:A"},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Over-Reporting Probability","text":"","code":"suppressPackageStartupMessages(library(dplyr)) suppressPackageStartupMessages(library(tidyr)) suppressPackageStartupMessages(library(simaerep)) suppressPackageStartupMessages(library(ggExtra))"},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Over-Reporting Probability","text":"{siamerep} originally created detect -reporting AEs therefore -reporting probability calculated. Nevertheless {simaerep} can theoretically used simulate kinds subject-based clinical events, issues -reporting can represent quality issue. recent release 0.5.0 added option calculate -reporting probability score.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"data-set","dir":"Articles","previous_headings":"","what":"Data Set","title":"Over-Reporting Probability","text":"simulate standard data set high number sites, patients, visits events ensure dimensions normally distributed. add - -reporting sites point.","code":"set.seed(1) df_visit <- sim_test_data_study( n_pat = 10000, n_sites = 1000, frac_site_with_ur = 0, max_visit_mean = 100, max_visit_sd = 1, ae_per_visit_mean = 5 ) df_visit$study_id <- \"A\""},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"run-simaerep","dir":"Articles","previous_headings":"","what":"Run {simaerep}","title":"Over-Reporting Probability","text":"order add -reporting probability score need set parameter under_only = FALSE. original setting skips simulation sites AEs study average. new parameter calculates probability site getting lower equal average AE count site visit_med75 every site, regardless initial value compares study average. calculation takes seconds longer default setting. evaluation data frame three columns available now.","code":"system.time( aerep_def <- simaerep(df_visit, under_only = TRUE) ) ## user system elapsed ## 19.149 1.116 20.268 system.time( aerep_ovr <- simaerep(df_visit, under_only = FALSE) ) ## user system elapsed ## 23.329 1.100 24.481 setdiff(colnames(aerep_ovr$df_eval), colnames(aerep_def$df_eval)) ## [1] \"prob_high\" \"prob_high_adj\" \"prob_high_prob_or\""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"probability-getting-a-lower-ae-count","dir":"Articles","previous_headings":"Analyze","what":"Probability getting a lower AE count","title":"Over-Reporting Probability","text":"can see gap default setting generated probabilities. values filling gap can interpreted probability higher site average originally observed.","code":"cols <- c(\"study_id\", \"site_number\", \"mean_ae_site_med75\", \"mean_ae_study_med75\", \"prob_low\") p <- bind_rows( select( aerep_ovr$df_eval, all_of(cols) ) %>% mutate(type = \"with over-reporting\"), select( aerep_def$df_eval, all_of(cols) ) %>% mutate(type = \"default\") ) %>% ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = prob_low, color = type)) + geom_point(alpha = 0.5) + theme(legend.position = \"bottom\") + scale_color_manual(values = c(\"gold\", \"blue\")) + labs(y = \"Probability of getting a lower or equal mean site AE in a 1000x simulation\") ggExtra::ggMarginal(p, groupColour = TRUE, type = \"density\")"},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"over-reporting","dir":"Articles","previous_headings":"Analyze","what":"Over-Reporting","title":"Over-Reporting Probability","text":"can add -reporting probability (1- -reporting probability), cases mean_ae_site_med75 equal mean_ae_study_med75 -reporting probability always zero.","code":"cols <- c(\"study_id\", \"site_number\", \"mean_ae_site_med75\", \"mean_ae_study_med75\") p <- bind_rows( select( aerep_ovr$df_eval, all_of(cols), value = \"prob_low\" ) %>% mutate(type = \"new under-reporting\"), select( aerep_ovr$df_eval, all_of(cols), value = \"prob_high\" ) %>% mutate(type = \"new over-reporting\"), select( aerep_def$df_eval, all_of(cols), value = \"prob_low\" ) %>% mutate(type = \"default under-reporting\") ) %>% ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + geom_point(alpha = 0.25) + theme(legend.position = \"bottom\") + scale_color_manual(values = c(\"gold\", \"purple\", \"blue\")) + labs(y = \"Probability of getting a lower or equal mean site AE in a 1000x simulation\") ggExtra::ggMarginal(p, groupColour = TRUE, type = \"density\")"},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"multiplicity-correction","dir":"Articles","previous_headings":"Analyze","what":"Multiplicity Correction","title":"Over-Reporting Probability","text":"multiplicity correction dampens signal, avoiding false positives result chance.","code":"cols <- c(\"study_id\", \"site_number\", \"mean_ae_site_med75\", \"mean_ae_study_med75\") p <- bind_rows( select( aerep_ovr$df_eval, all_of(cols), value = \"prob_low_prob_ur\" ) %>% mutate(type = \"new under-reporting\"), select( aerep_ovr$df_eval, all_of(cols), value = \"prob_high_prob_or\" ) %>% mutate(type = \"new over-reporting\"), select( aerep_def$df_eval, all_of(cols), value = \"prob_low_prob_ur\" ) %>% mutate(type = \"default under-reporting\") ) %>% ggplot(aes(x = mean_ae_site_med75 - mean_ae_study_med75, y = value, color = type)) + geom_point(alpha = 0.25) + theme(legend.position = \"bottom\") + scale_color_manual(values = c(\"gold\", \"purple\", \"blue\")) + labs(y = \"Probability of getting a lower or equal mean site AE in a 1000x simulation\") ggExtra::ggMarginal(p, groupColour = TRUE, type = \"density\")"},{"path":"https://openpharma.github.io/simaerep/articles/over.html","id":"simulating-over-reporting","dir":"Articles","previous_headings":"","what":"Simulating Over-Reporting","title":"Over-Reporting Probability","text":"can simulate -reporting supplying negative ratio ur_rate can plot -reporting changing setting prob_col = \"prob_high_prob_or\".","code":"set.seed(1) df_visit <- sim_test_data_study( frac_site_with_ur = 0.05, ur_rate = - 0.5, ) df_visit$study_id <- \"A\" distinct(df_visit, site_number, is_ur, ae_per_visit_mean) ## # A tibble: 20 × 3 ## site_number is_ur ae_per_visit_mean ## ## 1 S0001 TRUE 0.75 ## 2 S0002 FALSE 0.5 ## 3 S0003 FALSE 0.5 ## 4 S0004 FALSE 0.5 ## 5 S0005 FALSE 0.5 ## 6 S0006 FALSE 0.5 ## 7 S0007 FALSE 0.5 ## 8 S0008 FALSE 0.5 ## 9 S0009 FALSE 0.5 ## 10 S0010 FALSE 0.5 ## 11 S0011 FALSE 0.5 ## 12 S0012 FALSE 0.5 ## 13 S0013 FALSE 0.5 ## 14 S0014 FALSE 0.5 ## 15 S0015 FALSE 0.5 ## 16 S0016 FALSE 0.5 ## 17 S0017 FALSE 0.5 ## 18 S0018 FALSE 0.5 ## 19 S0019 FALSE 0.5 ## 20 S0020 FALSE 0.5 aerep <- simaerep(df_visit, under_only = FALSE) aerep$df_eval %>% select(site_number, mean_ae_site_med75, mean_ae_study_med75, prob_low_prob_ur, prob_high_prob_or) ## # A tibble: 20 × 5 ## site_number mean_ae_site_med75 mean_ae_study_med75 prob_low_prob_ur ## ## 1 S0001 11.7 7.48 0 ## 2 S0002 7.72 8.27 0.58 ## 3 S0003 6.93 7.72 0.687 ## 4 S0004 7.40 7.69 0.364 ## 5 S0005 7.98 7.66 0.118 ## 6 S0006 8.67 8.22 0.118 ## 7 S0007 7.58 7.68 0.276 ## 8 S0008 6.85 7.72 0.687 ## 9 S0009 7.78 7.67 0.118 ## 10 S0010 7.59 7.68 0.248 ## 11 S0011 7.17 7.71 0.587 ## 12 S0012 7.9 7.67 0.118 ## 13 S0013 7.6 7.19 0.118 ## 14 S0014 7.05 7.71 0.644 ## 15 S0015 7.98 7.66 0.118 ## 16 S0016 6.96 7.72 0.687 ## 17 S0017 7.36 7.69 0.445 ## 18 S0018 7.91 7.67 0.118 ## 19 S0019 7.46 7.69 0.364 ## 20 S0020 7.09 7.71 0.644 ## # ℹ 1 more variable: prob_high_prob_or plot(aerep, prob_col = \"prob_high_prob_or\") ## study = NULL, defaulting to study:A plot(aerep, prob_col = \"prob_low_prob_ur\") # Default ## study = NULL, defaulting to study:A"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Statistical Performance","text":"","code":"suppressPackageStartupMessages(library(dplyr)) suppressPackageStartupMessages(library(tibble)) suppressPackageStartupMessages(library(tidyr)) suppressPackageStartupMessages(library(simaerep)) suppressPackageStartupMessages(library(purrr)) suppressPackageStartupMessages(library(stringr))"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"performance-statistical","dir":"Articles","previous_headings":"","what":"Performance (Statistical)","title":"Statistical Performance","text":"","code":"suppressPackageStartupMessages(library(furrr)) suppressPackageStartupMessages(library(future)) plan(multisession, workers = 6)"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"load-portfolio","dir":"Articles","previous_headings":"Performance (Statistical)","what":"Load Portfolio","title":"Statistical Performance","text":"","code":"df_config <- readr::read_csv(\"ae_conf_20240220.csv\") df_ae_rates <- readr::read_csv(\"ae_rates_20240220.csv\") df_portf_flex <- sim_test_data_portfolio(df_config, df_ae_rates = df_ae_rates, parallel = TRUE, progress = TRUE) df_portf_flex %>% readr::write_csv(\"portf.csv\") df_portf_flex <- readr::read_csv(\"portf.csv\")"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"simulating-under-reporting","dir":"Articles","previous_headings":"Performance (Statistical)","what":"Simulating Under Reporting","title":"Statistical Performance","text":"reanalyze performance {simaerep} integrating learnings previous versions. apply following. generate portfolio using flexible AE rates remove AEs directly data set aggregated metric using sim_ur() set threshold confusion matrix methods similar fpr default algorithm default algorithm active -reporting scoring inframe algorithm visit_med75 inframe algorithm box-plot funnel-plot","code":""},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"functions","dir":"Articles","previous_headings":"Performance (Statistical)","what":"Functions","title":"Statistical Performance","text":"","code":"funnel <- function(df) { df %>% filter(visit == max(visit), .by = patnum) %>% summarise( Metric = sum(.data$n_ae) / sum(.data$visit), n_ae = sum(n_ae), visit = sum(visit), .by = \"site_number\" ) %>% mutate( vMu = sum(.data$n_ae) / sum(.data$visit), z_0 = ifelse(.data$vMu == 0, 0, (.data$Metric - .data$vMu) / sqrt(.data$vMu / .data$visit) ), phi = mean(.data$z_0^2), z_i = ifelse(.data$vMu == 0 | .data$phi == 0, 0, (.data$Metric - .data$vMu) / sqrt(.data$phi * .data$vMu / .data$visit) ) ) } box <- function(df) { df <- df %>% filter(visit == max(visit), .by = patnum) %>% summarise( event_per_visit = sum(.data$n_ae) / sum(.data$visit), .by = \"site_number\" ) bx <- boxplot.stats(df$event_per_visit) df <- df %>% mutate( box_out = event_per_visit < bx$stats[1] ) } perf <- function(df_visit, study_id, site_number, ur_rate) { df_vs_study <- df_visit %>% sim_ur(study_id, site_number, ur_rate) df_visit_med75 <- df_vs_study %>% simaerep(under_only = TRUE, progress = FALSE, check = FALSE) %>% .$df_eval %>% filter(.data$site_number == .env$site_number) df_visit_med75_over <- df_vs_study %>% simaerep(under_only = FALSE, progress = FALSE, check = FALSE) %>% .$df_eval %>% filter(.data$site_number == .env$site_number) df_inframe <- df_vs_study %>% simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = FALSE) %>% .$df_eval %>% filter(.data$site_number == .env$site_number) df_inframe_visit_med75 <- df_vs_study %>% simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = TRUE) %>% .$df_eval %>% filter(.data$site_number == .env$site_number) funnel_zi <- funnel(df_vs_study) %>% filter(.data$site_number == .env$site_number) %>% pull(z_i) box_out <- box(df_vs_study) %>% filter(.data$site_number == .env$site_number) %>% pull(box_out) tibble( score_visit_med75 = df_visit_med75$prob_low_prob_ur, score_visit_med75_no_mult = 1 - df_visit_med75$prob_low, score_visit_med75_over = df_visit_med75_over$prob_low_prob_ur, score_visit_med75_over_no_mult = 1 - df_visit_med75_over$prob_low, score_inframe = df_inframe$prob_low_prob_ur, score_inframe_no_mult = 1 - df_inframe$prob_low, score_inframe_visit_med75 = df_inframe_visit_med75$prob_low_prob_ur, score_inframe_visit_med75_no_mult = 1 - df_inframe_visit_med75$prob_low, score_funnel_zi = funnel_zi, score_box_out = as.integer(box_out), stat_visit_med75_visit_med75 = df_visit_med75$visit_med75, stat_visit_med75_n_pat_with_med75 = df_visit_med75$n_pat_with_med75, stat_visit_med75_mean_ae_site_med75 = df_visit_med75$mean_ae_site_med75, stat_visit_med75_mean_ae_study_med75 = df_visit_med75$mean_ae_study_med75, stat_inframe_visit_med75 = df_inframe_visit_med75$visit_med75, stat_inframe_visit_med75_n_pat_with_med75 = df_inframe_visit_med75$n_pat_with_med75, stat_inframe_visit_med75_events_per_visit_site = df_inframe_visit_med75$events_per_visit_site, stat_inframe_visit_med75_events_per_visit_study = df_inframe_visit_med75$events_per_visit_study, stat_inframe_n_pat = df_inframe$n_pat, stat_inframe_events_per_visit_site = df_inframe$events_per_visit_site, stat_inframe_events_per_visit_study = df_inframe$events_per_visit_study, ) } # perf(df_portf_flex, study_id = \"0010\", site_number = \"15153\", ur_rate = 0) %>% unlist() # perf(df_portf_flex, study_id = \"0010\", site_number = \"15153\", ur_rate = 0.5) %>% unlist() # perf(df_portf_flex, study_id = \"0010\", site_number = \"15153\", ur_rate = 0.75) %>% unlist() # perf(df_portf_flex, study_id = \"0010\", site_number = \"15153\", ur_rate = 1) %>% unlist()"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"grid","dir":"Articles","previous_headings":"Performance (Statistical)","what":"Grid","title":"Statistical Performance","text":"","code":"df_grid <- df_portf_flex %>% distinct(study_id, site_number) %>% # to reduce calculation time we only take every xth study filter(dense_rank(study_id)%%5 == 0) %>% mutate(ur_rate = list(c(0, 0.1, 0.25, 0.5, 0.75, 1))) %>% unnest(ur_rate) df_grid ## # A tibble: 27,120 × 3 ## study_id site_number ur_rate ## ## 1 0005 4480 0 ## 2 0005 4480 0.1 ## 3 0005 4480 0.25 ## 4 0005 4480 0.5 ## 5 0005 4480 0.75 ## 6 0005 4480 1 ## 7 0005 4481 0 ## 8 0005 4481 0.1 ## 9 0005 4481 0.25 ## 10 0005 4481 0.5 ## # ℹ 27,110 more rows"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"apply","dir":"Articles","previous_headings":"Performance (Statistical)","what":"Apply","title":"Statistical Performance","text":"","code":"with_progress_cnd( df_perf <- df_grid %>% mutate( perf = purrr_bar( list(study_id, site_number, ur_rate), .purrr = furrr::future_pmap, .f = function(x, y, z) perf(df_portf_flex, x, y, z), .purrr_args = list(.options = furrr_options(seed = TRUE)), .steps = nrow(.) ) ) ) df_perf %>% unnest(perf) %>% readr::write_csv(\"perf.csv\") df_perf <- readr::read_csv(\"perf.csv\", show_col_types = FALSE) df_perf_long <- df_perf %>% pivot_longer(cols = - c(study_id, site_number, ur_rate), names_to = \"type\", values_to = \"score\") %>% filter(startsWith(type, \"score_\")) %>% mutate(type = stringr::str_replace(type, \"score_\", \"\"))"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"thresholds","dir":"Articles","previous_headings":"Evaluation","what":"Thresholds","title":"Statistical Performance","text":"set thresholds get fpr 0.01. Note results probability thresholds ~ 0.99 scores w/o multiplicity correction recommended funne plot score threshold -2.","code":"target_fpr <- 0.01 df_thresh <- df_perf_long %>% group_by(type) %>% nest() %>% ungroup() %>% mutate( data = map(data, ~ filter(., ur_rate == 0)), thresh1 = map_dbl(data, ~ quantile(pull(., score), 1 - target_fpr)), thresh2 = map_dbl(data, ~ quantile(pull(., score), target_fpr)), thresh = ifelse(type == \"funnel_zi\", thresh2, thresh1) ) %>% select(type, thresh) df_thresh ## # A tibble: 10 × 2 ## type thresh ## ## 1 visit_med75 0.455 ## 2 visit_med75_no_mult 0.989 ## 3 visit_med75_over 0.455 ## 4 visit_med75_over_no_mult 0.989 ## 5 inframe 0.384 ## 6 inframe_no_mult 0.982 ## 7 inframe_visit_med75 0.412 ## 8 inframe_visit_med75_no_mult 0.987 ## 9 funnel_zi -2.03 ## 10 box_out 1"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"aggregate","dir":"Articles","previous_headings":"Evaluation","what":"Aggregate","title":"Statistical Performance","text":"","code":"get_prop_test_ci95 <- function(..., ix) { stopifnot(ix %in% c(1, 2)) tryCatch( prop.test(...)$conf.int[ix], error = function(cnd) c(NA, NA)[ix] ) } df_aggr <- df_perf_long %>% left_join(df_thresh, by = \"type\") %>% mutate( is_ur = ifelse(type == \"funnel_zi\", score <= thresh, score >= thresh), is_ur = ifelse(type == \"box_out\", score == 1, is_ur) ) %>% summarise( n = n(), .by = c(type, ur_rate, is_ur) ) %>% pivot_wider( names_from = is_ur, values_from = n, names_prefix = \"is_ur_\", values_fill = 0 ) %>% mutate( n_sites = is_ur_TRUE + is_ur_FALSE, ratio = is_ur_TRUE / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)), ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)), type_strip = str_replace(type, \"_no_mult\", \"\"), has_mult = ! str_detect(type, \"no_mult\") & ! type %in% c(\"funnel_zi\", \"box_out\") )"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"table","dir":"Articles","previous_headings":"Evaluation","what":"Table","title":"Statistical Performance","text":"Methods: visit_med75: default algorithm visit_med75_over: default algorithm including -reporting score inframe: new algorithm using table operations visit_med75 inframe visit_med75: new aldorithm using table operations visit_med75 funnel_zi: funnel plot derived outlier detection box_out: box plot derived outlier detection FN: false negatives TP: true positives","code":"df_aggr %>% select(method = type_strip, has_mult, ur_rate, FN = is_ur_FALSE, TP = is_ur_TRUE, n_sites, ratio_type, ratio, ci95_low, ci95_high) %>% knitr::kable(digits = 4)"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"plot","dir":"Articles","previous_headings":"Evaluation","what":"Plot","title":"Statistical Performance","text":"","code":"df_aggr %>% mutate(ur_rate = paste0(\"under-reporting rate: \", ur_rate, \" - \", ratio_type) ) %>% group_by(ur_rate) %>% ggplot(aes(type, ratio)) + geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type_strip, alpha = has_mult), linewidth = 1) + facet_wrap(~ ur_rate, ncol = 1) + coord_flip() + theme( legend.position = \"right\", axis.text.y = element_blank(), axis.ticks.y = element_blank() ) + labs( x = \"\", y = \"Ratio (CI95)\", title = \"{simaerep} Performance\", color = \"Method\", alpha = \"Multiplicity Correction\" ) + scale_color_manual(values = rev(RColorBrewer::brewer.pal(n = 6, name = \"Dark2\"))) + scale_alpha_manual(values = c(1, 0.5))"},{"path":"https://openpharma.github.io/simaerep/articles/performance.html","id":"summary","dir":"Articles","previous_headings":"Evaluation","what":"Summary","title":"Statistical Performance","text":"new inframe method slightly better performance original algorithm new inframe methods compares event per visit rates without dropping patients AEs analysis. likely explain performance increase. observed similar increase optimized visit_med75 past experiments. multiplicity correction imposes penalty true positive rate observation already made Boeringer Ingelheim Team evaluation {simaerep}. can now reporducibly confirm . unaltered probability score returned bootstrap algorithm already provides realistic -reporting probabilities. {simaerep} outperforms simpler methods funnel plot box plot outlier detection. controly confirm previous observations made {simaerep} validation.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"(superseeded) simaerep Portfolio Performance","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(furrr)) suppressPackageStartupMessages(library(future)) suppressPackageStartupMessages(library(simaerep)) # RAM ~26 GB # plan 4GB per core plan(multisession, workers = 6)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"(superseeded) simaerep Portfolio Performance","text":"want define minimal requirements simulating test data reflects realistic portfolio data want use benchmark overall {simaerep} performance.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"performance","dir":"Articles","previous_headings":"","what":"Performance","title":"(superseeded) simaerep Portfolio Performance","text":"simulations take time run require multiple cores appropriate memory. Rendering articles {pkgdown} can bit unstable recommend render first using pure {rmarkdown} generate intermediate csv files.","code":"rmarkdown::render(\"vignettes/_portfolio_perf.Rmd\", knit_root_dir = paste0(getwd(), \"/vignettes\"))"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"portfolio-configuration","dir":"Articles","previous_headings":"","what":"Portfolio Configuration","title":"(superseeded) simaerep Portfolio Performance","text":"portfolio configuration used generate compliant test data similar realistic portfolio studies sites compliant. subsequently remove percentage AEs study site calculate AE -reporting statistics calculate overall detection thresholds. portfolio configuration give minimal description portfolio without violating data privacy laws competitive intellectual property. propose include following metrics portfolio configuration: site parameters: mean maximum patient visits sd maximum patient visits total number patients study parameters: mean AE per visit information contained portfolio configuration scarce thus can shared easily within industry. can use parameters simulate test data assessing {simaerep} performance given portfolio. can start maximum aggregation visit n_ae patient level starting df_visit use simaerep::site_aggr(). can use simaerep::get_config generate valid portfolio configuration, automatically apply filters: remove patients 0 visits minimum number patients per study minimum number sites per study anonymize study site IDs","code":"df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- bind_rows(df_visit1, df_visit2) df_site_max <- df_visit %>% group_by(study_id, site_number, patnum) %>% summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config <- simaerep::get_config( df_site_max, anonymize = TRUE, min_pat_per_study = 100, min_sites_per_study = 10 ) df_config %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"simulate-portfolio-from-configuration","dir":"Articles","previous_headings":"Portfolio Configuration","what":"Simulate Portfolio from Configuration","title":"(superseeded) simaerep Portfolio Performance","text":"can now apply sim_test_data_portfolio uses sim_test_data_study() generate artificial data visit level.","code":"df_portf <- sim_test_data_portfolio(df_config) df_portf %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"load-realistic-configuration","dir":"Articles","previous_headings":"Portfolio Configuration","what":"Load Realistic Configuration","title":"(superseeded) simaerep Portfolio Performance","text":"load realistic portfolio configuration.","code":"df_config <- readr::read_csv(\"ae_profile.csv\") df_config %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"simulate-portfolio","dir":"Articles","previous_headings":"","what":"Simulate Portfolio","title":"(superseeded) simaerep Portfolio Performance","text":"simulate artificial visit level data. Using parallel processing. stage simulated compliant test data realistic study portfolio.","code":"df_portf <- sim_test_data_portfolio(df_config, parallel = TRUE, progress = TRUE) df_portf %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"confirm-that-portfolio-simulation-results-in-similar-configuration","dir":"Articles","previous_headings":"Simulate Portfolio","what":"Confirm that Portfolio Simulation results in Similar Configuration","title":"(superseeded) simaerep Portfolio Performance","text":"now use simulated portfolio data extract configuration. expect configuration extracting similar configuration started first place. portfolio simulation sample patient maximum visit values normal distribution. returns values smaller 1 replace one. larger SD values compared mean values likely sample patient maximum visit smaller one. Every time correction lowering patient maximum visit SD simulation, can see graph .","code":"df_site_max_portf <- df_portf %>% group_by(study_id, site_number, patnum) %>% summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config_portf <- simaerep::get_config(df_site_max_portf, anonymize = TRUE, min_pat_per_study = 100, min_sites_per_study = 10) df_comp <- df_config %>% left_join( df_config_portf, by = c(\"study_id\", \"site_number\"), suffix = c(\".ori\", \".sim\") ) %>% select( study_id, starts_with(\"ae\"), site_number, contains(\"max_visit_sd\"), contains(\"max_visit_mean\"), contains(\"n_pat\") ) df_comp %>% select(study_id, starts_with(\"ae\")) %>% distinct() %>% ggplot(aes(ae_per_visit_mean.ori, ae_per_visit_mean.sim)) + geom_point() + geom_smooth() + labs(title = \"simulated vs original AE per visit study mean\") + theme(aspect.ratio = 1) df_comp %>% ggplot(aes(max_visit_sd.ori, max_visit_sd.sim)) + geom_point() + geom_smooth() + geom_abline(slope = 1, color = \"red\") + labs(title = \"simulated vs original max visit sd site\") + theme(aspect.ratio = 1) df_comp %>% ggplot(aes(max_visit_mean.ori, max_visit_mean.sim)) + geom_point() + geom_smooth() + geom_abline(slope = 1, color = \"red\") + labs(title = \"simulated vs original max visit mean site\") + theme(aspect.ratio = 1) df_comp %>% ggplot(aes(n_pat.ori, n_pat.sim)) + geom_point() + geom_smooth() + labs(title = \"simulated vs original n_pat site\") + theme(aspect.ratio = 1)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"get-under-reporting-probability-for-different-under-reporting-scenarios","dir":"Articles","previous_headings":"","what":"Get Under-Reporting Probability for Different Under Reporting Scenarios","title":"(superseeded) simaerep Portfolio Performance","text":"performance detecting AE -reporting dependent three things: higher mean AE per visit study level better higher number patients -reporting site better higher maximum visit number per patient better higher number -reporting sites study worse initial usability assessment fixed parameters. going leave portfolio. vanilla version artificial portfolio data contain -reporting sites yet. However simaerep::sim_ur_scenarios() apply -reporting scenarios site. Reducing number AEs given -reporting rate (ur_rate) patients site add corresponding -reporting statistics.","code":"df_scen <- sim_ur_scenarios( df_portf, extra_ur_sites = 0, ur_rate = c(0.1, 0.25, 0.5, 0.75, 1), parallel = TRUE, poisson = TRUE, prob_lower = TRUE, progress = TRUE ) readr::write_csv(df_scen, file = \"scen.csv\") df_scen <- readr::read_csv(\"scen.csv\") df_scen %>% head(25) %>% knitr::kable()"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"portfolio-performance","dir":"Articles","previous_headings":"","what":"Portfolio Performance","title":"(superseeded) simaerep Portfolio Performance","text":"every site portfolio now generated AE -reporting probability following -reporting rates 0, 0.25, 0.5, 0.75 1: usually recommend 0.95 threshold flag sites -reporting. can use threshold calculate ratio flagged sites per -reporting rate. zero -reporting scenario defines expected false positive rates (fpr fp/N), scenarios give us expected true positive rate (tpr tp/P) sites -reporting level. condition tpr fpr rates simulated portfolio site -reporting site respective study.","code":"df_scen %>% select(study_id, site_number, ur_rate, prob_low_prob_ur) %>% head(25) %>% knitr::kable() df_perf_portf <- df_scen %>% mutate(is_ur = prob_low_prob_ur >= 0.95) %>% group_by(ur_rate, is_ur) %>% count() %>% pivot_wider( names_from = is_ur, values_from = n, names_prefix = \"is_ur_\" ) %>% mutate( n_sites = is_ur_TRUE + is_ur_FALSE, ratio = is_ur_TRUE / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = prop.test(is_ur_TRUE, n_sites)$conf.int[1], ci95_high = prop.test(is_ur_TRUE, n_sites)$conf.int[2] ) df_perf_portf %>% knitr::kable(digits = 3)"},{"path":[]},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"performance-under-optimal-conditions","dir":"Articles","previous_headings":"Benchmark {simaerep} Using Portfolio Performance","what":"Performance Under Optimal Conditions","title":"(superseeded) simaerep Portfolio Performance","text":"True positive rates sites -reporting rate 1 surprisingly small. expect true positive ratios close 100%. reason within portfolio sites just starting reported AEs yet. also studies overall low AE rates example studies healthy participants. Altogether allows uncompliant sites hide among compliant sites makes difficult detect . Therefore also like demonstrate {simaerep} performance can expected ideal conditions. generate studies following parameters: 200 patients 20 sites one -reporting site 0.5 AEs per visit average 20 visits per patient SD 2 simulate 500 studies -reporting scenario. Next apply {simaerep} calculate confusion matrices. calculate tpr fpr. ideal conditions sites 0.5 -reporting rate almost get flagged ratio 0.97 minimal ratio false positive flags < 0.003.","code":"standard_sim <- function(ur_rate, seed) { set.seed(seed) df <- sim_test_data_study( n_pat = 200, n_sites = 20, frac_site_with_ur = 0.05, ur_rate = ur_rate, ae_per_visit_mean = 0.5, max_visit_mean = 20, max_visit_sd = 2 ) if(ur_rate == 0) { df$is_ur <- FALSE } return(df) } df_data_grid <- tibble( ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1), seed = list(seq(1, 500)) ) %>% unnest(seed) %>% mutate( data = map2(ur_rate, seed, standard_sim) ) df_data_grid ## # A tibble: 3,000 × 3 ## ur_rate seed data ## ## 1 0 1 ## 2 0 2 ## 3 0 3 ## 4 0 4 ## 5 0 5 ## 6 0 6 ## 7 0 7 ## 8 0 8 ## 9 0 9 ## 10 0 10 ## # ℹ 2,990 more rows df_visit <- df_data_grid %>% mutate( study_id = paste0( \"study-\", str_pad(ur_rate, width= 3, side = \"left\", pad = \"0\"), \"-\", seed ) ) %>% unnest(data) df_visit %>% head(25) %>% knitr::kable() df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit) df_eval <- eval_sites(df_sim_sites) df_perf <- df_visit %>% select(ur_rate, study_id, site_number, is_ur) %>% distinct() %>% left_join(df_eval, by = c(\"study_id\", \"site_number\")) %>% mutate(is_ur_detected = prob_low_prob_ur >= 0.95) %>% group_by(ur_rate, is_ur, is_ur_detected) %>% count() readr::write_csv(df_perf, file = \"scen_st.csv\") remove(df_visit) # free up RAM df_perf <- readr::read_csv(file = \"scen_st.csv\") df_perf %>% knitr::kable() get_prop_test_ci95 <- function(..., ix) { stopifnot(ix %in% c(1, 2)) tryCatch( prop.test(...)$conf.int[ix], error = function(cnd) c(NA, NA)[ix] ) } df_perf_st <- df_perf %>% group_by(ur_rate) %>% summarize( N = sum(ifelse(! is_ur, n, 0)), P = sum(ifelse(is_ur, n, 0)), TP = sum(ifelse(is_ur & is_ur_detected, n, 0)), FP = sum(ifelse(! is_ur & is_ur_detected, n, 0)), TN = sum(ifelse(! is_ur & ! is_ur_detected, n, 0)), FN = sum(ifelse(is_ur & ! is_ur_detected, n, 0)) ) %>% mutate( tpr = TP / P, tpr_ci95_low = map2_dbl(TP, P, get_prop_test_ci95, ix = 1), tpr_ci95_high = map2_dbl(TP, P, get_prop_test_ci95, ix = 2), fpr = FP / N, fpr_ci95_low = map2_dbl(FP, N, get_prop_test_ci95, ix = 1), fpr_ci95_high = map2_dbl(FP, N, get_prop_test_ci95, ix = 2) ) df_perf_st %>% knitr::kable(digit = 4)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"effect-of-adjusting-visit_med75","dir":"Articles","previous_headings":"Benchmark {simaerep} Using Portfolio Performance","what":"Effect of Adjusting visit_med75","title":"(superseeded) simaerep Portfolio Performance","text":"One latest update simaerep improvement visit_med75 calculation. can check affected portfolio performance. find likely slightly increased performance.","code":"df_scen_old_visit_med75 <- sim_ur_scenarios( df_portf, extra_ur_sites = 5, ur_rate = c(0.1, 0.25, 0.5, 0.75, 1), parallel = TRUE, poisson = TRUE, prob_lower = TRUE, progress = TRUE, site_aggr_args = list(method = \"med75\") # default is \"med75_adj\" ) readr::write_csv(df_scen_old_visit_med75, file = \"scen_old.csv\") df_scen_old_visit_med75 <- readr::read_csv(\"scen_old.csv\") df_scen_old_visit_med75 %>% head(25) %>% knitr::kable() df_perf_portf_old_visit_med75 <- df_scen_old_visit_med75 %>% mutate(is_ur = prob_low_prob_ur >= 0.95) %>% group_by(ur_rate, is_ur) %>% count() %>% pivot_wider( names_from = is_ur, values_from = n, names_prefix = \"is_ur_\" ) %>% mutate( n_sites = is_ur_TRUE + is_ur_FALSE, ratio = is_ur_TRUE / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = prop.test(is_ur_TRUE, n_sites)$conf.int[1], ci95_high = prop.test(is_ur_TRUE, n_sites)$conf.int[2] ) df_perf_portf_old_visit_med75 %>% knitr::kable(digits = 3)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"days-vs--visits","dir":"Articles","previous_headings":"Benchmark {simaerep} Using Portfolio Performance","what":"Days vs. Visits","title":"(superseeded) simaerep Portfolio Performance","text":"Instead normalising AEs per patient visit alternative normalise number days passed since patients enrollment. {simaerep} can used types normalisation demonstrated . normalising days bit complex creates data points. maximum number days per patient can several years, > 1000 days. simaerep exposes implicitly missing entries can lead single patients 1000 entries , one entry day study. order avoid generate huge portfolio data frame preserve memory wrapping sim_test_data_portfolio() sim_ur_scenarios() single call apply per study.","code":"wr <- function(df) { df_portf <- sim_test_data_portfolio(df, parallel = FALSE, progress = FALSE) df_scen <- sim_ur_scenarios(df_portf, extra_ur_sites = 5, ur_rate = c(0.1, 0.25, 0.5, 0.75, 1), parallel = FALSE, poisson = TRUE, prob_lower = TRUE, progress = FALSE) return(df_scen) } df_prep <- df_config %>% select(- max_visit_sd, - max_visit_mean, - ae_per_visit_mean) %>% rename(max_visit_sd = max_days_sd, max_visit_mean = max_days_mean, ae_per_visit_mean = ae_per_day_mean) %>% group_by(study_id_gr = study_id) %>% nest() %>% ungroup() progressr::with_progress( df_scen_days <- df_prep %>% mutate(data = purrr_bar( .data$data, .purrr = furrr::future_map, .f = wr, .progress = TRUE, .steps = nrow(.), .purrr_args = list(.options = furrr_options(seed = TRUE)) ) ) ) df_scen_days <- df_scen_days %>% unnest(data) %>% select(- study_id_gr) readr::write_csv(df_scen_days, file = \"scen_days.csv\") df_scen_days <- readr::read_csv(\"scen_days.csv\") df_scen_days %>% head(25) %>% knitr::kable() df_perf_portf_days <- df_scen_days %>% mutate(is_ur = prob_low_prob_ur >= 0.95) %>% group_by(ur_rate, is_ur) %>% count() %>% pivot_wider( names_from = is_ur, values_from = n, names_prefix = \"is_ur_\" ) %>% mutate( n_sites = is_ur_TRUE + is_ur_FALSE, ratio = is_ur_TRUE / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = prop.test(is_ur_TRUE, n_sites)$conf.int[1], ci95_high = prop.test(is_ur_TRUE, n_sites)$conf.int[2] ) df_perf_portf_days %>% knitr::kable(digits = 3)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"heuristic-rank","dir":"Articles","previous_headings":"Benchmark {simaerep} Using Portfolio Performance","what":"Heuristic Rank","title":"(superseeded) simaerep Portfolio Performance","text":"Instead using {simaerep} can also use heuristic method based AE per visit apply simulated portfolio different scenarios -reporting Flag 5% (always round ) sites study lowest AE per visit rate. Always flag sites AEs. write function : - determines many sites flagged study - pools ae_per_visit rates ranks sites (using dense_rank()) - site gets flagged specific rank site within number sites get flagged Next wrap function another function simulates -reporting apply. see method generously flagging sites resulting good tpr cost high fpr. default least one site per study gets flagged.","code":"df_ae_per_vs <-df_portf %>% group_by(study_id, site_number, patnum) %>% filter(visit == max(visit)) %>% group_by(study_id, site_number) %>% summarise(visit = sum(visit), n_ae = sum(n_ae), .groups = \"drop\") %>% mutate(ae_per_visit = n_ae / visit) %>% group_by(study_id) %>% mutate( ls_study_ae_per_visit = list(ae_per_visit), rwn = row_number(), # we take out site ae_per_visit from study pool ls_study_ae_per_visit = map2( ls_study_ae_per_visit, rwn, function(ls, rwn) ls[- rwn] ) ) %>% select(- rwn) %>% ungroup() df_ae_per_vs ## # A tibble: 15,674 × 6 ## study_id site_number visit n_ae ae_per_visit ls_study_ae_per_visit ## ## 1 0001 0001 44 13 0.295 ## 2 0001 0002 53 15 0.283 ## 3 0001 0003 74 19 0.257 ## 4 0001 0004 215 57 0.265 ## 5 0001 0005 30 9 0.3 ## 6 0001 0006 35 11 0.314 ## 7 0001 0007 36 14 0.389 ## 8 0001 0008 172 37 0.215 ## 9 0001 0009 40 19 0.475 ## 10 0001 0010 137 32 0.234 ## # ℹ 15,664 more rows flag_heuristics_rnk <- function(ae_per_visit, ls_study_ae_per_visit) { n_flags <- ceiling(length(ls_study_ae_per_visit + 1) * 0.05) rnk <- tibble(ae_per_visit = ae_per_visit, site = \"site\") %>% bind_rows( tibble(ae_per_visit = ls_study_ae_per_visit, site = \"other\") ) %>% # using dense_rank will assign rank 1 to all sites with lowest rate # this is important as there can be many sites with a zero ratio # occasionally this will flag more sites than anticipated arrange(ae_per_visit, site) %>% mutate(rnk = dense_rank(ae_per_visit)) %>% filter(site == \"site\") %>% pull(rnk) return(rnk <= n_flags) } flag_heuristics_rnk( df_ae_per_vs$ae_per_visit[[1]], df_ae_per_vs$ls_study_ae_per_visit[[1]] ) ## [1] FALSE sim_heuristic_ur <- function(ae_per_visit, ls_study_ae_per_visit, ur_rates, .f = flag_heuristics_rnk) { tibble( ur_rate = ur_rates, ae_per_visit = ae_per_visit ) %>% mutate( ae_per_visit = ae_per_visit * (1 - ur_rate), is_ur = map_lgl(ae_per_visit, .f, ls_study_ae_per_visit) ) } sim_heuristic_ur( df_ae_per_vs$ae_per_visit[[1]], df_ae_per_vs$ls_study_ae_per_visit[[1]], ur_rates = c(0, 0.1, 0.25, 0.5, 0.75, 1) ) ## # A tibble: 6 × 3 ## ur_rate ae_per_visit is_ur ## ## 1 0 0.295 FALSE ## 2 0.1 0.266 FALSE ## 3 0.25 0.222 FALSE ## 4 0.5 0.148 TRUE ## 5 0.75 0.0739 TRUE ## 6 1 0 TRUE progressr::with_progress( df_perf_heuristic_rnk <- df_ae_per_vs %>% mutate( sim = simaerep::purrr_bar( ae_per_visit, ls_study_ae_per_visit, .purrr = furrr::future_map2, .f = sim_heuristic_ur, .f_args = list(ur_rates = c(0, 0.1, 0.25, 0.5, 0.75, 1)), .steps = nrow(.) ) ) ) df_perf_heuristic_rnk <- df_perf_heuristic_rnk %>% select(sim) %>% unnest(sim) %>% group_by(ur_rate) %>% summarise( is_ur = sum(is_ur), n_sites = n(), .groups = \"drop\" ) %>% mutate( ratio = is_ur / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = map2_dbl(is_ur, n_sites, get_prop_test_ci95, ix = 1), ci95_high = map2_dbl(is_ur, n_sites, get_prop_test_ci95, ix = 2), ) readr::write_csv(df_perf_heuristic_rnk, file = \"heuristic_rnk.csv\") df_perf_heuristic_rnk <- readr::read_csv(file = \"heuristic_rnk.csv\") df_perf_heuristic_rnk %>% knitr::kable(digits = 3)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"heuristic-box-plot-outlier","dir":"Articles","previous_headings":"Benchmark {simaerep} Using Portfolio Performance","what":"Heuristic Box Plot Outlier","title":"(superseeded) simaerep Portfolio Performance","text":"can also imagine heuristic tries detect lower boundary outliers basis ae per visit rate. flagging conservatively without flagging sites default. simple non-parametric method outlier detection calculate box plot statistic flag points lower whisker boundary. apply.","code":"flag_heuristics_box <- function(ae_per_visit, ls_study_ae_per_visit) { min_whisker <- min(boxplot.stats(c(ae_per_visit, ls_study_ae_per_visit))$stats) return(ae_per_visit < min_whisker) } flag_heuristics_box( df_ae_per_vs$ae_per_visit[[1]], df_ae_per_vs$ls_study_ae_per_visit[[1]] ) ## [1] FALSE flag_heuristics_box( 0, df_ae_per_vs$ls_study_ae_per_visit[[1]] ) ## [1] TRUE progressr::with_progress( df_perf_heuristic_box <- df_ae_per_vs %>% mutate( sim = simaerep::purrr_bar( ae_per_visit, ls_study_ae_per_visit, .purrr = furrr::future_map2, .f = sim_heuristic_ur, .f_args = list( ur_rates = c(0, 0.1, 0.25, 0.5, 0.75, 1), .f = flag_heuristics_box ), .steps = nrow(.) ) ) ) df_perf_heuristic_box <- df_perf_heuristic_box %>% select(sim) %>% unnest(sim) %>% group_by(ur_rate) %>% summarise( is_ur = sum(is_ur), n_sites = n(), .groups = \"drop\" ) %>% mutate( ratio = is_ur / n_sites, ratio_type = ifelse(ur_rate == 0, \"fpr\", \"tpr\"), ci95_low = map2_dbl(is_ur, n_sites, get_prop_test_ci95, ix = 1), ci95_high = map2_dbl(is_ur, n_sites, get_prop_test_ci95, ix = 2), ) readr::write_csv(df_perf_heuristic_box, file = \"heuristic_box.csv\") df_perf_heuristic_box <- readr::read_csv(file = \"heuristic_box.csv\") df_perf_heuristic_box %>% knitr::kable(digits = 3)"},{"path":"https://openpharma.github.io/simaerep/articles/portfolio_perf.html","id":"plot-performance-metrics","dir":"Articles","previous_headings":"","what":"Plot Performance Metrics","title":"(superseeded) simaerep Portfolio Performance","text":"{simaerep} reduces false positive rate compared heuristics. Rank-based heuristics higher true positive rates cost higher false positive rates. Similar effects achieved lowering {simaerep} flagging threshold Using AE per visits AE per patient days better performance. Adjusting visit_med75 also improved performance {simaerep} results closest boxplot outlier heuristic better overall performance optimal conditions {simaerep} catches almost -reporting sites -reporting rate greater 0.5","code":"prep_for_plot <- function(df, type) { df %>% mutate(ur_rate = paste0(\"under-reporting rate: \", ur_rate, \" - \", ratio_type), ur_rate = ifelse(str_detect(ur_rate, \"fpr\"), \"fpr\", ur_rate)) %>% select(ur_rate, ratio_type, ratio, ci95_low, ci95_high) %>% mutate(type = type) } df_perf <- df_perf_st %>% filter(ur_rate == 0) %>% mutate(ratio_type = \"fpr\") %>% select(ur_rate, ratio_type, ratio = fpr, ci95_low = fpr_ci95_low, ci95_high = fpr_ci95_high) %>% bind_rows( df_perf_st %>% filter(ur_rate > 0) %>% mutate(ratio_type = \"tpr\") %>% select(ur_rate, ratio_type, ratio = tpr, ci95_low = tpr_ci95_low, ci95_high = tpr_ci95_high) ) %>% prep_for_plot(type = \"{simaerep} optimal study conditions\") %>% bind_rows( prep_for_plot(df_perf_portf, type = \"{simaerep} default\"), prep_for_plot(df_perf_portf_days, type = \"{simaerep} AE per days on study\"), prep_for_plot(df_perf_portf_old_visit_med75, type = \"{simaerep} unadjusted visit-med75\"), prep_for_plot(df_perf_heuristic_rnk, type = \"heuristic - rank\"), prep_for_plot(df_perf_heuristic_box, type = \"heuristic - boxplot outlier\"), ) %>% mutate( type = fct_relevel(type, c( \"heuristic - boxplot outlier\", \"heuristic - rank\", \"{simaerep} default\", \"{simaerep} optimal study conditions\" ) ) ) df_perf %>% mutate(color = ifelse(type == \"{simaerep} default\", \"violetred2\", \"darkgrey\"), ref = ifelse(type == \"{simaerep} default\", ratio, 0)) %>% group_by(ur_rate) %>% mutate(ref = max(ref)) %>% ggplot(aes(type, ratio)) + geom_hline(aes(yintercept = ref), linetype = 2, color = \"violetred2\") + geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = color)) + facet_wrap(~ ur_rate, ncol = 1) + scale_colour_identity() + coord_flip() + labs( x = \"\", y = \"CI95 Performance Ratio\", title = \"{simaerep} Performance\", subtitle = \"Only one under-reporting site per study.\\nCut-Off Under-Reporting Probability: 0.95\" ) df_perf %>% arrange(ur_rate, desc(type)) %>% select(ur_rate, type, ratio, ci95_low, ci95_high) %>% knitr::kable(digits = 3) plan(sequential)"},{"path":"https://openpharma.github.io/simaerep/articles/sas_files.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"SAS Files as a Data Source","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(simaerep)) suppressPackageStartupMessages(library(haven))"},{"path":"https://openpharma.github.io/simaerep/articles/sas_files.html","id":"sas-files","dir":"Articles","previous_headings":"","what":"SAS files","title":"SAS Files as a Data Source","text":"Typically clinical data stored several SAS files standardized format. need files visits AEs recorded. demo selected anonymized data set contains patients enrolled control arm. data sets AE onset dates visit dates replaced number days passed since specific cut-date. can proceed similar way order assign AE visit union event tables sort date. patient example AE first visit AEs NA date aggregate visit number. patient example . control check whether numbers visits AEs processed data still matches number AEs original data.","code":"df_ae <- haven::read_sas('adae.sas7bdat') %>% select(STUDYID, SUBJID, SITEID, AESTDY) df_ae ## # A tibble: 4,702 × 4 ## STUDYID SUBJID SITEID AESTDY ## ## 1 CO-101-001 01001001 001 -14 ## 2 CO-101-001 01001001 001 -13 ## 3 CO-101-001 01001001 001 50 ## 4 CO-101-001 01001001 001 67 ## 5 CO-101-001 01001001 001 77 ## 6 CO-101-001 01001001 001 84 ## 7 CO-101-001 01001001 001 99 ## 8 CO-101-001 01001001 001 99 ## 9 CO-101-001 01001001 001 127 ## 10 CO-101-001 01001001 001 127 ## # ℹ 4,692 more rows df_vs <- haven::read_sas('advs.sas7bdat') %>% select(STUDYID, SUBJID, SITEID, ADY) df_vs ## # A tibble: 45,176 × 4 ## STUDYID SUBJID SITEID ADY ## ## 1 CO-101-001 01001001 001 -7 ## 2 CO-101-001 01001001 001 1 ## 3 CO-101-001 01001001 001 1 ## 4 CO-101-001 01001001 001 8 ## 5 CO-101-001 01001001 001 8 ## 6 CO-101-001 01001001 001 15 ## 7 CO-101-001 01001001 001 15 ## 8 CO-101-001 01001001 001 29 ## 9 CO-101-001 01001001 001 29 ## 10 CO-101-001 01001001 001 38 ## # ℹ 45,166 more rows df_ae <- df_ae %>% rename(DY = AESTDY) %>% mutate(EVENT = \"AE\") df_vs <- df_vs %>% rename(DY = ADY) %>% mutate(EVENT = \"VS\") %>% # we ignore visits that have no date filter(! is.na(DY)) %>% # we are not interested in same day visits distinct() df_aevs <- bind_rows(df_ae, df_vs) %>% # NA's get sorted towards the end thus AEs with no date get sorted towards last visit arrange(STUDYID, SITEID, SUBJID, DY) %>% group_by(STUDYID, SITEID, SUBJID) %>% mutate(AE_NO = cumsum(ifelse(EVENT == \"AE\", 1, 0)), VS_NO = cumsum(ifelse(EVENT == \"VS\", 1, 0))) %>% # we remove patients with 0 visits filter(max(VS_NO) > 0) %>% # AE's before fist visit should register to visit 1 not zero mutate(VS_NO = ifelse(VS_NO == 0, 1, VS_NO)) df_aevs %>% filter(SUBJID == \"01007004\") %>% knitr::kable() df_aevs_aggr <- df_aevs %>% group_by(STUDYID, SITEID, SUBJID, VS_NO) %>% summarise(MIN_AE_NO = min(AE_NO), MAX_AE_NO = max(AE_NO), .groups = \"drop\") %>% group_by(STUDYID, SITEID, SUBJID) %>% mutate(MAX_VS_PAT = max(VS_NO)) %>% ungroup() %>% # assign AEs that occur after last visit to last AE mutate( CUM_AE = ifelse( VS_NO == MAX_VS_PAT, MAX_AE_NO, MIN_AE_NO) ) df_aevs_aggr %>% filter(SUBJID == \"01007004\") %>% knitr::kable() stopifnot(nrow(df_aevs_aggr) == nrow(df_vs)) n_aes <- df_aevs_aggr %>% group_by(SUBJID) %>% summarize(n_aes = max(CUM_AE)) %>% pull(n_aes) %>% sum() n_aes_original <- df_ae %>% # all AEs for patients with more than 1 visit filter(SUBJID %in% df_aevs$SUBJID) %>% nrow() stopifnot(n_aes == n_aes_original)"},{"path":"https://openpharma.github.io/simaerep/articles/sas_files.html","id":"simaerep","dir":"Articles","previous_headings":"","what":"{simaerep}","title":"SAS Files as a Data Source","text":"renaming columns can pass aggregated data SAS files simaerep Left panel shows mean AE reporting per site (lightblue darkblue lines) mean AE reporting entire study (golden line). Single sites plotted descending order AE -reporting probability right panel grey lines denote cumulative AE count single patients. Grey dots left panel plot indicate sites picked single plotting. AE -reporting probability dark blue lines crossed threshold 95%. Numbers upper left corner indicate ratio patients used analysis total number patients. Patients study long enough reach evaluation point (visit_med75, see introduction) ignored.","code":"df_visit <- df_aevs_aggr %>% rename( study_id = \"STUDYID\", site_number = \"SITEID\", patnum = \"SUBJID\", n_ae = \"CUM_AE\", visit = \"VS_NO\" ) %>% select(study_id, site_number, patnum, n_ae, visit) df_visit ## # A tibble: 5,432 × 5 ## study_id site_number patnum n_ae visit ## ## 1 CO-101-001 001 01001001 1 1 ## 2 CO-101-001 001 01001001 2 2 ## 3 CO-101-001 001 01001001 2 3 ## 4 CO-101-001 001 01001001 2 4 ## 5 CO-101-001 001 01001001 2 5 ## 6 CO-101-001 001 01001001 2 6 ## 7 CO-101-001 001 01001001 2 7 ## 8 CO-101-001 001 01001001 3 8 ## 9 CO-101-001 001 01001001 3 9 ## 10 CO-101-001 001 01001001 6 10 ## # ℹ 5,422 more rows aerep <- simaerep(df_visit) plot(aerep)"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Address Usability Limits of Bootstrap Method","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(furrr)) suppressPackageStartupMessages(library(future)) suppressPackageStartupMessages(library(simaerep))"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Address Usability Limits of Bootstrap Method","text":"already mentioned introduction bootstrap resampling comes price. need good proportion sites compliant -reporting AEs. Otherwise patient pool draw tainted. Also, general count data, negative deviations overall small integer values (3 less) hard detect. assess usability limits bootstrap resampling method applying different simulated scenarios.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"parameter-grid","dir":"Articles","previous_headings":"","what":"Parameter Grid","title":"Address Usability Limits of Bootstrap Method","text":"start defining parameter grid. Fixed Parameters: - n_pat: 1000 - n_sites: 100 - max_visit_mean: 20 - max_visit_sd: 4 Variable Parameters: - ae_visit: 0.05 - 2 - frac_sites_wit_ur: 0.05 - 0.75 - ur_rate: 0.05 - 1","code":"set.seed(1) df_grid <- tibble( ae_per_visit_mean = seq(0.05, 2, length.out = 10)) %>% mutate(frac_site_with_ur = list(seq(0.05, 0.75, length.out = 10))) %>% unnest(frac_site_with_ur) %>% mutate(ur_rate = list(seq(0.05, 1, length.out = 10)) ) %>% unnest(ur_rate) df_grid ## # A tibble: 1,000 × 3 ## ae_per_visit_mean frac_site_with_ur ur_rate ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"apply","dir":"Articles","previous_headings":"","what":"Apply","title":"Address Usability Limits of Bootstrap Method","text":"use furrr future package multiprocessing. proceed applying functions already introduced.","code":"suppressWarnings(future::plan(multiprocess))"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"simulate-test-data","dir":"Articles","previous_headings":"Apply","what":"Simulate Test Data","title":"Address Usability Limits of Bootstrap Method","text":"iteratively apply sim_test_data_study() different parameter combination grid","code":"df_grid <- df_grid %>% mutate( df_visit = furrr::future_pmap( list(am = ae_per_visit_mean, fr = frac_site_with_ur, ur = ur_rate), function(am, fr, ur) sim_test_data_study(n_pat = 1000, n_sites = 100, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = am, frac_site_with_ur = fr, ur_rate = ur), .progress = FALSE, .options = furrr_options(seed = TRUE) ) ) df_grid ## # A tibble: 1,000 × 4 ## ae_per_visit_mean frac_site_with_ur ur_rate df_visit ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"aggregate-test-data","dir":"Articles","previous_headings":"Apply","what":"Aggregate Test Data","title":"Address Usability Limits of Bootstrap Method","text":"apply site_aggr() different simulated data sets.","code":"df_grid <- df_grid %>% mutate( df_visit = map2(df_visit, row_number(), function(x,y) mutate(x, study_id = paste(\"S\", y))), df_site = furrr::future_map(df_visit, site_aggr, .progress = FALSE, .options = furrr_options(seed = TRUE) ) ) df_grid ## # A tibble: 1,000 × 5 ## ae_per_visit_mean frac_site_with_ur ur_rate df_visit df_site ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"simulate-sites","dir":"Articles","previous_headings":"Apply","what":"Simulate Sites","title":"Address Usability Limits of Bootstrap Method","text":"apply sim_sites() calculate AE -reporting probability.","code":"df_grid <- df_grid %>% mutate( df_sim_sites = furrr::future_map2(df_site, df_visit, sim_sites, r = 1000, poisson_test = TRUE, prob_lower = TRUE, .progress = FALSE, .options = furrr_options(seed = TRUE) ) ) df_grid ## # A tibble: 1,000 × 6 ## ae_per_visit_mean frac_site_with_ur ur_rate df_visit df_site df_sim_sites ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"evaluate-sites","dir":"Articles","previous_headings":"Apply","what":"Evaluate Sites","title":"Address Usability Limits of Bootstrap Method","text":"apply eval_sites() balance bootstrapped probabilities expected number false positives.","code":"df_grid <- df_grid %>% mutate( df_eval = furrr::future_map(df_sim_sites, eval_sites, .progress = FALSE, .options = furrr_options(seed = TRUE) ) ) df_grid ## # A tibble: 1,000 × 7 ## ae_per_visit_mean frac_site_with_ur ur_rate df_visit df_site df_sim_sites ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows, and 1 more variable: df_eval "},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"get-metrics","dir":"Articles","previous_headings":"Apply","what":"Get Metrics","title":"Address Usability Limits of Bootstrap Method","text":"Apart calculating true false positives rate final -reporting probability also calculate metrics couple benchmark probabilities. p-values returned poisson.test() probabilities p-values adjusting expected false positives cases use 95% probability threshold.","code":"get_metrics <- function(df_visit, df_eval, method) { if (method == \"ptest\") { prob_ur_adj = \"pval_prob_ur\" prob_ur_unadj = \"pval\" } else { prob_ur_adj = \"prob_low_prob_ur\" prob_ur_unadj = \"prob_low\" } df_ur <- df_visit %>% select(site_number, is_ur) %>% distinct() df_metric_prep <- df_eval %>% left_join(df_ur, \"site_number\") %>% rename( prob_ur_adj = !! as.name(prob_ur_adj), prob_ur_unadj = !! as.name(prob_ur_unadj) ) df_metric_adjusted <- df_metric_prep %>% select(study_id, site_number, is_ur, prob_ur_adj) %>% mutate( tp = ifelse(is_ur & prob_ur_adj >= 0.95, 1, 0), fn = ifelse(is_ur & prob_ur_adj < 0.95, 1, 0), tn = ifelse((! is_ur) & prob_ur_adj < 0.95, 1, 0), fp = ifelse((! is_ur) & prob_ur_adj >= 0.95, 1, 0), p = ifelse(prob_ur_adj >= 0.95, 1, 0), n = ifelse(prob_ur_adj < 0.95, 1, 0), P = ifelse(is_ur, 1, 0), N = ifelse(! is_ur, 1, 0) ) %>% group_by(study_id) %>% select(-site_number, - is_ur, - prob_ur_adj) %>% summarize_all(sum) %>% mutate(prob_type = \"adjusted\") df_metric_unadjusted <- df_metric_prep %>% select(study_id, site_number, is_ur, prob_ur_unadj) %>% mutate( tp = ifelse(is_ur & prob_ur_unadj <= 0.05, 1, 0), fn = ifelse(is_ur & prob_ur_unadj > 0.05, 1, 0), tn = ifelse((! is_ur) & prob_ur_unadj > 0.05, 1, 0), fp = ifelse((! is_ur) & prob_ur_unadj <= 0.05, 1, 0), p = ifelse(prob_ur_unadj <= 0.05, 1, 0), n = ifelse(prob_ur_unadj > 0.05, 1, 0), P = ifelse(is_ur, 1, 0), N = ifelse(! is_ur, 1, 0) ) %>% group_by(study_id) %>% select(-site_number, - is_ur, - prob_ur_unadj) %>% summarize_all(sum) %>% mutate(prob_type = \"unadjusted\") df_metric <- bind_rows(df_metric_adjusted, df_metric_unadjusted) %>% mutate(method = method) } df_grid <- df_grid %>% mutate(df_metric_pval = furrr::future_map2(df_visit, df_eval, get_metrics, method = \"ptest\"), df_metric_prob_low = furrr::future_map2(df_visit, df_eval, get_metrics, method = \"bootstrap\"), df_metric = map2(df_metric_pval, df_metric_prob_low, bind_rows) ) df_grid ## # A tibble: 1,000 × 10 ## ae_per_visit_mean frac_site_with_ur ur_rate df_visit df_site df_sim_sites ## ## 1 0.05 0.05 0.05 ## 2 0.05 0.05 0.156 ## 3 0.05 0.05 0.261 ## 4 0.05 0.05 0.367 ## 5 0.05 0.05 0.472 ## 6 0.05 0.05 0.578 ## 7 0.05 0.05 0.683 ## 8 0.05 0.05 0.789 ## 9 0.05 0.05 0.894 ## 10 0.05 0.05 1 ## # … with 990 more rows, and 4 more variables: df_eval , ## # df_metric_pval , df_metric_prob_low , df_metric "},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"plot","dir":"Articles","previous_headings":"","what":"Plot","title":"Address Usability Limits of Bootstrap Method","text":"","code":"df_plot <- df_grid %>% select(ae_per_visit_mean, frac_site_with_ur, ur_rate, df_metric) %>% unnest(df_metric) df_plot ## # A tibble: 4,000 × 14 ## ae_per_visit_mean frac_site_with_ur ur_rate study_id tp fn tn fp ## ## 1 0.05 0.05 0.05 S 1 0 5 95 0 ## 2 0.05 0.05 0.05 S 1 0 5 94 1 ## 3 0.05 0.05 0.05 S 1 0 5 95 0 ## 4 0.05 0.05 0.05 S 1 0 5 94 1 ## 5 0.05 0.05 0.156 S 2 0 5 95 0 ## 6 0.05 0.05 0.156 S 2 0 5 95 0 ## 7 0.05 0.05 0.156 S 2 0 5 95 0 ## 8 0.05 0.05 0.156 S 2 0 5 92 3 ## 9 0.05 0.05 0.261 S 3 0 5 95 0 ## 10 0.05 0.05 0.261 S 3 0 5 95 0 ## # … with 3,990 more rows, and 6 more variables: p , n , P , ## # N , prob_type , method "},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"true-positive-rate---p---tpp","dir":"Articles","previous_headings":"","what":"True positive rate - P - TP/P","title":"Address Usability Limits of Bootstrap Method","text":"comparing bootstrap method poisson.test benchmark test, find perform mostly similar 0.05 - 0.3 ratio -reporting sites. performance still acceptable 0.3 - 0.5 ratio becomes pretty useless. expected detection -reporting sites close impossible ae/visit ratio low (0.05) detection rates close zero tested scenarios.","code":"p <- df_plot %>% mutate( tp_P_ratio = tp/P) %>% select(ae_per_visit_mean, frac_site_with_ur, ur_rate, tp_P_ratio, method, prob_type) %>% mutate_if(is.numeric, round, 3) %>% mutate(metric = case_when(method == \"ptest\" & prob_type == \"unadjusted\" ~ \"ptest unadjusted\", method == \"ptest\" & prob_type == \"adjusted\" ~ \"ptest adjusted\", method == \"bootstrap\" & prob_type == \"unadjusted\" ~ \"bootstrap unadjusted\", method == \"bootstrap\" & prob_type == \"adjusted\" ~ \"bootstrap adjusted\"), metric = fct_relevel(metric, c(\"ptest unadjusted\", \"ptest adjusted\", \"bootstrap unadjusted\", \"bootstrap adjusted\"))) %>% ggplot(aes(ae_per_visit_mean, tp_P_ratio, color = metric)) + geom_line(aes(linetype = prob_type), size = 0.5, alpha = 0.5) + facet_grid( frac_site_with_ur ~ ur_rate) + scale_color_manual(values = c(\"dodgerblue3\", \"skyblue1\", \"sienna4\", \"peru\")) + theme(panel.grid = element_blank(), legend.position = \"bottom\", axis.text.x = element_text(angle = 90, vjust = 0.5)) + labs(title = \"AE Under Reporting Rate ~ Rate of AE Under-Reporting Sites\", y = \"True positive rate - tp/P\", x = \"AEs per Visit\") p"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"false-positive-rate-fpn","dir":"Articles","previous_headings":"","what":"False positive rate FP/N","title":"Address Usability Limits of Bootstrap Method","text":"can show adjusting AE -reporting probability expected number false positives quite effective.false positive rate almost zero tested scenarios. However, also comes cost lowering true positive rate well.","code":"p <- df_plot %>% mutate( fpr = fp/N) %>% select(ae_per_visit_mean, frac_site_with_ur, ur_rate, fpr, method, prob_type) %>% mutate_if(is.numeric, round, 3) %>% mutate(metric = case_when(method == \"ptest\" & prob_type == \"unadjusted\" ~ \"ptest unadjusted\", method == \"ptest\" & prob_type == \"adjusted\" ~ \"ptest adjusted\", method == \"bootstrap\" & prob_type == \"unadjusted\" ~ \"bootstrap unadjusted\", method == \"bootstrap\" & prob_type == \"adjusted\" ~ \"bootstrap adjusted\"), metric = fct_relevel(metric, c(\"ptest unadjusted\", \"ptest adjusted\", \"bootstrap unadjusted\", \"bootstrap adjusted\"))) %>% ggplot(aes(ae_per_visit_mean, fpr, color = metric, linetype = prob_type)) + geom_line(size = 0.5, alpha = 0.5) + facet_grid( frac_site_with_ur ~ ur_rate) + scale_color_manual(values = c(\"dodgerblue3\", \"skyblue1\", \"sienna4\", \"peru\")) + theme(panel.grid = element_blank(), legend.position = \"bottom\", axis.text.x = element_text(angle = 90, vjust = 0.5)) + labs(title = \"AE Under Reporting Rate by Rate of AE Under-Reporting Sites\", y = \"False positive rate - fp/N\", x = \"AEs per Visit\") p"},{"path":"https://openpharma.github.io/simaerep/articles/usability_limits.html","id":"conclusion","dir":"Articles","previous_headings":"","what":"Conclusion","title":"Address Usability Limits of Bootstrap Method","text":"already outlined introduction true AE generating process unlikely standard poisson process. Therefore using non-parametric test remains recommended option unless reason believe 50% sites -reporting AEs. also show important adjust expected false positive rate calculating final probabilities. general can say recommend method detecting AE -reporting involves comparing AE counts ae per visit rate 0.05 lower.","code":""},{"path":"https://openpharma.github.io/simaerep/articles/visit_med75.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Adjusted Evaluation Point visit_med75","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(simaerep))"},{"path":"https://openpharma.github.io/simaerep/articles/visit_med75.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Adjusted Evaluation Point visit_med75","text":"Previously used unadjusted visit_med75. Simply taking median maximum patient visits site multiplying 0.75. number patients site reasonably large smallest maximum visit patients making cut-close equal cut-point.","code":"set.seed(1) df_visit <- sim_test_data_study( n_pat = 120, n_sites = 6, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit, method = \"med75_adj\") plot_visit_med75(df_visit, df_site, study_id_str = \"A\", n_site = 6) ## purple line: mean site ae of patients with visit_med75 ## grey line: patient included ## black dashed line: patient excluded ## dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site ## solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients ## dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study"},{"path":"https://openpharma.github.io/simaerep/articles/visit_med75.html","id":"only-few-patients-per-site","dir":"Articles","previous_headings":"","what":"Only few Patients per Site","title":"Adjusted Evaluation Point visit_med75","text":"patients per site chances greater smallest maximum visit patients make cut-much greater cut-point thus include visits analysis. cases adjust evaluation point smallest maximum visit patients make initial cut.","code":"set.seed(1) df_visit <- sim_test_data_study( n_pat = 24, n_sites = 6, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit, method = \"med75_adj\") plot_visit_med75(df_visit, df_site, study_id_str = \"A\", n_site = 6) ## purple line: mean site ae of patients with visit_med75 ## grey line: patient included ## black dashed line: patient excluded ## dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site ## solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients ## dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study"},{"path":"https://openpharma.github.io/simaerep/articles/visit_med75.html","id":"early-starters","dir":"Articles","previous_headings":"","what":"Early Starters","title":"Adjusted Evaluation Point visit_med75","text":"can happen site early starting patients. avoid evaluation point limited patient pool set maximum limit evaluation point includes least 20% patients study, taking 80% quantile maximum patient visits study.","code":"set.seed(1) # early starting site df_visit1 <- sim_test_data_study( n_pat = 3, n_sites = 1, max_visit_sd = 2, max_visit_mean = 40 ) # late starting sites df_visit2 <- sim_test_data_study( n_pat = 25, n_sites = 5, max_visit_sd = 2, max_visit_mean = 10 ) df_visit1$site_number <- paste0(\"A\", df_visit1$site_number) df_visit2$site_number <- paste0(\"B\", df_visit2$site_number) df_visit1$patnum <- paste0(\"A\", df_visit1$patnum) df_visit2$patnum <- paste0(\"B\", df_visit2$patnum) df_visit <- bind_rows(df_visit1, df_visit2) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) plot_visit_med75(df_visit, df_site, study_id_str = \"A\", n_site = 6) ## purple line: mean site ae of patients with visit_med75 ## grey line: patient included ## black dashed line: patient excluded ## dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site ## solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients ## dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study"},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"load","dir":"Articles","previous_headings":"","what":"Load","title":"Aggregate AEs by Days or Visit?","text":"","code":"suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) suppressPackageStartupMessages(library(simaerep))"},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Aggregate AEs by Days or Visit?","text":"generally aggregate AEs visit. patient follows visit schedule specified number days passes consecutive visit. visits scheduled contact point patient physicians day visit usually also day AEs get reported. Alternatively can also choose use simaerep ","code":""},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"load-data","dir":"Articles","previous_headings":"","what":"Load Data","title":"Aggregate AEs by Days or Visit?","text":"load public clinical trial data set contains data control arm see SAS files Data Source Article","code":"df_ae <- haven::read_sas('adae.sas7bdat') %>% select(STUDYID, SUBJID, SITEID, AESTDY) df_vs <- haven::read_sas('advs.sas7bdat') %>% select(STUDYID, SUBJID, SITEID, ADY) df_ae <- df_ae %>% rename(DY = AESTDY) %>% mutate(EVENT = \"AE\") df_vs <- df_vs %>% rename(DY = ADY) %>% mutate(EVENT = \"VS\") %>% # we ignore visits that have no date filter(! is.na(DY)) %>% # we are not interested in same day visits distinct() df_aevs <- bind_rows(df_ae, df_vs) %>% # NA's get sorted towards the end thus AEs with no date get sorted towards last visit arrange(STUDYID, SITEID, SUBJID, DY) %>% group_by(STUDYID, SITEID, SUBJID) %>% mutate(AE_NO = cumsum(ifelse(EVENT == \"AE\", 1, 0)), VS_NO = cumsum(ifelse(EVENT == \"VS\", 1, 0))) %>% # we remove patients with 0 visits filter(max(VS_NO) > 0) %>% # AE's before fist visit should register to visit 1 not zero mutate(VS_NO = ifelse(VS_NO == 0, 1, VS_NO)) df_aevs_aggr <- df_aevs %>% group_by(STUDYID, SITEID, SUBJID, VS_NO) %>% summarise(MIN_AE_NO = min(AE_NO), MAX_AE_NO = max(AE_NO), .groups = \"drop\") %>% group_by(STUDYID, SITEID, SUBJID) %>% mutate(MAX_VS_PAT = max(VS_NO)) %>% ungroup() %>% # assign AEs that occur after last visit to last AE mutate( CUM_AE = ifelse( VS_NO == MAX_VS_PAT, MAX_AE_NO, MIN_AE_NO) ) df_visit <- df_aevs_aggr %>% rename( study_id = \"STUDYID\", site_number = \"SITEID\", patnum = \"SUBJID\", n_ae = \"CUM_AE\", visit = \"VS_NO\" ) %>% select(study_id, site_number, patnum, n_ae, visit)"},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"aggregate-on-days","dir":"Articles","previous_headings":"","what":"Aggregate on Days","title":"Aggregate AEs by Days or Visit?","text":"aggregating days need align reference timelines single patients. day first visit different patient start negative values. First correct values positive normalize AE date values date value first visit patient check get transformation visit aggregations gaps days leading implicitly missing values. simaerep correct automatically throw warning. silence warning can use check df_visit() also called internally functions accepting df_visit argument. proceed usual.","code":"df_vs_min_max <- df_vs %>% group_by(STUDYID, SUBJID, SITEID) %>% summarise(min_DY = min(DY, na.rm = TRUE), max_DY = max(DY, na.rm = TRUE), .groups = \"drop\") df_vs_min_max$min_DY[1:25] ## [1] -7 -14 -14 -7 -3 -10 -19 -12 -6 -9 -9 -12 -8 -7 -6 -8 -3 -14 -7 ## [20] -14 -19 -12 -9 -21 -28 df_vs_min_max$max_DY[1:25] ## [1] 309 134 43 1 224 265 100 163 51 103 40 38 125 168 85 92 100 708 119 ## [20] 64 43 51 70 1 225 corr_factor <- abs(min(df_vs_min_max$min_DY)) df_days <- df_ae %>% # include patients with vs but no AE right_join(df_vs_min_max, by = c(\"STUDYID\", \"SUBJID\", \"SITEID\")) %>% # replace DY NULL with max patient DY group_by(STUDYID, SUBJID, SITEID) %>% mutate(DY = ifelse(is.na(DY) & ! is.na(EVENT), max(DY, na.rm = TRUE), DY)) %>% # replace DY for patients with 0 AE with day of maximum visit mutate(DY = ifelse(is.na(DY) & is.na(EVENT), max_DY, DY)) %>% # correct timelines mutate(DY = DY + corr_factor, min_DY = min_DY + corr_factor, DY_corr = DY + min_DY) %>% group_by(STUDYID, SITEID, SUBJID) %>% arrange(STUDYID, SITEID, SUBJID, DY_corr) %>% mutate(n_ae = row_number()) %>% ungroup() %>% # set AE count to 0 for patients with no AEs mutate(n_ae = ifelse(is.na(EVENT), 0 , n_ae)) %>% rename( study_id = STUDYID, site_number = SITEID, patnum = SUBJID, visit = DY_corr ) %>% group_by(study_id, site_number, patnum, visit) %>% summarise(n_ae = max(n_ae), .groups = \"drop\") stopifnot(n_distinct(df_days$site_number) == n_distinct(df_visit$site_number)) stopifnot(n_distinct(df_days$patnum) == n_distinct(df_visit$patnum)) pat0_days <- df_days %>% group_by(study_id, site_number, patnum) %>% filter(max(n_ae) == 0) %>% pull(patnum) %>% unique() %>% sort() pat0_vs <- df_visit %>% group_by(study_id, site_number, patnum) %>% filter(max(n_ae) == 0) %>% pull(patnum) %>% unique() %>% sort() stopifnot(all(pat0_days == pat0_vs)) df_days ## # A tibble: 3,220 × 5 ## study_id site_number patnum visit n_ae ## ## 1 CO-101-001 001 01001001 43 1 ## 2 CO-101-001 001 01001001 44 2 ## 3 CO-101-001 001 01001001 107 3 ## 4 CO-101-001 001 01001001 124 4 ## 5 CO-101-001 001 01001001 134 5 ## 6 CO-101-001 001 01001001 141 6 ## 7 CO-101-001 001 01001001 156 8 ## 8 CO-101-001 001 01001001 184 10 ## 9 CO-101-001 001 01001001 240 13 ## 10 CO-101-001 001 01001001 241 14 ## # ℹ 3,210 more rows df_site <- site_aggr(df_visit = df_days) ## Warning in exp_implicit_missing_visits(df_visit): implicitly missing visit ## numbers detected and corrected df_days <- simaerep:::check_df_visit(df_days) ## Warning in exp_implicit_missing_visits(df_visit): implicitly missing visit ## numbers detected and corrected df_days ## # A tibble: 57,873 × 5 ## study_id site_number patnum visit n_ae ## ## 1 CO-101-001 001 01001001 5 0 ## 2 CO-101-001 001 01001001 6 0 ## 3 CO-101-001 001 01001001 7 0 ## 4 CO-101-001 001 01001001 8 0 ## 5 CO-101-001 001 01001001 9 0 ## 6 CO-101-001 001 01001001 10 0 ## 7 CO-101-001 001 01001001 11 0 ## 8 CO-101-001 001 01001001 12 0 ## 9 CO-101-001 001 01001001 13 0 ## 10 CO-101-001 001 01001001 14 0 ## # ℹ 57,863 more rows df_sim_sites <- sim_sites(df_site, df_visit = df_days) df_eval_days <- eval_sites(df_sim_sites) simaerep::plot_study(df_visit = df_days, df_site = df_site, df_eval = df_eval_days, study = unique(df_days$study_id))"},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"aggregate-on-visits","dir":"Articles","previous_headings":"","what":"Aggregate on Visits","title":"Aggregate AEs by Days or Visit?","text":"results compare aggregating visits?","code":"df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit) df_eval_vs <- eval_sites(df_sim_sites) simaerep::plot_study(df_visit, df_site, df_eval_vs, study = unique(df_visit$study_id))"},{"path":"https://openpharma.github.io/simaerep/articles/visits_or_days.html","id":"compare","dir":"Articles","previous_headings":"","what":"Compare","title":"Aggregate AEs by Days or Visit?","text":"observe difference results. largely attributable difference cut-visit_med75 points influences set patients included. case observe high rank correlation low p-value results greater 0. inclusion/exclusion patients analysis site ongoing trial can shift results, recommend aggregate actually occurred visits included patients equal amount opportunities report AEs.","code":"df_comp <- df_eval_days %>% select( site_number, prob_low_prob_ur_days = prob_low_prob_ur, n_pat_with_med75_days = n_pat_with_med75 ) %>% left_join( select( df_eval_vs, site_number, prob_low_prob_ur_vs = prob_low_prob_ur, n_pat_with_med75_vs = n_pat_with_med75 ), by = \"site_number\" ) %>% filter(prob_low_prob_ur_days > 0 | prob_low_prob_ur_vs > 0) %>% select(site_number, starts_with(\"prob\"), starts_with(\"n_pat\")) %>% arrange(desc(prob_low_prob_ur_vs)) df_comp %>% knitr::kable() cor.test( df_comp$prob_low_prob_ur_vs, df_comp$prob_low_prob_ur_days, method = \"spearman\" ) ## Warning in cor.test.default(df_comp$prob_low_prob_ur_vs, ## df_comp$prob_low_prob_ur_days, : Cannot compute exact p-value with ties ## ## Spearman's rank correlation rho ## ## data: df_comp$prob_low_prob_ur_vs and df_comp$prob_low_prob_ur_days ## S = 114.71, p-value = 0.009797 ## alternative hypothesis: true rho is not equal to 0 ## sample estimates: ## rho ## 0.6848696"},{"path":"https://openpharma.github.io/simaerep/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Bjoern Koneswarakantha. Author, maintainer, copyright holder. F. Hoffmann-La Roche Ltd. Copyright holder.","code":""},{"path":"https://openpharma.github.io/simaerep/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Koneswarakantha B (2024). simaerep: Find Clinical Trial Sites -Reporting Adverse Events. R package version 0.5.0.900, https://github.com/openpharma/simaerep, https://openpharma.github.io/simaerep/.","code":"@Manual{, title = {simaerep: Find Clinical Trial Sites Under-Reporting Adverse Events}, author = {Bjoern Koneswarakantha}, year = {2024}, note = {R package version 0.5.0.900, https://github.com/openpharma/simaerep}, url = {https://openpharma.github.io/simaerep/}, }"},{"path":"https://openpharma.github.io/simaerep/index.html","id":"simaerep-","dir":"","previous_headings":"","what":"Find Clinical Trial Sites Under-Reporting Adverse Events","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"Simulate adverse event reporting clinical trials goal detecting -reporting sites. Monitoring Adverse Event (AE) reporting clinical trials important patient safety. use bootstrap-based simulation assign AE -reporting probability site clinical trial. method inspired ‘infer’ R package Allen Downey’s blog article: “one test!”.","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/index.html","id":"cran","dir":"","previous_headings":"Installation","what":"CRAN","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"","code":"install.packages(\"simaerep\")"},{"path":"https://openpharma.github.io/simaerep/index.html","id":"development-version","dir":"","previous_headings":"Installation","what":"Development Version","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"can install development version GitHub :","code":"# install.packages(\"devtools\") devtools::install_github(\"openpharma/simaerep\")"},{"path":"https://openpharma.github.io/simaerep/index.html","id":"impala","dir":"","previous_headings":"","what":"IMPALA","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"simaerep published workproduct Inter-Company Quality Analytics (IMPALA) consortium. IMPALA aims engage Health Authorities inspectors defining guiding principles use advanced analytics complement, enhance accelerate current QA practices. simaerep initially developed Roche currently evaluated companies across industry complement quality assurance activities (see testimonials).","code":""},{"path":"https://openpharma.github.io/simaerep/index.html","id":"publication","dir":"","previous_headings":"","what":"Publication","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"Koneswarakantha, B., Barmaz, Y., Ménard, T. et al. Follow-Use Advanced Analytics Clinical Quality Assurance: Bootstrap Resampling Enhance Detection Adverse Event -Reporting. Drug Saf (2020). https://doi.org/10.1007/s40264-020-01011-5","code":""},{"path":"https://openpharma.github.io/simaerep/index.html","id":"vignettes-articles-tutorials","dir":"","previous_headings":"","what":"Vignettes/ Articles/ Tutorials","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"video presentation 15 min Introduction Usability Limits Check Poisson Test Applicability SAS Files Adjusted Evaluation Point visit_med75 Aggregate AEs Days Visit? Portfolio Performance","code":""},{"path":"https://openpharma.github.io/simaerep/index.html","id":"validation-report","dir":"","previous_headings":"","what":"Validation Report","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"Download pdf release section generated using thevalidatoR.","code":""},{"path":"https://openpharma.github.io/simaerep/index.html","id":"application","dir":"","previous_headings":"","what":"Application","title":"Find Clinical Trial Sites Under-Reporting Adverse Events","text":"Left panel shows mean AE reporting per site (lightblue darkblue lines) mean AE reporting entire study (golden line). Single sites plotted descending order AE -reporting probability right panel grey lines denote cumulative AE count single patients. Grey dots left panel plot indicate sites picked single plotting. AE -reporting probability dark blue lines crossed threshold 95%. Numbers upper left corner indicate ratio patients used analysis total number patients. Patients study long enough reach evaluation point (visit_med75, see introduction) ignored.","code":"suppressPackageStartupMessages(library(simaerep)) suppressPackageStartupMessages(library(tidyverse)) suppressPackageStartupMessages(library(knitr)) set.seed(1) df_visit <- sim_test_data_study( n_pat = 1000, # number of patients in study n_sites = 100, # number of sites in study frac_site_with_ur = 0.05, # fraction of sites under-reporting ur_rate = 0.4, # rate of under-reporting ae_per_visit_mean = 0.5 # mean AE per patient visit ) df_visit$study_id <- \"A\" df_visit %>% select(study_id, site_number, patnum, visit, n_ae) %>% head(25) %>% knitr::kable() aerep <- simaerep(df_visit) plot(aerep, study = \"A\")"},{"path":"https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html","id":null,"dir":"Reference","previous_headings":"","what":"Aggregate duplicated visits. — aggr_duplicated_visits","title":"Aggregate duplicated visits. — aggr_duplicated_visits","text":"Internal function called check_df_visit().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Aggregate duplicated visits. — aggr_duplicated_visits","text":"","code":"aggr_duplicated_visits(df_visit)"},{"path":"https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Aggregate duplicated visits. — aggr_duplicated_visits","text":"df_visit dataframe columns: study_id, site_number, patnum, visit, n_ae","code":""},{"path":"https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Aggregate duplicated visits. — aggr_duplicated_visits","text":"df_visit corrected","code":""},{"path":"https://openpharma.github.io/simaerep/reference/check_df_visit.html","id":null,"dir":"Reference","previous_headings":"","what":"Integrity check for df_visit. — check_df_visit","title":"Integrity check for df_visit. — check_df_visit","text":"Internal function used functions accept df_visit parameter. Checks NA columns, numeric visits AEs, implicitly missing duplicated visits.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/check_df_visit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Integrity check for df_visit. — check_df_visit","text":"","code":"check_df_visit(df_visit)"},{"path":"https://openpharma.github.io/simaerep/reference/check_df_visit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Integrity check for df_visit. — check_df_visit","text":"df_visit dataframe columns: study_id, site_number, patnum, visit, n_ae","code":""},{"path":"https://openpharma.github.io/simaerep/reference/check_df_visit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Integrity check for df_visit. — check_df_visit","text":"corrected df_visit","code":""},{"path":"https://openpharma.github.io/simaerep/reference/check_df_visit.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Integrity check for df_visit. — check_df_visit","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" df_visit_filt <- df_visit %>% dplyr::filter(visit != 3) df_visit_corr <- check_df_visit(df_visit_filt) #> Warning: implicitly missing visit numbers detected and corrected 3 %in% df_visit_corr$visit #> [1] TRUE nrow(df_visit_corr) == nrow(df_visit) #> [1] TRUE df_visit_corr <- check_df_visit(dplyr::bind_rows(df_visit, df_visit)) #> Warning: Duplicated visit entries for some patients detected and corrected. nrow(df_visit_corr) == nrow(df_visit) #> [1] TRUE"},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites.html","id":null,"dir":"Reference","previous_headings":"","what":"Evaluate sites. — eval_sites","title":"Evaluate sites. — eval_sites","text":"Correct -reporting probabilities using p.adjust.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Evaluate sites. — eval_sites","text":"","code":"eval_sites(df_sim_sites, method = \"BH\", under_only = TRUE, ...)"},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Evaluate sites. — eval_sites","text":"df_sim_sites dataframe generated sim_sites method character, passed stats::p.adjust(), NULL eval_sites_deprecated() used instead, Default = \"BH\" under_only compute -reporting probabilities , default = TRUE check_df_visit(), computationally expensive large data sets. Default: TRUE ... use pass r_sim_sites parameter eval_sites_deprecated()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Evaluate sites. — eval_sites","text":"dataframe following columns: study_id study identification site_number site identification visit_med75 median(max(visit)) * 0.75 mean_ae_site_med75 mean AE visit_med75 site level mean_ae_study_med75 mean AE visit_med75 study level pval p-value returned poisson.test prob_low bootstrapped probability mean_ae_site_med75 lower pval_adj adjusted p-values prob_low_adj adjusted bootstrapped probability mean_ae_site_med75 lower pval_prob_ur probability -reporting 1 - pval_adj, poisson.test (use benchmark) prob_low_prob_ur probability -reporting 1 - prob_low_adj, bootstrapped (use)","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Evaluate sites. — eval_sites","text":"","code":"df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit, r = 100) df_eval <- eval_sites(df_sim_sites) df_eval #> # A tibble: 5 × 14 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0001 20 16 15 2.62 #> 2 A S0002 20 16 16 2.75 #> 3 A S0003 20 19 15 6.37 #> 4 A S0004 20 16 16 7.81 #> 5 A S0005 20 17 16 8.59 #> # ℹ 8 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # pval , prob_low , pval_adj , pval_prob_ur , #> # prob_low_adj , prob_low_prob_ur # use deprecated method ------- df_eval <- eval_sites(df_sim_sites, method = NULL, r_sim_sites = 100) #> Warning: using deprecated method for probability adjustment df_eval #> # A tibble: 5 × 19 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0002 20 16 16 2.75 #> 2 A S0001 20 16 15 2.62 #> 3 A S0003 20 19 15 6.37 #> 4 A S0004 20 16 16 7.81 #> 5 A S0005 20 17 16 8.59 #> # ℹ 13 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # pval , prob_low , n_site , pval_n_detected , #> # pval_fp , pval_p_vs_fp_ratio , pval_prob_ur , #> # prob_low_n_detected , prob_low_fp , prob_low_p_vs_fp_ratio , #> # prob_low_prob_ur "},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":null,"dir":"Reference","previous_headings":"","what":"Evaluate sites. — eval_sites_deprecated","title":"Evaluate sites. — eval_sites_deprecated","text":"Correct -reporting probabilities expected number false positives (fp). deprecated favor conventional methods available via p.adjust.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Evaluate sites. — eval_sites_deprecated","text":"","code":"eval_sites_deprecated(df_sim_sites, r_sim_sites)"},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Evaluate sites. — eval_sites_deprecated","text":"df_sim_sites dataframe generated sim_sites() r_sim_sites integer, number repeats bootstrap resampling site simulation, needed zero probability correction fp calculation, Default: 1000","code":""},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Evaluate sites. — eval_sites_deprecated","text":"dataframe following columns: study_id study identification site_number site identification visit_med75 median(max(visit)) * 0.75 mean_ae_site_med75 mean AE visit_med75 site level mean_ae_study_med75 mean AE visit_med75 study level pval p-value returned poisson.test prob_low bootstrapped probability mean_ae_site_med75 lower n_site number study sites pval_n_detected sites p-value lower pval_fp expected number fp, pval * n_site pval_p_vs_fp_ratio odds -reporting p/fp, poisson.test (use benchmark) pval_prob_ur probability -reporting 1 - fp/p, poisson.test (use benchmark) prob_low_n_detected sites bootstrapped probability lower prob_low_fp expected number fp, prob_lower * n_site prob_low_p_vs_fp_ratio odds -reporting p/fp, bootstrapped (use) prob_low_prob_ur probability -reporting 1 - fp/p, bootstrapped (use)","code":""},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Evaluate sites. — eval_sites_deprecated","text":"chance expected number false positives (fp) greater total number positives (p) set p_vs_fp_ratio = 1 prob_ur = 0.","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Evaluate sites. — eval_sites_deprecated","text":"","code":"df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit, r = 100) df_eval <- eval_sites_deprecated(df_sim_sites, r_sim_sites = 100) df_eval #> # A tibble: 5 × 19 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0002 20 17 18 3.94 #> 2 A S0001 20 18 16 3.39 #> 3 A S0003 20 18 17 10.2 #> 4 A S0004 20 18 14 7.28 #> 5 A S0005 20 17 15 7.29 #> # ℹ 13 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # pval , prob_low , n_site , pval_n_detected , #> # pval_fp , pval_p_vs_fp_ratio , pval_prob_ur , #> # prob_low_n_detected , prob_low_fp , prob_low_p_vs_fp_ratio , #> # prob_low_prob_ur "},{"path":"https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html","id":null,"dir":"Reference","previous_headings":"","what":"Expose implicitly missing visits. — exp_implicit_missing_visits","title":"Expose implicitly missing visits. — exp_implicit_missing_visits","text":"Internal function called check_df_visit().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Expose implicitly missing visits. — exp_implicit_missing_visits","text":"","code":"exp_implicit_missing_visits(df_visit)"},{"path":"https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Expose implicitly missing visits. — exp_implicit_missing_visits","text":"df_visit dataframe columns: study_id, site_number, patnum, visit, n_ae","code":""},{"path":"https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Expose implicitly missing visits. — exp_implicit_missing_visits","text":"df_visit corrected","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_config.html","id":null,"dir":"Reference","previous_headings":"","what":"Get Portfolio Configuration — get_config","title":"Get Portfolio Configuration — get_config","text":"Get Portfolio configuration dataframe aggregated patient level max_ae max_visit. filter studies sites patients anonymize IDs. Portfolio configuration can used sim_test_data_portfolio generate data artificial portfolio.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_config.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get Portfolio Configuration — get_config","text":"","code":"get_config( df_site, min_pat_per_study = 100, min_sites_per_study = 10, anonymize = TRUE, pad_width = 4 )"},{"path":"https://openpharma.github.io/simaerep/reference/get_config.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get Portfolio Configuration — get_config","text":"df_site dataframe aggregated patient level max_ae max_visit min_pat_per_study minimum number patients per study, Default: 100 min_sites_per_study minimum number sites per study, Default: 10 anonymize logical, Default: TRUE pad_width padding width newly created IDs, Default: 4","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_config.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get Portfolio Configuration — get_config","text":"dataframe following columns: study_id study identification ae_per_visit_mean mean AE per visit per study site_number site max_visit_sd standard deviation maximum patient visits per site max_visit_mean mean maximum patient visits per site n_pat number patients","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/get_config.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get Portfolio Configuration — get_config","text":"","code":"# \\donttest{ df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site_max <- df_visit %>% dplyr::group_by(study_id, site_number, patnum) %>% dplyr::summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config <- get_config(df_site_max) df_config #> # A tibble: 20 × 6 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean n_pat #> #> 1 0001 0.400 0001 4.61 18.8 10 #> 2 0001 0.400 0002 3.78 19.4 10 #> 3 0001 0.400 0003 3.28 20.1 10 #> 4 0001 0.400 0004 4.84 19.4 10 #> 5 0001 0.400 0005 4.30 21.6 10 #> 6 0001 0.400 0006 4.18 19.1 10 #> 7 0001 0.400 0007 4.06 17.5 10 #> 8 0001 0.400 0008 4.10 20.8 10 #> 9 0001 0.400 0009 4.74 18.5 10 #> 10 0001 0.400 0010 4.30 18.6 10 #> 11 0002 0.487 0001 3.10 20.6 10 #> 12 0002 0.487 0002 2.75 20 10 #> 13 0002 0.487 0003 3.71 19.2 10 #> 14 0002 0.487 0004 5.34 20.4 10 #> 15 0002 0.487 0005 3.84 18.5 10 #> 16 0002 0.487 0006 4.35 19.4 10 #> 17 0002 0.487 0007 5.48 19.4 10 #> 18 0002 0.487 0008 3.62 22 10 #> 19 0002 0.487 0009 4.00 17.7 10 #> 20 0002 0.487 0010 2.60 19.1 10 df_portf <- sim_test_data_portfolio(df_config) df_portf #> # A tibble: 3,792 × 8 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean patnum #> #> 1 0001 0.400 0001 4.61 18.8 0001 #> 2 0001 0.400 0001 4.61 18.8 0001 #> 3 0001 0.400 0001 4.61 18.8 0001 #> 4 0001 0.400 0001 4.61 18.8 0001 #> 5 0001 0.400 0001 4.61 18.8 0001 #> 6 0001 0.400 0001 4.61 18.8 0001 #> 7 0001 0.400 0001 4.61 18.8 0001 #> 8 0001 0.400 0001 4.61 18.8 0001 #> 9 0001 0.400 0001 4.61 18.8 0001 #> 10 0001 0.400 0001 4.61 18.8 0001 #> # ℹ 3,782 more rows #> # ℹ 2 more variables: visit , n_ae df_scen <- sim_ur_scenarios(df_portf, extra_ur_sites = 2, ur_rate = c(0.5, 1)) #> aggregating site level #> prepping for simulation #> generating scenarios #> getting under-reporting stats #> evaluating stats df_scen #> # A tibble: 140 × 14 #> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat #> #> 1 0001 0001 0 0 0 10 #> 2 0001 0001 0 0.0989 0.5 10 #> 3 0001 0001 0 0.0989 1 10 #> 4 0001 0001 1 0.199 0.5 10 #> 5 0001 0001 1 0.199 1 10 #> 6 0001 0001 2 0.299 0.5 10 #> 7 0001 0001 2 0.299 1 10 #> 8 0001 0002 0 0 0 10 #> 9 0001 0002 0 0.107 0.5 10 #> 10 0001 0002 0 0.107 1 10 #> # ℹ 130 more rows #> # ℹ 8 more variables: n_pat_with_med75 , visit_med75 , #> # mean_ae_site_med75 , mean_ae_study_med75 , #> # n_pat_with_med75_study , prob_low , prob_low_adj , #> # prob_low_prob_ur df_perf <- get_portf_perf(df_scen) df_perf #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> #> 1 0.001 0.53 0 0 0.2 #> 2 0.001 0.53 1 0 0.2 #> 3 0.001 0.53 2 0 0.2 #> 4 0.001 0.53 0 0.5 1 #> 5 0.001 0.53 1 0.5 1 #> 6 0.001 0.53 2 0.5 1 #> 7 0.001 0.53 0 1 1 #> 8 0.001 0.53 1 1 1 #> 9 0.001 0.53 2 1 1 #> 10 0.01 0.53 0 0 0.2 #> # ℹ 17 more rows # }"},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":null,"dir":"Reference","previous_headings":"","what":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"Test function, test applicability poisson test, calculating bootstrapped probability obtaining specific p-value lower, use combination sim_studies().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"","code":"get_ecd_values(df_sim_studies, df_sim_sites, val_str)"},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"df_sim_studies dataframe, generated sim_studies() df_sim_sites dataframe, generated sim_sites() val_str c(\"prob_low\",\"pval\")","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"dataframe following columns: study_id study identification site_number site identification visit_med75 median(max(visit)) * 0.75 mean_ae_site_med75 mean AE visit_med75 site level mean_ae_study_med75 mean AE visit_med75 study level pval/prob_low p-value returned poisson.test pval/prob_low_ecd p-value returned poisson.test","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"trains ecdf function studies based results sim_studies()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_ecd_values.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get empirical cumulative distribution values of pval or prob_lower — get_ecd_values","text":"","code":"df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.3) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit, r = 100) df_sim_studies <- sim_studies( df_site = df_site, df_visit = df_visit, r = 3, parallel = FALSE, poisson_test = TRUE, prob_lower = TRUE ) get_ecd_values(df_sim_studies, df_sim_sites, \"prob_low\") #> # A tibble: 5 × 11 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0001 20 18 17 5.89 #> 2 A S0002 20 18 15 5.5 #> 3 A S0003 20 17 16 8.53 #> 4 A S0004 20 20 15 6.4 #> 5 A S0005 20 16 16 8.38 #> # ℹ 5 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # pval , prob_low , prob_low_ecd get_ecd_values(df_sim_studies, df_sim_sites, \"pval\") #> # A tibble: 5 × 11 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0001 20 18 17 5.89 #> 2 A S0002 20 18 15 5.5 #> 3 A S0003 20 17 16 8.53 #> 4 A S0004 20 20 15 6.4 #> 5 A S0005 20 16 16 8.38 #> # ℹ 5 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # pval , prob_low , pval_ecd "},{"path":"https://openpharma.github.io/simaerep/reference/get_legend.html","id":null,"dir":"Reference","previous_headings":"","what":"replace cowplot::get_legend, to silence warning Multiple components found; returning the first one. To return all, use `return_all = TRUE — get_legend","title":"replace cowplot::get_legend, to silence warning Multiple components found; returning the first one. To return all, use `return_all = TRUE — get_legend","text":"replace cowplot::get_legend, silence warning Multiple components found; returning first one. return , use `return_all = TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_legend.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"replace cowplot::get_legend, to silence warning Multiple components found; returning the first one. To return all, use `return_all = TRUE — get_legend","text":"","code":"get_legend(p)"},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":null,"dir":"Reference","previous_headings":"","what":"Configure study patient pool by site parameters. — get_pat_pool_config","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"Internal Function used sim_sites()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"","code":"get_pat_pool_config(df_visit, df_site, min_n_pat_with_med75 = 1)"},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"df_visit dataframe df_site dataframe created site_aggr() min_n_pat_with_med75 minimum number patients visit_med_75 simulation, Default: 1","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"simulating study need configure study patient pool match configuration sites","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Configure study patient pool by site parameters. — get_pat_pool_config","text":"","code":"df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 1000, n_sites = 3, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site <- site_aggr(df_visit) df_config <- get_pat_pool_config(df_visit, df_site) df_config #> # A tibble: 8 × 6 #> study_id site_number visit_med75 n_pat_with_med75 n_pat_study pat_pool #> #> 1 A S0001 15 19 71 #> 2 A S0002 15 18 72 #> 3 A S0003 16 17 66 #> 4 A S0004 15 20 70 #> 5 A S0005 16 16 67 #> 6 B S0001 15 302 602 #> 7 B S0002 15 301 603 #> 8 B S0003 15 301 603 "},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":null,"dir":"Reference","previous_headings":"","what":"Get Portfolio Performance — get_portf_perf","title":"Get Portfolio Performance — get_portf_perf","text":"Performance true positive rate (tpr tp/P) basis desired false positive rates (fpr fp/P).","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get Portfolio Performance — get_portf_perf","text":"","code":"get_portf_perf(df_scen, stat = \"prob_low_prob_ur\", fpr = c(0.001, 0.01, 0.05))"},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get Portfolio Performance — get_portf_perf","text":"df_scen dataframe returned sim_ur_scenarios stat character denoting column name -reporting statistic, Default: 'prob_low_prob_ur' fpr numeric vector specifying false positive rates, Default: c(0.001, 0.01, 0.05)","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get Portfolio Performance — get_portf_perf","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Get Portfolio Performance — get_portf_perf","text":"DETAILS","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/get_portf_perf.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get Portfolio Performance — get_portf_perf","text":"","code":"# \\donttest{ df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site_max <- df_visit %>% dplyr::group_by(study_id, site_number, patnum) %>% dplyr::summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config <- get_config(df_site_max) df_config #> # A tibble: 20 × 6 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean n_pat #> #> 1 0001 0.373 0001 3.79 19.2 10 #> 2 0001 0.373 0002 3.07 19.9 10 #> 3 0001 0.373 0003 1.91 17.9 10 #> 4 0001 0.373 0004 4.88 20 10 #> 5 0001 0.373 0005 3.43 19 10 #> 6 0001 0.373 0006 3.77 21.3 10 #> 7 0001 0.373 0007 3.45 18.9 10 #> 8 0001 0.373 0008 2.81 17.1 10 #> 9 0001 0.373 0009 5.27 17.4 10 #> 10 0001 0.373 0010 3.77 19.3 10 #> 11 0002 0.507 0001 3.57 18.5 10 #> 12 0002 0.507 0002 5.10 19.6 10 #> 13 0002 0.507 0003 4.44 19.8 10 #> 14 0002 0.507 0004 3.38 19.1 10 #> 15 0002 0.507 0005 2.72 21.4 10 #> 16 0002 0.507 0006 2.46 19.4 10 #> 17 0002 0.507 0007 5.29 19.3 10 #> 18 0002 0.507 0008 4.17 19.5 10 #> 19 0002 0.507 0009 3.68 18.2 10 #> 20 0002 0.507 0010 2.12 20.5 10 df_portf <- sim_test_data_portfolio(df_config) df_portf #> # A tibble: 3,896 × 8 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean patnum #> #> 1 0001 0.373 0001 3.79 19.2 0001 #> 2 0001 0.373 0001 3.79 19.2 0001 #> 3 0001 0.373 0001 3.79 19.2 0001 #> 4 0001 0.373 0001 3.79 19.2 0001 #> 5 0001 0.373 0001 3.79 19.2 0001 #> 6 0001 0.373 0001 3.79 19.2 0001 #> 7 0001 0.373 0001 3.79 19.2 0001 #> 8 0001 0.373 0001 3.79 19.2 0001 #> 9 0001 0.373 0001 3.79 19.2 0001 #> 10 0001 0.373 0001 3.79 19.2 0001 #> # ℹ 3,886 more rows #> # ℹ 2 more variables: visit , n_ae df_scen <- sim_ur_scenarios(df_portf, extra_ur_sites = 2, ur_rate = c(0.5, 1)) #> aggregating site level #> prepping for simulation #> generating scenarios #> getting under-reporting stats #> evaluating stats df_scen #> # A tibble: 140 × 14 #> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat #> #> 1 0001 0001 0 0 0 10 #> 2 0001 0001 0 0.111 0.5 10 #> 3 0001 0001 0 0.111 1 10 #> 4 0001 0001 1 0.211 0.5 10 #> 5 0001 0001 1 0.211 1 10 #> 6 0001 0001 2 0.311 0.5 10 #> 7 0001 0001 2 0.311 1 10 #> 8 0001 0002 0 0 0 10 #> 9 0001 0002 0 0.120 0.5 10 #> 10 0001 0002 0 0.120 1 10 #> # ℹ 130 more rows #> # ℹ 8 more variables: n_pat_with_med75 , visit_med75 , #> # mean_ae_site_med75 , mean_ae_study_med75 , #> # n_pat_with_med75_study , prob_low , prob_low_adj , #> # prob_low_prob_ur df_perf <- get_portf_perf(df_scen) df_perf #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> #> 1 0.001 0.872 0 0 0.05 #> 2 0.001 0.872 1 0 0.05 #> 3 0.001 0.872 2 0 0.05 #> 4 0.001 0.872 0 0.5 1 #> 5 0.001 0.872 1 0.5 0.95 #> 6 0.001 0.872 2 0.5 0.95 #> 7 0.001 0.872 0 1 1 #> 8 0.001 0.872 1 1 1 #> 9 0.001 0.872 2 1 1 #> 10 0.01 0.799 0 0 0.05 #> # ℹ 17 more rows # }"},{"path":"https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html","id":null,"dir":"Reference","previous_headings":"","what":"Get site mean ae development. — get_site_mean_ae_dev","title":"Get site mean ae development. — get_site_mean_ae_dev","text":"Internal function used site_aggr(), plot_visit_med75(), returns mean AE development visit 0 visit_med75.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get site mean ae development. — get_site_mean_ae_dev","text":"","code":"get_site_mean_ae_dev(df_visit, df_pat, df_site)"},{"path":"https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get site mean ae development. — get_site_mean_ae_dev","text":"df_visit dataframe df_pat dataframe returned pat_aggr() df_site dataframe returned site_aggr()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get site mean ae development. — get_site_mean_ae_dev","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_visit_med75.html","id":null,"dir":"Reference","previous_headings":"","what":"Get visit_med75. — get_visit_med75","title":"Get visit_med75. — get_visit_med75","text":"Internal function used site_aggr().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_visit_med75.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get visit_med75. — get_visit_med75","text":"","code":"get_visit_med75(df_pat, method = \"med75_adj\", min_pat_pool = 0.2)"},{"path":"https://openpharma.github.io/simaerep/reference/get_visit_med75.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get visit_med75. — get_visit_med75","text":"df_pat dataframe returned pat_aggr() method character, one c(\"med75\", \"med75_adj\") defining method defining evaluation point visit_med75 (see details), Default: \"med75_adj\" min_pat_pool double, minimum ratio available patients available sampling. Determines maximum visit_med75 value see Details. Default: 0.2","code":""},{"path":"https://openpharma.github.io/simaerep/reference/get_visit_med75.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get visit_med75. — get_visit_med75","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_orivisit.html","id":null,"dir":"Reference","previous_headings":"","what":"is orivisit class — is_orivisit","title":"is orivisit class — is_orivisit","text":"internal function","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_orivisit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"is orivisit class — is_orivisit","text":"","code":"is_orivisit(x)"},{"path":"https://openpharma.github.io/simaerep/reference/is_orivisit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"is orivisit class — is_orivisit","text":"x object","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_orivisit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"is orivisit class — is_orivisit","text":"logical","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_simaerep.html","id":null,"dir":"Reference","previous_headings":"","what":"is simaerep class — is_simaerep","title":"is simaerep class — is_simaerep","text":"internal function","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_simaerep.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"is simaerep class — is_simaerep","text":"","code":"is_simaerep(x)"},{"path":"https://openpharma.github.io/simaerep/reference/is_simaerep.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"is simaerep class — is_simaerep","text":"x object","code":""},{"path":"https://openpharma.github.io/simaerep/reference/is_simaerep.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"is simaerep class — is_simaerep","text":"logical","code":""},{"path":"https://openpharma.github.io/simaerep/reference/max_rank.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate Max Rank — max_rank","title":"Calculate Max Rank — max_rank","text":"like rank() ties.method = \"max\", works tbl objects","code":""},{"path":"https://openpharma.github.io/simaerep/reference/max_rank.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate Max Rank — max_rank","text":"","code":"max_rank(df, col, col_new)"},{"path":"https://openpharma.github.io/simaerep/reference/max_rank.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate Max Rank — max_rank","text":"df dataframe col character column name rank y col_new character column name rankings","code":""},{"path":"https://openpharma.github.io/simaerep/reference/max_rank.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Calculate Max Rank — max_rank","text":"needed hochberg p value adjustment. need assign higher rank multiple sites p value","code":""},{"path":"https://openpharma.github.io/simaerep/reference/max_rank.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate Max Rank — max_rank","text":"","code":"df <- tibble::tibble(s = c(1, 2, 2, 2, 5, 10)) %>% dplyr::mutate( rank = rank(s, ties.method = \"max\") ) df %>% max_rank(\"s\", \"max_rank\") #> # A tibble: 6 × 3 #> s rank max_rank #> #> 1 1 1 1 #> 2 2 4 4 #> 3 2 4 4 #> 4 2 4 4 #> 5 5 5 5 #> 6 10 6 6 # \\donttest{ # Database con <- DBI::dbConnect(duckdb::duckdb(), dbdir = \":memory:\") dplyr::copy_to(con, df, \"df\") max_rank(dplyr::tbl(con, \"df\"), \"s\", \"max_rank\") #> # Source: SQL [6 x 3] #> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] #> s rank max_rank #> #> 1 10 6 6 #> 2 2 4 4 #> 3 2 4 4 #> 4 2 4 4 #> 5 1 1 1 #> 6 5 5 5 DBI::dbDisconnect(con) # }"},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":null,"dir":"Reference","previous_headings":"","what":"create orivisit object — orivisit","title":"create orivisit object — orivisit","text":"Internal S3 object, stores lazy reference original visit data.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"create orivisit object — orivisit","text":"","code":"orivisit(df_visit, call = NULL, env = parent.frame())"},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"create orivisit object — orivisit","text":"df_visit dataframe original visit data call optional, provide call, Default: NULL env optional, provide environment original visit data, Default: parent.frame()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"create orivisit object — orivisit","text":"orivisit object","code":""},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"create orivisit object — orivisit","text":"Saves variable name original visit data, checks whether can retrieved parent environment stores summary. Original data can retrieved using .data.frame(x).","code":""},{"path":"https://openpharma.github.io/simaerep/reference/orivisit.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"create orivisit object — orivisit","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" visit <- orivisit(df_visit) object.size(df_visit) #> 125288 bytes object.size(visit) #> 2288 bytes as.data.frame(visit) #> # A tibble: 1,956 × 9 #> patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit #> #> 1 P000001 S0001 TRUE 20 4 0.2 1 #> 2 P000001 S0001 TRUE 20 4 0.2 2 #> 3 P000001 S0001 TRUE 20 4 0.2 3 #> 4 P000001 S0001 TRUE 20 4 0.2 4 #> 5 P000001 S0001 TRUE 20 4 0.2 5 #> 6 P000001 S0001 TRUE 20 4 0.2 6 #> 7 P000001 S0001 TRUE 20 4 0.2 7 #> 8 P000001 S0001 TRUE 20 4 0.2 8 #> 9 P000001 S0001 TRUE 20 4 0.2 9 #> 10 P000001 S0001 TRUE 20 4 0.2 10 #> # ℹ 1,946 more rows #> # ℹ 2 more variables: n_ae , study_id "},{"path":"https://openpharma.github.io/simaerep/reference/p_adjust_bh_inframe.html","id":null,"dir":"Reference","previous_headings":"","what":"benjamini hochberg p value correction using table operations — p_adjust_bh_inframe","title":"benjamini hochberg p value correction using table operations — p_adjust_bh_inframe","text":"benjamini hochberg p value correction using table operations","code":""},{"path":"https://openpharma.github.io/simaerep/reference/p_adjust_bh_inframe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"benjamini hochberg p value correction using table operations — p_adjust_bh_inframe","text":"","code":"p_adjust_bh_inframe(df_eval, col, suffix)"},{"path":"https://openpharma.github.io/simaerep/reference/pat_aggr.html","id":null,"dir":"Reference","previous_headings":"","what":"Aggregate visit to patient level. — pat_aggr","title":"Aggregate visit to patient level. — pat_aggr","text":"Internal function used site_aggr() plot_visit_med75(), adds maximum visit patient.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_aggr.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Aggregate visit to patient level. — pat_aggr","text":"","code":"pat_aggr(df_visit)"},{"path":"https://openpharma.github.io/simaerep/reference/pat_aggr.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Aggregate visit to patient level. — pat_aggr","text":"df_visit dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_aggr.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Aggregate visit to patient level. — pat_aggr","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_pool.html","id":null,"dir":"Reference","previous_headings":"","what":"Create a study specific patient pool for sampling — pat_pool","title":"Create a study specific patient pool for sampling — pat_pool","text":"Internal function sim_sites, filter visits greater max_visit_med75_study returns dataframe one column studies one column nested patient data.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_pool.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create a study specific patient pool for sampling — pat_pool","text":"","code":"pat_pool(df_visit, df_site)"},{"path":"https://openpharma.github.io/simaerep/reference/pat_pool.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create a study specific patient pool for sampling — pat_pool","text":"df_visit dataframe, created sim_sites df_site dataframe created site_aggr","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_pool.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create a study specific patient pool for sampling — pat_pool","text":"dataframe nested pat_pool column","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pat_pool.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Create a study specific patient pool for sampling — pat_pool","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_pat_pool <- pat_pool(df_visit, df_site) df_pat_pool #> # A tibble: 1 × 2 #> study_id pat_pool #> #> 1 A "},{"path":"https://openpharma.github.io/simaerep/reference/pipe.html","id":null,"dir":"Reference","previous_headings":"","what":"Pipe operator — %>%","title":"Pipe operator — %>%","text":"See magrittr::%>% details.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/pipe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Pipe operator — %>%","text":"","code":"lhs %>% rhs"},{"path":"https://openpharma.github.io/simaerep/reference/pipe.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Pipe operator — %>%","text":"returns output rhs function","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":null,"dir":"Reference","previous_headings":"","what":"plot AE under-reporting simulation results — plot.simaerep","title":"plot AE under-reporting simulation results — plot.simaerep","text":"generic plot function simaerep objects","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"plot AE under-reporting simulation results — plot.simaerep","text":"","code":"# S3 method for class 'simaerep' plot( x, ..., study = NULL, what = \"ur\", n_sites = 16, df_visit = NULL, env = parent.frame() )"},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"plot AE under-reporting simulation results — plot.simaerep","text":"x simaerep object ... additional parameters passed plot_study() plot_visit_med75() study character specifying study plotted, Default: NULL one c(\"ur\", \"med75\"), specifying whether plot site AE -reporting visit_med75 values, Default: 'ur' n_sites number sites plot, Default: 16 df_visit optional, pass original visit data retrieved parent environment, Default: NULL env optional, pass environment retrieve original visit data, Default: parent.frame()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"plot AE under-reporting simulation results — plot.simaerep","text":"ggplot object","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"plot AE under-reporting simulation results — plot.simaerep","text":"see plot_study() plot_visit_med75()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot.simaerep.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"plot AE under-reporting simulation results — plot.simaerep","text":"","code":"# \\donttest{ df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" aerep <- simaerep(df_visit) plot(aerep, what = \"ur\", study = \"A\") plot(aerep, what = \"med75\", study = \"A\") #> purple line: mean site ae of patients with visit_med75 #> grey line: patient included #> black dashed line: patient excluded #> dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site #> solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients #> dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study #> # }"},{"path":"https://openpharma.github.io/simaerep/reference/plot_dots.html","id":null,"dir":"Reference","previous_headings":"","what":"Plots AE per site as dots. — plot_dots","title":"Plots AE per site as dots. — plot_dots","text":"plot meant supplement package documentation.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_dots.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plots AE per site as dots. — plot_dots","text":"","code":"plot_dots( df, nrow = 10, ncols = 10, col_group = \"site\", thresh = NULL, color_site_a = \"#BDBDBD\", color_site_b = \"#757575\", color_site_c = \"gold3\", color_high = \"#00695C\", color_low = \"#25A69A\", size_dots = 10 )"},{"path":"https://openpharma.github.io/simaerep/reference/plot_dots.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plots AE per site as dots. — plot_dots","text":"df dataframe, cols = c('site', 'patients', 'n_ae') nrow integer, number rows, Default: 10 ncols integer, number columns, Default: 10 col_group character, grouping column, Default: 'site' thresh numeric, threshold determine color mean_ae annotation, Default: NULL color_site_a character, hex color value, Default: '#BDBDBD' color_site_b character, hex color value, Default: '#757575' color_site_c character, hex color value, Default: 'gold3' color_high character, hex color value, Default: '#00695C' color_low character, hex color value, Default: '#25A69A' size_dots integer, Default: 10","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_dots.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plots AE per site as dots. — plot_dots","text":"ggplot object","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_dots.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plots AE per site as dots. — plot_dots","text":"","code":"study <- tibble::tibble( site = LETTERS[1:3], patients = c(list(seq(1, 50, 1)), list(seq(1, 40, 1)), list(seq(1, 10, 1))) ) %>% tidyr::unnest(patients) %>% dplyr::mutate(n_ae = as.integer(runif(min = 0, max = 10, n = nrow(.)))) plot_dots(study)"},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot simulation example. — plot_sim_example","title":"Plot simulation example. — plot_sim_example","text":"plots supplements package documentation.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot simulation example. — plot_sim_example","text":"","code":"plot_sim_example( substract_ae_per_pat = 0, size_dots = 10, size_raster_label = 12, color_site_a = \"#BDBDBD\", color_site_b = \"#757575\", color_site_c = \"gold3\", color_high = \"#00695C\", color_low = \"#25A69A\", title = TRUE, legend = TRUE, seed = 5 )"},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot simulation example. — plot_sim_example","text":"substract_ae_per_pat integer, subtract aes patients site C, Default: 0 size_dots integer, Default: 10 size_raster_label integer, Default: 12 color_site_a character, hex color value, Default: '#BDBDBD' color_site_b character, hex color value, Default: '#757575' color_site_c character, hex color value, Default: 'gold3' color_high character, hex color value, Default: '#00695C' color_low character, hex color value, Default: '#25A69A' title logical, include title, Default: T legend logical, include legend, Default: T seed pass seed simulations Default: 5","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot simulation example. — plot_sim_example","text":"ggplot","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Plot simulation example. — plot_sim_example","text":"uses plot_dots() adds 2 simulation panels, uses made-site config three sites ,B,C simulating site C","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_example.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plot simulation example. — plot_sim_example","text":"","code":"# \\donttest{ plot_sim_example(size_dots = 5) # }"},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot multiple simulation examples. — plot_sim_examples","title":"Plot multiple simulation examples. — plot_sim_examples","text":"plot meant supplement package documentation.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot multiple simulation examples. — plot_sim_examples","text":"","code":"plot_sim_examples(substract_ae_per_pat = c(0, 1, 3), ...)"},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot multiple simulation examples. — plot_sim_examples","text":"substract_ae_per_pat integer, Default: c(0, 1, 3) ... parameters passed plot_sim_example()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot multiple simulation examples. — plot_sim_examples","text":"ggplot","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Plot multiple simulation examples. — plot_sim_examples","text":"function wrapper plot_sim_example()","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/plot_sim_examples.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plot multiple simulation examples. — plot_sim_examples","text":"","code":"# \\donttest{ plot_sim_examples(size_dot = 3, size_raster_label = 10) plot_sim_examples() # }"},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot ae development of study and sites highlighting at risk sites. — plot_study","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"suitable visual representation AE -reporting statistics.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"","code":"plot_study( df_visit, df_site, df_eval, study, df_al = NULL, n_sites = 16, pval = FALSE, prob_col = \"prob_low_prob_ur\" )"},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"df_visit dataframe, created sim_sites() df_site dataframe created site_aggr() df_eval dataframe created eval_sites() study study df_al dataframe containing study_id, site_number, alert_level_site, alert_level_study (optional), Default: NA n_sites integer number risk sites, Default: 16 pval logical show p-value, Default:FALSE prob_col character, denotes probability column, Default: \"prob_low_prob_ur\"","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"ggplot","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"Left panel shows mean AE reporting per site (lightblue darkblue lines) mean AE reporting entire study (golden line). Single sites plotted descending order AE -reporting probability right panel grey lines denote cumulative AE count single patients. Grey dots left panel plot indicate sites picked single plotting. AE -reporting probability dark blue lines crossed threshold 95%. Numbers upper left corner indicate ratio patients used analysis total number patients. Patients study long enough reach evaluation point (visit_med75) ignored.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_study.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plot ae development of study and sites highlighting at risk sites. — plot_study","text":"","code":"# \\donttest{ df_visit <- sim_test_data_study(n_pat = 1000, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.15, max_visit_sd = 8) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit, r = 100) df_eval <- eval_sites(df_sim_sites) plot_study(df_visit, df_site, df_eval, study = \"A\") # }"},{"path":"https://openpharma.github.io/simaerep/reference/plot_visit_med75.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot patient visits against visit_med75. — plot_visit_med75","title":"Plot patient visits against visit_med75. — plot_visit_med75","text":"Plots cumulative AEs visits patients sites given study compares visit_med75.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_visit_med75.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot patient visits against visit_med75. — plot_visit_med75","text":"","code":"plot_visit_med75( df_visit, df_site = NULL, study_id_str, n_sites = 6, min_pat_pool = 0.2, verbose = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/plot_visit_med75.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot patient visits against visit_med75. — plot_visit_med75","text":"df_visit dataframe df_site dataframe, returned site_aggr() study_id_str character, specify study study_id column n_sites integer, Default: 6 min_pat_pool double, minimum ratio available patients available sampling. Determines maximum visit_med75 value see Details. Default: 0.2 verbose logical, Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_visit_med75.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot patient visits against visit_med75. — plot_visit_med75","text":"ggplot","code":""},{"path":"https://openpharma.github.io/simaerep/reference/plot_visit_med75.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plot patient visits against visit_med75. — plot_visit_med75","text":"","code":"df_visit <- sim_test_data_study(n_pat = 120, n_sites = 6, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) plot_visit_med75(df_visit, df_site, study_id_str = \"A\", n_site = 6) #> purple line: mean site ae of patients with visit_med75 #> grey line: patient included #> black dashed line: patient excluded #> dotted vertical line: visit_med75, 0.75 x median of maximum patient visits of site #> solid vertical line: visit_med75 adjusted, increased to minimum maximum patient visit of included patients #> dashed vertical line: maximum value for visit_med75 adjusted, 80% quantile of maximum patient visits of study #>"},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":null,"dir":"Reference","previous_headings":"","what":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"Internal function used sim_sites().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"","code":"poiss_test_site_ae_vs_study_ae(site_ae, study_ae, visit_med75)"},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"site_ae vector AE numbers study_ae vector AE numbers visit_med75 integer","code":""},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"pval","code":""},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"sets pvalue=1 mean AE site greater mean AE study ttest gives error","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Poisson test for vector with site AEs vs vector with study AEs. — poiss_test_site_ae_vs_study_ae","text":"","code":"poiss_test_site_ae_vs_study_ae( site_ae = c(5, 3, 3, 2, 1, 6), study_ae = c(9, 8, 7, 9, 6, 7, 8), visit_med75 = 10 ) #> [1] 0.0009782833 poiss_test_site_ae_vs_study_ae( site_ae = c(11, 9, 8, 6, 3), study_ae = c(9, 8, 7, 9, 6, 7, 8), visit_med75 = 10 ) #> [1] 0.9154536"},{"path":"https://openpharma.github.io/simaerep/reference/prep_for_sim.html","id":null,"dir":"Reference","previous_headings":"","what":"Prepare data for simulation. — prep_for_sim","title":"Prepare data for simulation. — prep_for_sim","text":"Internal function called sim_sites. Collect AEs per patient visit_med75 site study vector integers.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prep_for_sim.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Prepare data for simulation. — prep_for_sim","text":"","code":"prep_for_sim(df_site, df_visit)"},{"path":"https://openpharma.github.io/simaerep/reference/prep_for_sim.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Prepare data for simulation. — prep_for_sim","text":"df_site dataframe created site_aggr df_visit dataframe, created sim_sites","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prep_for_sim.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Prepare data for simulation. — prep_for_sim","text":"dataframe","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/prep_for_sim.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Prepare data for simulation. — prep_for_sim","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.2 ) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_prep <- prep_for_sim(df_site, df_visit) df_prep #> # A tibble: 5 × 7 #> study_id site_number n_pat n_pat_with_med75 visit_med75 n_ae_site n_ae_study #> #> 1 A S0001 20 18 15 #> 2 A S0002 20 16 16 #> 3 A S0003 20 19 15 #> 4 A S0004 20 18 14 #> 5 A S0005 20 17 15 "},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"Internal function used sim_sites()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"","code":"prob_lower_site_ae_vs_study_ae( site_ae, study_ae, r = 1000, parallel = FALSE, under_only = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"site_ae vector AE numbers study_ae vector AE numbers r integer, denotes number simulations, default = 1000 parallel logical, toggles parallel processing , default = F under_only compute -reporting probabilities , default = TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"pval","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"sets pvalue=1 mean AE site greater mean AE study","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate bootstrapped probability for obtaining a lower site mean AE number. — prob_lower_site_ae_vs_study_ae","text":"","code":"prob_lower_site_ae_vs_study_ae( site_ae = c(5, 3, 3, 2, 1, 6), study_ae = c(9, 8, 7, 9, 6, 7, 8), parallel = FALSE ) #> [1] 0.011"},{"path":"https://openpharma.github.io/simaerep/reference/prune_to_visit_med75_inframe.html","id":null,"dir":"Reference","previous_headings":"","what":"prune visits to visit_med75 using table operations — prune_to_visit_med75_inframe","title":"prune visits to visit_med75 using table operations — prune_to_visit_med75_inframe","text":"prune visits visit_med75 using table operations","code":""},{"path":"https://openpharma.github.io/simaerep/reference/prune_to_visit_med75_inframe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"prune visits to visit_med75 using table operations — prune_to_visit_med75_inframe","text":"","code":"prune_to_visit_med75_inframe(df_visit, df_site)"},{"path":"https://openpharma.github.io/simaerep/reference/prune_to_visit_med75_inframe.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"prune visits to visit_med75 using table operations — prune_to_visit_med75_inframe","text":"df_visit Data frame columns: study_id, site_number, patnum, visit, n_ae. df_site dataframe, returned site_aggr()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":null,"dir":"Reference","previous_headings":"","what":"Execute a purrr or furrr function with a progress bar. — purrr_bar","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"Internal utility function.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"","code":"purrr_bar( ..., .purrr, .f, .f_args = list(), .purrr_args = list(), .steps, .slow = FALSE, .progress = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"... iterable arguments passed .purrr .purrr purrr furrr function .f function executed iterables .f_args list arguments passed .f, Default: list() .purrr_args list arguments passed .purrr, Default: list() .steps integer number iterations .slow logical slows execution, Default: FALSE .progress logical, show progress bar, Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"result function passed .f","code":""},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"Call still needs wrapped with_progress with_progress_cnd()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/purrr_bar.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Execute a purrr or furrr function with a progress bar. — purrr_bar","text":"","code":"# purrr::map progressr::with_progress( purrr_bar(rep(0.25, 5), .purrr = purrr::map, .f = Sys.sleep, .steps = 5) ) #> [[1]] #> NULL #> #> [[2]] #> NULL #> #> [[3]] #> NULL #> #> [[4]] #> NULL #> #> [[5]] #> NULL #> # \\donttest{ # purrr::walk progressr::with_progress( purrr_bar(rep(0.25, 5), .purrr = purrr::walk,.f = Sys.sleep, .steps = 5) ) # progress bar off progressr::with_progress( purrr_bar( rep(0.25, 5), .purrr = purrr::walk,.f = Sys.sleep, .steps = 5, .progress = FALSE ) ) # purrr::map2 progressr::with_progress( purrr_bar( rep(1, 5), rep(2, 5), .purrr = purrr::map2, .f = `+`, .steps = 5, .slow = TRUE ) ) #> [[1]] #> [1] 3 #> #> [[2]] #> [1] 3 #> #> [[3]] #> [1] 3 #> #> [[4]] #> [1] 3 #> #> [[5]] #> [1] 3 #> # purrr::pmap progressr::with_progress( purrr_bar( list(rep(1, 5), rep(2, 5)), .purrr = purrr::pmap, .f = `+`, .steps = 5, .slow = TRUE ) ) #> [[1]] #> [1] 3 #> #> [[2]] #> [1] 3 #> #> [[3]] #> [1] 3 #> #> [[4]] #> [1] 3 #> #> [[5]] #> [1] 3 #> # define function within purr_bar() call progressr::with_progress( purrr_bar( list(rep(1, 5), rep(2, 5)), .purrr = purrr::pmap, .f = function(x, y) { paste0(x, y) }, .steps = 5, .slow = TRUE ) ) #> [[1]] #> [1] \"12\" #> #> [[2]] #> [1] \"12\" #> #> [[3]] #> [1] \"12\" #> #> [[4]] #> [1] \"12\" #> #> [[5]] #> [1] \"12\" #> # with mutate progressr::with_progress( tibble::tibble(x = rep(0.25, 5)) %>% dplyr::mutate(x = purrr_bar(x, .purrr = purrr::map, .f = Sys.sleep, .steps = 5)) ) #> # A tibble: 5 × 1 #> x #> #> 1 #> 2 #> 3 #> 4 #> 5 # }"},{"path":"https://openpharma.github.io/simaerep/reference/sim_after_prep.html","id":null,"dir":"Reference","previous_headings":"","what":"Start simulation after preparation. — sim_after_prep","title":"Start simulation after preparation. — sim_after_prep","text":"Internal function called sim_sites prep_for_sim","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_after_prep.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Start simulation after preparation. — sim_after_prep","text":"","code":"sim_after_prep( df_sim_prep, r = 1000, poisson_test = FALSE, prob_lower = TRUE, progress = FALSE, under_only = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_after_prep.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Start simulation after preparation. — sim_after_prep","text":"df_sim_prep dataframe returned prep_for_sim r integer, denotes number simulations, default = 1000 poisson_test logical, calculates poisson.test pvalue prob_lower logical, calculates probability getting lower value progress logical, display progress bar, Default = TRUE under_only compute -reporting probabilities , default = TRUE check_df_visit(), computationally expensive large data sets. Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_after_prep.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Start simulation after preparation. — sim_after_prep","text":"dataframe","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/sim_after_prep.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Start simulation after preparation. — sim_after_prep","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.2 ) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_prep <- prep_for_sim(df_site, df_visit) df_sim <- sim_after_prep(df_prep) df_sim #> # A tibble: 5 × 9 #> study_id site_number n_pat n_pat_with_med75 visit_med75 mean_ae_site_med75 #> #> 1 A S0001 20 20 15 6.1 #> 2 A S0002 20 17 16 6.53 #> 3 A S0003 20 17 16 8.35 #> 4 A S0004 20 20 15 6.55 #> 5 A S0005 20 16 15 7.62 #> # ℹ 3 more variables: mean_ae_study_med75 , n_pat_with_med75_study , #> # prob_low "},{"path":"https://openpharma.github.io/simaerep/reference/sim_inframe.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate prob_lower for study sites using table operations — sim_inframe","title":"Calculate prob_lower for study sites using table operations — sim_inframe","text":"Calculate prob_lower study sites using table operations","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_inframe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate prob_lower for study sites using table operations — sim_inframe","text":"","code":"sim_inframe(df_visit, r = 1000, df_site = NULL)"},{"path":"https://openpharma.github.io/simaerep/reference/sim_inframe.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate prob_lower for study sites using table operations — sim_inframe","text":"df_visit Data frame columns: study_id, site_number, patnum, visit, n_ae. r Integer tbl_object, number repetitions bootstrap simulation. Pass tbl object referring table one column many rows desired repetitions. Default: 1000. df_site, dataframe returned site_aggr(), switch visit_med75. Default: NULL","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_inframe.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate prob_lower for study sites using table operations — sim_inframe","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" df_sim <- sim_inframe(df_visit) df_eval <- eval_sites(df_sim) df_eval #> # A tibble: 5 × 10 #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0001 0.188 75 400 20 0 #> 2 A S0002 0.186 75 403 20 0 #> 3 A S0003 0.496 187 377 20 0.998 #> 4 A S0004 0.453 180 397 20 0.98 #> 5 A S0005 0.492 187 380 20 0.998 #> # ℹ 3 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur "},{"path":"https://openpharma.github.io/simaerep/reference/sim_scenario.html","id":null,"dir":"Reference","previous_headings":"","what":"simulate single scenario — sim_scenario","title":"simulate single scenario — sim_scenario","text":"internal function called simulate_scenarios()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_scenario.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"simulate single scenario — sim_scenario","text":"","code":"sim_scenario(n_ae_site, n_ae_study, frac_pat_with_ur, ur_rate)"},{"path":"https://openpharma.github.io/simaerep/reference/sim_scenario.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"simulate single scenario — sim_scenario","text":"n_ae_site integer vector n_ae_study integer vector frac_pat_with_ur double ur_rate double","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_scenario.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"simulate single scenario — sim_scenario","text":"list","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_scenario.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"simulate single scenario — sim_scenario","text":"","code":"sim_scenario(c(5,5,5,5), c(8,8,8,8), 0.2, 0.5) #> $n_ae_site #> [1] 2.5 2.5 5.0 5.0 #> #> $n_ae_study #> [1] 8 8 8 8 #> sim_scenario(c(5,5,5,5), c(8,8,8,8), 0.75, 0.5) #> $n_ae_site #> [1] 2.5 2.5 2.5 2.5 #> #> $n_ae_study #> [1] 4 4 8 8 #> sim_scenario(c(5,5,5,5), c(8,8,8,8), 1, 0.5) #> $n_ae_site #> [1] 2.5 2.5 2.5 2.5 #> #> $n_ae_study #> [1] 4 4 4 4 #> sim_scenario(c(5,5,5,5), c(8,8,8,8), 1, 1) #> $n_ae_site #> [1] 0 0 0 0 #> #> $n_ae_study #> [1] 0 0 0 0 #> sim_scenario(c(5,5,5,5), c(8,8,8,8), 0, 0.5) #> $n_ae_site #> [1] 5 5 5 5 #> #> $n_ae_study #> [1] 8 8 8 8 #> sim_scenario(c(5,5,5,5), c(8,8,8,8), 2, 0.5) #> $n_ae_site #> [1] 2.5 2.5 2.5 2.5 #> #> $n_ae_study #> [1] 4 4 4 4 #>"},{"path":"https://openpharma.github.io/simaerep/reference/sim_sites.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","title":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","text":"Collects number AEs eligible patients meet visit_med75 criteria site. calculates poisson.test pvalue bootstrapped probability lower mean value.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_sites.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","text":"","code":"sim_sites( df_site, df_visit, r = 1000, poisson_test = TRUE, prob_lower = TRUE, progress = TRUE, check = TRUE, under_only = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_sites.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","text":"df_site dataframe created site_aggr df_visit dataframe, created sim_sites r integer, denotes number simulations, default = 1000 poisson_test logical, calculates poisson.test pvalue prob_lower logical, calculates probability getting lower value progress logical, display progress bar, Default = TRUE check, logical, perform data check attempt repair under_only compute -reporting probabilities , default = TRUE check_df_visit(), computationally expensive large data sets. Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_sites.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","text":"dataframe following columns: study_id study identification site_number site identification n_pat number patients site visit_med75 median(max(visit)) * 0.75 n_pat_with_med75 number patients site med75 mean_ae_site_med75 mean AE visit_med75 site level mean_ae_study_med75 mean AE visit_med75 study level n_pat_with_med75_study number patients study med75 excl. site pval p-value returned poisson.test prob_low bootstrapped probability mean_ae_site_med75 lower","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/sim_sites.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate prob_lower and poisson.test pvalue for study sites. — sim_sites","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.2 ) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_sim_sites <- sim_sites(df_site, df_visit, r = 100) df_sim_sites %>% knitr::kable(digits = 2) #> #> #> |study_id |site_number | n_pat| n_pat_with_med75| visit_med75| mean_ae_site_med75| mean_ae_study_med75| n_pat_with_med75_study| pval| prob_low| #> |:--------|:-----------|-----:|----------------:|-----------:|------------------:|-------------------:|----------------------:|----:|--------:| #> |A |S0001 | 20| 20| 15| 6.35| 6.31| 71| 1.00| 1.00| #> |A |S0002 | 20| 19| 15| 5.37| 6.57| 72| 0.06| 0.06| #> |A |S0003 | 20| 18| 15| 7.11| 6.12| 73| 1.00| 1.00| #> |A |S0004 | 20| 15| 17| 7.87| 7.03| 61| 1.00| 1.00| #> |A |S0005 | 20| 19| 15| 6.37| 6.31| 72| 1.00| 1.00|"},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":null,"dir":"Reference","previous_headings":"","what":"Simulate studies. — sim_studies","title":"Simulate studies. — sim_studies","text":"Test function, test applicability poisson test, calculating bootstrapped probability obtaining specific p-value lower, use combination get_ecd_values().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simulate studies. — sim_studies","text":"","code":"sim_studies( df_visit, df_site, r = 100, poisson_test = TRUE, prob_lower = TRUE, r_prob_lower = 1000, under_only = TRUE, parallel = FALSE, keep_ae = FALSE, min_n_pat_with_med75 = 1, studies = NULL, .progress = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simulate studies. — sim_studies","text":"df_visit dataframe df_site dataframe r integer, denotes number simulations, Default: 1000 poisson_test logical, calculates poisson.test pvalue, Default: TRUE prob_lower logical, calculates probability getting lower value, Default: FALSE r_prob_lower integer, denotes number simulations prob_lower value calculation,, Default: 1000 under_only compute -reporting probabilities , default = TRUE parallel logical, see examples registering parallel processing framework , Default: FALSE keep_ae logical, keep ae numbers output dataframe memory increase roughly 30 percent, Default: F min_n_pat_with_med75 integer, min number patients med75 site simulate, Default: 1 studies vector study names, Default: NULL .progress logical, show progress bar","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simulate studies. — sim_studies","text":"dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Simulate studies. — sim_studies","text":"simulate study replicates maintaining number sites, patients visit_med75 bootstrap resampling, probabilities obtaining lower mean_ae count p-values using poisson.test calculated. adds column simulated probabilities equal lower mean_ae visit_med75","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_studies.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Simulate studies. — sim_studies","text":"","code":"# \\donttest{ df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 1000, n_sites = 3, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site <- site_aggr(df_visit) sim_studies(df_visit, df_site, r = 3, keep_ae = TRUE) #> # A tibble: 24 × 10 #> r study_id site_number visit_med75 n_pat_with_med75 n_pat_study n_ae_site #> #> 1 1 A S0001 16 18 68 11,5,7,4… #> 2 1 A S0002 18 17 53 11,16,8,… #> 3 1 A S0003 17 18 62 13,11,13… #> 4 1 A S0004 14 18 74 7,9,7,6,… #> 5 1 A S0005 15 17 71 6,9,5,4,… #> 6 1 B S0001 15 297 597 10,13,11… #> 7 1 B S0002 15 296 598 3,10,4,6… #> 8 1 B S0003 15 301 593 3,6,7,10… #> 9 2 A S0001 16 18 68 10,5,7,3… #> 10 2 A S0002 18 17 53 5,17,8,6… #> # ℹ 14 more rows #> # ℹ 3 more variables: n_ae_study , pval , prob_low # } if (FALSE) { # \\dontrun{ # parallel processing ------------------------- library(future) future::plan(multiprocess) sim_studies(df_visit, df_site, r = 3, keep_ae = TRUE, parallel = TRUE) future::plan(sequential) } # }"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":null,"dir":"Reference","previous_headings":"","what":"simulate patient ae reporting test data — sim_test_data_patient","title":"simulate patient ae reporting test data — sim_test_data_patient","text":"helper function sim_test_data_study()","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"simulate patient ae reporting test data — sim_test_data_patient","text":"","code":"sim_test_data_patient( .f_sample_max_visit = function() rnorm(1, mean = 20, sd = 4), .f_sample_ae_per_visit = function(max_visit) rpois(max_visit, 0.5) )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"simulate patient ae reporting test data — sim_test_data_patient","text":".f_sample_max_visit function used sample maximum number aes, Default: function() rnorm(1, mean = 20, sd = 4) .f_sample_ae_per_visit function used sample aes visit, Default: function(x) rpois(x, 0.5)","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"simulate patient ae reporting test data — sim_test_data_patient","text":"vector containing cumulative aes","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"simulate patient ae reporting test data — sim_test_data_patient","text":"\"\"","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"simulate patient ae reporting test data — sim_test_data_patient","text":"","code":"replicate(5, sim_test_data_patient()) #> [[1]] #> [1] 1 2 3 3 4 4 4 4 4 4 5 5 5 7 8 9 10 10 11 11 12 14 14 #> #> [[2]] #> [1] 2 2 3 4 5 5 5 6 6 6 7 7 8 9 9 11 11 11 11 11 11 11 12 12 #> #> [[3]] #> [1] 2 4 5 7 7 8 9 11 11 11 12 12 13 14 14 15 15 15 15 15 15 #> #> [[4]] #> [1] 0 0 1 2 3 3 3 3 4 5 5 5 5 5 #> #> [[5]] #> [1] 1 1 1 1 1 1 2 2 4 4 4 4 6 6 7 7 7 8 9 11 12 12 #> replicate(5, sim_test_data_patient( .f_sample_ae_per_visit = function(x) rpois(x, 1.2)) ) #> [[1]] #> [1] 1 3 3 5 6 6 6 6 8 8 9 13 15 15 16 16 #> #> [[2]] #> [1] 2 3 4 4 4 8 9 9 11 11 11 13 15 16 16 18 22 23 23 24 25 #> #> [[3]] #> [1] 2 2 3 3 3 3 5 8 9 11 13 14 16 16 17 19 21 23 23 23 24 #> #> [[4]] #> [1] 2 4 5 5 9 9 10 11 12 16 16 20 21 22 24 25 27 29 31 34 36 37 41 41 45 #> [26] 47 #> #> [[5]] #> [1] 0 1 1 2 3 3 3 4 4 5 5 5 5 7 9 11 13 15 16 16 16 17 #> replicate(5, sim_test_data_patient( .f_sample_max_visit = function() rnorm(1, mean = 5, sd = 5)) ) #> [[1]] #> [1] 0 2 2 4 4 4 #> #> [[2]] #> [1] 1 #> #> [[3]] #> [1] 0 0 0 #> #> [[4]] #> [1] 3 3 3 3 4 4 4 4 4 4 #> #> [[5]] #> [1] 1 1 2 3 3 3 3 3 3 3 3 4 4 4 4 4 6 #>"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":null,"dir":"Reference","previous_headings":"","what":"Simulate Portfolio Test Data — sim_test_data_portfolio","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"Simulate visit level data portfolio configuration.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"","code":"sim_test_data_portfolio( df_config, df_ae_rates = NULL, parallel = FALSE, progress = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"df_config dataframe returned get_config df_ae_rates dataframe ae rates. Default: NULL parallel logical activate parallel processing, see details, Default: FALSE progress logical, Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"dataframe following columns: study_id study identification ae_per_visit_mean mean AE per visit per study site_number site max_visit_sd standard deviation maximum patient visits per site max_visit_mean mean maximum patient visits per site patnum number patients visit visit number n_ae cumulative sum AEs","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"uses sim_test_data_study. use furrr package implement parallel processing simulations can take long time run. work need specify plan code run, e.g. `plan(multisession, workers = 3)","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Simulate Portfolio Test Data — sim_test_data_portfolio","text":"","code":"# \\donttest{ df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site_max <- df_visit %>% dplyr::group_by(study_id, site_number, patnum) %>% dplyr::summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config <- get_config(df_site_max) df_config #> # A tibble: 20 × 6 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean n_pat #> #> 1 0001 0.355 0001 4.23 19.9 10 #> 2 0001 0.355 0002 4.13 19.8 10 #> 3 0001 0.355 0003 5.03 19.3 10 #> 4 0001 0.355 0004 4.40 20 10 #> 5 0001 0.355 0005 3.77 20.3 10 #> 6 0001 0.355 0006 3.58 17.2 10 #> 7 0001 0.355 0007 5.10 19.6 10 #> 8 0001 0.355 0008 3.74 19.2 10 #> 9 0001 0.355 0009 4.40 19.6 10 #> 10 0001 0.355 0010 4.50 19.4 10 #> 11 0002 0.505 0001 1.51 17.6 10 #> 12 0002 0.505 0002 2.99 20.5 10 #> 13 0002 0.505 0003 3.59 19.7 10 #> 14 0002 0.505 0004 2.70 17.8 10 #> 15 0002 0.505 0005 4.72 21.1 10 #> 16 0002 0.505 0006 5.70 18.9 10 #> 17 0002 0.505 0007 2.68 20.5 10 #> 18 0002 0.505 0008 4.28 19.1 10 #> 19 0002 0.505 0009 3.44 18.5 10 #> 20 0002 0.505 0010 4.86 20.1 10 df_portf <- sim_test_data_portfolio(df_config) df_portf #> # A tibble: 3,729 × 8 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean patnum #> #> 1 0001 0.355 0001 4.23 19.9 0001 #> 2 0001 0.355 0001 4.23 19.9 0001 #> 3 0001 0.355 0001 4.23 19.9 0001 #> 4 0001 0.355 0001 4.23 19.9 0001 #> 5 0001 0.355 0001 4.23 19.9 0001 #> 6 0001 0.355 0001 4.23 19.9 0001 #> 7 0001 0.355 0001 4.23 19.9 0001 #> 8 0001 0.355 0001 4.23 19.9 0001 #> 9 0001 0.355 0001 4.23 19.9 0001 #> 10 0001 0.355 0001 4.23 19.9 0001 #> # ℹ 3,719 more rows #> # ℹ 2 more variables: visit , n_ae df_scen <- sim_ur_scenarios(df_portf, extra_ur_sites = 2, ur_rate = c(0.5, 1)) #> aggregating site level #> prepping for simulation #> generating scenarios #> getting under-reporting stats #> evaluating stats df_scen #> # A tibble: 140 × 14 #> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat #> #> 1 0001 0001 0 0 0 10 #> 2 0001 0001 0 0.222 0.5 10 #> 3 0001 0001 0 0.222 1 10 #> 4 0001 0001 1 0.333 0.5 10 #> 5 0001 0001 1 0.333 1 10 #> 6 0001 0001 2 0.444 0.5 10 #> 7 0001 0001 2 0.444 1 10 #> 8 0001 0002 0 0 0 10 #> 9 0001 0002 0 0.119 0.5 10 #> 10 0001 0002 0 0.119 1 10 #> # ℹ 130 more rows #> # ℹ 8 more variables: n_pat_with_med75 , visit_med75 , #> # mean_ae_site_med75 , mean_ae_study_med75 , #> # n_pat_with_med75_study , prob_low , prob_low_adj , #> # prob_low_prob_ur df_perf <- get_portf_perf(df_scen) df_perf #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> #> 1 0.001 0.555 0 0 0.1 #> 2 0.001 0.555 1 0 0.1 #> 3 0.001 0.555 2 0 0.1 #> 4 0.001 0.555 0 0.5 1 #> 5 0.001 0.555 1 0.5 1 #> 6 0.001 0.555 2 0.5 0.9 #> 7 0.001 0.555 0 1 1 #> 8 0.001 0.555 1 1 1 #> 9 0.001 0.555 2 1 1 #> 10 0.01 0.555 0 0 0.1 #> # ℹ 17 more rows # }"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":null,"dir":"Reference","previous_headings":"","what":"simulate study test data — sim_test_data_study","title":"simulate study test data — sim_test_data_study","text":"evenly distributes number given patients across number given sites. simulates ae development patient reducing number reported AEs patients distributed AE--reporting sites.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"simulate study test data — sim_test_data_study","text":"","code":"sim_test_data_study( n_pat = 1000, n_sites = 20, frac_site_with_ur = 0, ur_rate = 0, max_visit_mean = 20, max_visit_sd = 4, ae_per_visit_mean = 0.5, ae_rates = NULL )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"simulate study test data — sim_test_data_study","text":"n_pat integer, number patients, Default: 1000 n_sites integer, number sites, Default: 20 frac_site_with_ur fraction AE -reporting sites, Default: 0 ur_rate AE -reporting rate, lower mean ae per visit used simulate patients sites flagged AE--reporting. Negative Values simulate -reporting., Default: 0 max_visit_mean mean maximum number visits patient, Default: 20 max_visit_sd standard deviation maximum number visits patient, Default: 4 ae_per_visit_mean mean ae per visit per patient, Default: 0.5 ae_rates vector visit-specific ae rates, Default: Null","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"simulate study test data — sim_test_data_study","text":"tibble columns site_number, patnum, is_ur, max_visit_mean, max_visit_sd, ae_per_visit_mean, visit, n_ae","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"simulate study test data — sim_test_data_study","text":"maximum visit number sampled normal distribution characteristics derived max_visit_mean max_visit_sd, ae per visit sampled poisson distribution described ae_per_visit_mean.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_test_data_study.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"simulate study test data — sim_test_data_study","text":"","code":"set.seed(1) df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5) df_visit[which(df_visit$patnum == \"P000001\"),] #> # A tibble: 17 × 8 #> patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit #> #> 1 P000001 S0001 FALSE 20 4 0.5 1 #> 2 P000001 S0001 FALSE 20 4 0.5 2 #> 3 P000001 S0001 FALSE 20 4 0.5 3 #> 4 P000001 S0001 FALSE 20 4 0.5 4 #> 5 P000001 S0001 FALSE 20 4 0.5 5 #> 6 P000001 S0001 FALSE 20 4 0.5 6 #> 7 P000001 S0001 FALSE 20 4 0.5 7 #> 8 P000001 S0001 FALSE 20 4 0.5 8 #> 9 P000001 S0001 FALSE 20 4 0.5 9 #> 10 P000001 S0001 FALSE 20 4 0.5 10 #> 11 P000001 S0001 FALSE 20 4 0.5 11 #> 12 P000001 S0001 FALSE 20 4 0.5 12 #> 13 P000001 S0001 FALSE 20 4 0.5 13 #> 14 P000001 S0001 FALSE 20 4 0.5 14 #> 15 P000001 S0001 FALSE 20 4 0.5 15 #> 16 P000001 S0001 FALSE 20 4 0.5 16 #> 17 P000001 S0001 FALSE 20 4 0.5 17 #> # ℹ 1 more variable: n_ae df_visit <- sim_test_data_study(n_pat = 100, n_sites = 5, frac_site_with_ur = 0.2, ur_rate = 0.5) df_visit[which(df_visit$patnum == \"P000001\"),] #> # A tibble: 23 × 8 #> patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit #> #> 1 P000001 S0001 TRUE 20 4 0.25 1 #> 2 P000001 S0001 TRUE 20 4 0.25 2 #> 3 P000001 S0001 TRUE 20 4 0.25 3 #> 4 P000001 S0001 TRUE 20 4 0.25 4 #> 5 P000001 S0001 TRUE 20 4 0.25 5 #> 6 P000001 S0001 TRUE 20 4 0.25 6 #> 7 P000001 S0001 TRUE 20 4 0.25 7 #> 8 P000001 S0001 TRUE 20 4 0.25 8 #> 9 P000001 S0001 TRUE 20 4 0.25 9 #> 10 P000001 S0001 TRUE 20 4 0.25 10 #> # ℹ 13 more rows #> # ℹ 1 more variable: n_ae ae_rates <- c(0.7, rep(0.5, 8), rep(0.3, 5)) sim_test_data_study(n_pat = 100, n_sites = 5, ae_rates = ae_rates) #> # A tibble: 1,968 × 8 #> patnum site_number is_ur max_visit_mean max_visit_sd ae_per_visit_mean visit #> #> 1 P000001 S0001 FALSE 20 4 0.443 1 #> 2 P000001 S0001 FALSE 20 4 0.443 2 #> 3 P000001 S0001 FALSE 20 4 0.443 3 #> 4 P000001 S0001 FALSE 20 4 0.443 4 #> 5 P000001 S0001 FALSE 20 4 0.443 5 #> 6 P000001 S0001 FALSE 20 4 0.443 6 #> 7 P000001 S0001 FALSE 20 4 0.443 7 #> 8 P000001 S0001 FALSE 20 4 0.443 8 #> 9 P000001 S0001 FALSE 20 4 0.443 9 #> 10 P000001 S0001 FALSE 20 4 0.443 10 #> # ℹ 1,958 more rows #> # ℹ 1 more variable: n_ae "},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur.html","id":null,"dir":"Reference","previous_headings":"","what":"simulate under-reporting — sim_ur","title":"simulate under-reporting — sim_ur","text":"remove fraction AEs specific site","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"simulate under-reporting — sim_ur","text":"","code":"sim_ur(df_visit, study_id, site_number, ur_rate)"},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"simulate under-reporting — sim_ur","text":"df_visit, dataframe study_id, character site_number, character ur_rate, double","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"simulate under-reporting — sim_ur","text":"determine absolute number AEs per patient removal. remove first visit. intentionally allow fractions","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"simulate under-reporting — sim_ur","text":"","code":"df_visit <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit$study_id <- \"A\" df_ur <- sim_ur(df_visit, \"A\", site_number = \"S0001\", ur_rate = 0.35) # Example cumulated AE for first patient with 35% under-reporting df_ur[df_ur$site_number == \"S0001\" & df_ur$patnum == \"P000001\",]$n_ae #> [1] 0.0 0.6 0.6 1.6 1.6 1.6 1.6 1.6 1.6 2.6 2.6 2.6 2.6 2.6 2.6 2.6 2.6 # Example cumulated AE for first patient with no under-reporting df_visit[df_visit$site_number == \"S0001\" & df_visit$patnum == \"P000001\",]$n_ae #> [1] 1 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4"},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":null,"dir":"Reference","previous_headings":"","what":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"Use simulated portfolio data generate -reporting stats specified scenarios.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"","code":"sim_ur_scenarios( df_portf, extra_ur_sites = 3, ur_rate = c(0.25, 0.5), r = 1000, poisson_test = FALSE, prob_lower = TRUE, parallel = FALSE, progress = TRUE, site_aggr_args = list(), eval_sites_args = list(), check = TRUE )"},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"df_portf dataframe returned sim_test_data_portfolio extra_ur_sites numeric, set maximum number additional -reporting sites, see details Default: 3 ur_rate numeric vector, set -reporting rates scenarios Default: c(0.25, 0.5) r integer, denotes number simulations, default = 1000 poisson_test logical, calculates poisson.test pvalue prob_lower logical, calculates probability getting lower value parallel logical, use parallel processing see details, Default: FALSE progress logical, show progress bar, Default: TRUE site_aggr_args named list parameters passed site_aggr, Default: list() eval_sites_args named list parameters passed eval_sites, Default: list() check logical, perform data check attempt repair ","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"dataframe following columns: study_id study identification site_number site identification n_pat number patients site n_pat_with_med75 number patients site visit_med75 visit_med75 median(max(visit)) * 0.75 mean_ae_site_med75 mean AE visit_med75 site level mean_ae_study_med75 mean AE visit_med75 study level n_pat_with_med75_study number patients site visit_med75 study excl site extra_ur_sites additional sites -reporting patients frac_pat_with_ur ratio patients study -reporting ur_rate -reporting rate pval p-value returned poisson.test prob_low bootstrapped probability mean_ae_site_med75 lower pval_adj adjusted p-values prob_low_adj adjusted bootstrapped probability mean_ae_site_med75 lower pval_prob_ur probability -reporting 1 - pval_adj, poisson.test (use benchmark) prob_low_prob_ur probability -reporting 1 - prob_low_adj, bootstrapped (use)","code":""},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"function apply -reporting scenarios site. Reducing number AEs given -reporting (ur_rate) patients site add corresponding -reporting statistics. Since -reporting probability also affected number sites -reporting additionally calculate -reporting statistics scenario additional reporting sites present. use median number patients per site study calculate final number patients lower AEs given -reporting scenario. use furrr package implement parallel processing simulations can take long time run. work need specify plan code run, e.g. plan(multisession, workers = 18)","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Simulate Under-Reporting Scenarios — sim_ur_scenarios","text":"","code":"# \\donttest{ df_visit1 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.4, ur_rate = 0.6) df_visit1$study_id <- \"A\" df_visit2 <- sim_test_data_study(n_pat = 100, n_sites = 10, frac_site_with_ur = 0.2, ur_rate = 0.1) df_visit2$study_id <- \"B\" df_visit <- dplyr::bind_rows(df_visit1, df_visit2) df_site_max <- df_visit %>% dplyr::group_by(study_id, site_number, patnum) %>% dplyr::summarise(max_visit = max(visit), max_ae = max(n_ae), .groups = \"drop\") df_config <- get_config(df_site_max) df_config #> # A tibble: 20 × 6 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean n_pat #> #> 1 0001 0.381 0001 2.85 19.9 10 #> 2 0001 0.381 0002 3.31 18.1 10 #> 3 0001 0.381 0003 3.14 18.1 10 #> 4 0001 0.381 0004 4.74 20.7 10 #> 5 0001 0.381 0005 5.20 19.2 10 #> 6 0001 0.381 0006 3.30 21 10 #> 7 0001 0.381 0007 4.07 19.9 10 #> 8 0001 0.381 0008 4.53 18.5 10 #> 9 0001 0.381 0009 2.95 21.7 10 #> 10 0001 0.381 0010 3.36 20.2 10 #> 11 0002 0.491 0001 3.03 20.9 10 #> 12 0002 0.491 0002 2.44 18.8 10 #> 13 0002 0.491 0003 3.62 20.3 10 #> 14 0002 0.491 0004 3.79 19.8 10 #> 15 0002 0.491 0005 4.19 19.3 10 #> 16 0002 0.491 0006 5.34 18.5 10 #> 17 0002 0.491 0007 4.23 18.9 10 #> 18 0002 0.491 0008 3.28 18.9 10 #> 19 0002 0.491 0009 5.33 18 10 #> 20 0002 0.491 0010 3.48 19.1 10 df_portf <- sim_test_data_portfolio(df_config) df_portf #> # A tibble: 3,746 × 8 #> study_id ae_per_visit_mean site_number max_visit_sd max_visit_mean patnum #> #> 1 0001 0.381 0001 2.85 19.9 0001 #> 2 0001 0.381 0001 2.85 19.9 0001 #> 3 0001 0.381 0001 2.85 19.9 0001 #> 4 0001 0.381 0001 2.85 19.9 0001 #> 5 0001 0.381 0001 2.85 19.9 0001 #> 6 0001 0.381 0001 2.85 19.9 0001 #> 7 0001 0.381 0001 2.85 19.9 0001 #> 8 0001 0.381 0001 2.85 19.9 0001 #> 9 0001 0.381 0001 2.85 19.9 0001 #> 10 0001 0.381 0001 2.85 19.9 0001 #> # ℹ 3,736 more rows #> # ℹ 2 more variables: visit , n_ae df_scen <- sim_ur_scenarios(df_portf, extra_ur_sites = 2, ur_rate = c(0.5, 1)) #> aggregating site level #> prepping for simulation #> generating scenarios #> getting under-reporting stats #> evaluating stats df_scen #> # A tibble: 140 × 14 #> study_id site_number extra_ur_sites frac_pat_with_ur ur_rate n_pat #> #> 1 0001 0001 0 0 0 10 #> 2 0001 0001 0 0.122 0.5 10 #> 3 0001 0001 0 0.122 1 10 #> 4 0001 0001 1 0.222 0.5 10 #> 5 0001 0001 1 0.222 1 10 #> 6 0001 0001 2 0.322 0.5 10 #> 7 0001 0001 2 0.322 1 10 #> 8 0001 0002 0 0 0 10 #> 9 0001 0002 0 0.104 0.5 10 #> 10 0001 0002 0 0.104 1 10 #> # ℹ 130 more rows #> # ℹ 8 more variables: n_pat_with_med75 , visit_med75 , #> # mean_ae_site_med75 , mean_ae_study_med75 , #> # n_pat_with_med75_study , prob_low , prob_low_adj , #> # prob_low_prob_ur df_perf <- get_portf_perf(df_scen) df_perf #> # A tibble: 27 × 5 #> fpr thresh extra_ur_sites ur_rate tpr #> #> 1 0.001 0.529 0 0 0.05 #> 2 0.001 0.529 1 0 0.05 #> 3 0.001 0.529 2 0 0.05 #> 4 0.001 0.529 0 0.5 1 #> 5 0.001 0.529 1 0.5 1 #> 6 0.001 0.529 2 0.5 0.95 #> 7 0.001 0.529 0 1 1 #> 8 0.001 0.529 1 1 1 #> 9 0.001 0.529 2 1 1 #> 10 0.01 0.517 0 0 0.05 #> # ℹ 17 more rows # }"},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":null,"dir":"Reference","previous_headings":"","what":"Create simaerep object — simaerep","title":"Create simaerep object — simaerep","text":"Simulate AE -reporting probabilities.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create simaerep object — simaerep","text":"","code":"simaerep( df_visit, r = 1000, check = TRUE, under_only = TRUE, visit_med75 = TRUE, inframe = FALSE, progress = TRUE, param_site_aggr = list(method = \"med75_adj\", min_pat_pool = 0.2), param_sim_sites = list(r = 1000, poisson_test = FALSE, prob_lower = TRUE), param_eval_sites = list(method = \"BH\"), env = parent.frame() )"},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create simaerep object — simaerep","text":"df_visit Data frame columns: study_id, site_number, patnum, visit, n_ae. r Integer tbl_object, number repetitions bootstrap simulation. Pass tbl object referring table one column many rows desired repetitions. Default: 1000. check Logical, perform data check attempt repair check_df_visit(). Computationally expensive large data sets. Default: TRUE. under_only Logical, compute -reporting probabilities . Supersedes under_only parameter passed eval_sites() sim_sites(). Default: TRUE. visit_med75 Logical, evaluation point visit_med75 used. Default: TRUE. inframe Logical, table operations used; require visit_med75. Compatible dbplyr supported database backends. progress Logical, display progress bar. Default: TRUE. param_site_aggr List parameters passed site_aggr(). Default: list(method = \"med75_adj\", min_pat_pool = 0.2). param_sim_sites List parameters passed sim_sites(). Default: list(r = 1000, poisson_test = FALSE, prob_lower = TRUE). param_eval_sites List parameters passed eval_sites(). Default: list(method = \"BH\"). env Optional, provide environment original visit data. Default: parent.frame().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create simaerep object — simaerep","text":"simaerep object.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Create simaerep object — simaerep","text":"Executes site_aggr(), sim_sites(), eval_sites() original visit data stores intermediate results. Stores lazy reference original visit data facilitated plotting using generic plot(x).","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/simaerep.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Create simaerep object — simaerep","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" aerep <- simaerep(df_visit) aerep #> simaerep object: #> Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty. #> Plot results using plot() generic. str(aerep) #> List of 11 #> $ visit :List of 3 #> ..$ dim : int [1:2] 1972 9 #> ..$ df_summary: tibble [1 × 5] (S3: tbl_df/tbl/data.frame) #> .. ..$ n_studies : int 1 #> .. ..$ n_sites : int 5 #> .. ..$ n_patients: int 100 #> .. ..$ n_visits : int 1972 #> .. ..$ n_aes : int 705 #> ..$ str_call : chr \"df_visit\" #> ..- attr(*, \"class\")= chr \"orivisit\" #> $ df_site : tibble [5 × 6] (S3: tbl_df/tbl/data.frame) #> ..$ study_id : chr [1:5] \"A\" \"A\" \"A\" \"A\" ... #> ..$ site_number : chr [1:5] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... #> ..$ n_pat : int [1:5] 20 20 20 20 20 #> ..$ n_pat_with_med75 : num [1:5] 18 18 17 20 16 #> ..$ visit_med75 : Named num [1:5] 17 15 16 15 16 #> .. ..- attr(*, \"names\")= chr [1:5] \"80%\" \"80%\" \"80%\" \"80%\" ... #> ..$ mean_ae_site_med75: num [1:5] 3.11 2.56 8.53 6.4 8.38 #> $ df_sim_sites : tibble [5 × 9] (S3: tbl_df/tbl/data.frame) #> ..$ study_id : chr [1:5] \"A\" \"A\" \"A\" \"A\" ... #> ..$ site_number : chr [1:5] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... #> ..$ n_pat : int [1:5] 20 20 20 20 20 #> ..$ n_pat_with_med75 : num [1:5] 18 18 17 20 16 #> ..$ visit_med75 : Named num [1:5] 17 15 16 15 16 #> .. ..- attr(*, \"names\")= chr [1:5] \"80%\" \"80%\" \"80%\" \"80%\" ... #> ..$ mean_ae_site_med75 : num [1:5] 3.11 2.56 8.53 6.4 8.38 #> ..$ mean_ae_study_med75 : num [1:5] 7.03 6.21 5.26 5.21 5.34 #> ..$ n_pat_with_med75_study: int [1:5] 59 72 66 70 67 #> ..$ prob_low : num [1:5] 0 0 1 1 1 #> $ df_eval : tibble [5 × 11] (S3: tbl_df/tbl/data.frame) #> ..$ study_id : chr [1:5] \"A\" \"A\" \"A\" \"A\" ... #> ..$ site_number : chr [1:5] \"S0001\" \"S0002\" \"S0003\" \"S0004\" ... #> ..$ n_pat : int [1:5] 20 20 20 20 20 #> ..$ n_pat_with_med75 : num [1:5] 18 18 17 20 16 #> ..$ visit_med75 : Named num [1:5] 17 15 16 15 16 #> .. ..- attr(*, \"names\")= chr [1:5] \"80%\" \"80%\" \"80%\" \"80%\" ... #> ..$ mean_ae_site_med75 : num [1:5] 3.11 2.56 8.53 6.4 8.38 #> ..$ mean_ae_study_med75 : num [1:5] 7.03 6.21 5.26 5.21 5.34 #> ..$ n_pat_with_med75_study: int [1:5] 59 72 66 70 67 #> ..$ prob_low : num [1:5] 0 0 1 1 1 #> ..$ prob_low_adj : num [1:5] 0 0 1 1 1 #> ..$ prob_low_prob_ur : num [1:5] 1 1 0 0 0 #> $ r : num 1000 #> $ visit_med75 : logi TRUE #> $ inframe : logi FALSE #> $ under_only : logi TRUE #> $ param_site_aggr :List of 2 #> ..$ method : chr \"med75_adj\" #> ..$ min_pat_pool: num 0.2 #> $ param_sim_sites :List of 4 #> ..$ r : num 1000 #> ..$ poisson_test: logi FALSE #> ..$ prob_lower : logi TRUE #> ..$ under_only : logi TRUE #> $ param_eval_sites:List of 2 #> ..$ method : chr \"BH\" #> ..$ under_only: logi TRUE #> - attr(*, \"class\")= chr \"simaerep\" # \\donttest{ # In-frame table operations simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval #> # A tibble: 5 × 13 #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0001 0.183 76 415 20 0 #> 2 A S0002 0.186 75 403 20 0 #> 3 A S0003 0.496 187 377 20 0.999 #> 4 A S0004 0.453 180 397 20 0.987 #> 5 A S0005 0.492 187 380 20 0.996 #> # ℹ 6 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , prob_high , prob_high_adj , #> # prob_high_prob_or simaerep(df_visit, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval #> # A tibble: 5 × 15 #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0001 0.183 56 306 18 0 #> 2 A S0002 0.170 46 270 18 0 #> 3 A S0003 0.533 145 272 17 1 #> 4 A S0004 0.427 128 300 20 0.892 #> 5 A S0005 0.523 134 256 16 0.996 #> # ℹ 8 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , prob_high , prob_high_adj , #> # prob_high_prob_or , n_pat_with_med75 , visit_med75 # Database example con <- DBI::dbConnect(duckdb::duckdb(), dbdir = \":memory:\") df_r <- tibble::tibble(rep = seq(1, 1000)) dplyr::copy_to(con, df_visit, \"visit\") dplyr::copy_to(con, df_r, \"r\") tbl_visit <- dplyr::tbl(con, \"visit\") tbl_r <- dplyr::tbl(con, \"r\") simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval #> # Source: SQL [5 x 13] #> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] #> # Ordered by: study_id, site_number #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0005 0.492 187 380 20 0.998 #> 2 A S0003 0.496 187 377 20 0.998 #> 3 A S0004 0.453 180 397 20 0.987 #> 4 A S0001 0.183 76 415 20 0 #> 5 A S0002 0.186 75 403 20 0 #> # ℹ 6 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , prob_high , prob_high_adj , #> # prob_high_prob_or simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval #> # Source: SQL [5 x 15] #> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] #> # Ordered by: study_id, site_number #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0003 0.533 145 272 17 1 #> 2 A S0005 0.523 134 256 16 1 #> 3 A S0004 0.427 128 300 20 0.908 #> 4 A S0002 0.170 46 270 18 0 #> 5 A S0001 0.183 56 306 18 0 #> # ℹ 8 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , prob_high , prob_high_adj , #> # prob_high_prob_or , n_pat_with_med75 , visit_med75 DBI::dbDisconnect(con) # }"},{"path":"https://openpharma.github.io/simaerep/reference/simaerep_inframe.html","id":null,"dir":"Reference","previous_headings":"","what":"simulate in dataframe — simaerep_inframe","title":"simulate in dataframe — simaerep_inframe","text":"simulate dataframe","code":""},{"path":"https://openpharma.github.io/simaerep/reference/simaerep_inframe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"simulate in dataframe — simaerep_inframe","text":"","code":"simaerep_inframe( df_visit, r = 1000, under_only = FALSE, visit_med75 = FALSE, check = TRUE, param_site_aggr = list(method = \"med75_adj\", min_pat_pool = 0.2), param_eval_sites = list(method = \"BH\"), env = parent.frame() )"},{"path":"https://openpharma.github.io/simaerep/reference/simaerep_inframe.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"simulate in dataframe — simaerep_inframe","text":"df_visit Data frame columns: study_id, site_number, patnum, visit, n_ae. r Integer tbl_object, number repetitions bootstrap simulation. Pass tbl object referring table one column many rows desired repetitions. Default: 1000. under_only Logical, compute -reporting probabilities . Supersedes under_only parameter passed eval_sites() sim_sites(). Default: TRUE. visit_med75 Logical, evaluation point visit_med75 used. Default: TRUE. check Logical, perform data check attempt repair check_df_visit(). Computationally expensive large data sets. Default: TRUE. param_site_aggr List parameters passed site_aggr(). Default: list(method = \"med75_adj\", min_pat_pool = 0.2). param_eval_sites List parameters passed eval_sites(). Default: list(method = \"BH\"). env Optional, provide environment original visit data. Default: parent.frame().","code":""},{"path":"https://openpharma.github.io/simaerep/reference/simaerep_inframe.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"simulate in dataframe — simaerep_inframe","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" simaerep_inframe(df_visit) #> simaerep object: #> Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty. #> Plot results using plot() generic. simaerep_inframe(df_visit, visit_med75 = TRUE)$df_eval #> # A tibble: 5 × 12 #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0001 0.261 61 234 18 0.006 #> 2 A S0002 0.201 61 304 19 0 #> 3 A S0003 0.426 116 272 17 0.894 #> 4 A S0004 0.489 130 266 19 0.994 #> 5 A S0005 0.481 139 289 17 0.989 #> # ℹ 5 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , n_pat_with_med75 , visit_med75 # \\donttest{ # Database con <- DBI::dbConnect(duckdb::duckdb(), dbdir = \":memory:\") df_r <- tibble::tibble(rep = seq(1, 1000)) dplyr::copy_to(con, df_visit, \"visit\") dplyr::copy_to(con, df_r, \"r\") tbl_visit <- dplyr::tbl(con, \"visit\") tbl_r <- dplyr::tbl(con, \"r\") simaerep_inframe(tbl_visit, r = tbl_r)$df_eval #> # Source: SQL [5 x 10] #> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] #> # Ordered by: study_id, site_number #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0003 0.433 165 381 20 0.821 #> 2 A S0001 0.239 84 352 20 0 #> 3 A S0002 0.210 83 396 20 0 #> 4 A S0004 0.480 184 383 20 0.985 #> 5 A S0005 0.470 181 385 20 0.962 #> # ℹ 3 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur simaerep_inframe(tbl_visit, r = tbl_r, visit_med75 = TRUE)$df_eval #> # Source: SQL [5 x 12] #> # Database: DuckDB v1.0.0 [koneswab@Darwin 23.6.0:R 4.4.1/:memory:] #> # Ordered by: study_id, site_number #> study_id site_number events_per_visit_site events visits n_pat prob_low #> #> 1 A S0004 0.489 130 266 19 0.986 #> 2 A S0002 0.201 61 304 19 0 #> 3 A S0005 0.481 139 289 17 0.987 #> 4 A S0003 0.426 116 272 17 0.894 #> 5 A S0001 0.261 61 234 18 0.011 #> # ℹ 5 more variables: events_per_visit_study , prob_low_adj , #> # prob_low_prob_ur , n_pat_with_med75 , visit_med75 DBI::dbDisconnect(con) # }"},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":null,"dir":"Reference","previous_headings":"","what":"Aggregate from visit to site level. — site_aggr","title":"Aggregate from visit to site level. — site_aggr","text":"Calculates visit_med75, n_pat_with_med75 mean_ae_site_med75","code":""},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Aggregate from visit to site level. — site_aggr","text":"","code":"site_aggr(df_visit, method = \"med75_adj\", min_pat_pool = 0.2, check = TRUE)"},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Aggregate from visit to site level. — site_aggr","text":"df_visit dataframe columns: study_id, site_number, patnum, visit, n_ae method character, one c(\"med75\", \"med75_adj\") defining method defining evaluation point visit_med75 (see details), Default: \"med75_adj\" min_pat_pool, double, minimum ratio available patients available sampling. Determines maximum visit_med75 value see Details. Default: 0.2 check, logical, perform data check attempt repair check_df_visit(), computationally expensive large data sets. Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Aggregate from visit to site level. — site_aggr","text":"dataframe following columns: study_id study identification site_number site identification n_pat number patients, site level visit_med75 adjusted median(max(visit)) * 0.75 see Details n_pat_with_med75 number patients meet visit_med75 criterion, site level mean_ae_site_med75 mean AE visit_med75, site level","code":""},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Aggregate from visit to site level. — site_aggr","text":"determining visit number going evaluate AE reporting take maximum visit patient site take median. multiply 0.75 give us cut-point determining patient evaluated. patients evaluate take minimum maximum visits hence ensuring take highest visit number possible without excluding patients analysis. order ensure sampling pool visit large enough limit visit number 80% quantile maximum visits patients study.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/site_aggr.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Aggregate from visit to site level. — site_aggr","text":"","code":"df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) df_visit$study_id <- \"A\" df_site <- site_aggr(df_visit) df_site %>% knitr::kable(digits = 2) #> #> #> |study_id |site_number | n_pat| n_pat_with_med75| visit_med75| mean_ae_site_med75| #> |:--------|:-----------|-----:|----------------:|-----------:|------------------:| #> |A |S0001 | 20| 18| 15| 2.39| #> |A |S0002 | 20| 19| 13| 2.68| #> |A |S0003 | 20| 19| 16| 7.74| #> |A |S0004 | 20| 16| 17| 8.69| #> |A |S0005 | 20| 16| 18| 8.19|"},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":null,"dir":"Reference","previous_headings":"","what":"Conditional with_progress. — with_progress_cnd","title":"Conditional with_progress. — with_progress_cnd","text":"Internal function. Use instead with_progress within custom functions progress bars.","code":""},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Conditional with_progress. — with_progress_cnd","text":"","code":"with_progress_cnd(ex, progress = TRUE)"},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Conditional with_progress. — with_progress_cnd","text":"ex expression progress logical, Default: TRUE","code":""},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Conditional with_progress. — with_progress_cnd","text":"return value, called side effects","code":""},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Conditional with_progress. — with_progress_cnd","text":"wrapper adds progress parameter with_progress can control progress bar user facing functions. progressbar shows interactive mode.","code":""},{"path":[]},{"path":"https://openpharma.github.io/simaerep/reference/with_progress_cnd.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Conditional with_progress. — with_progress_cnd","text":"","code":"if (interactive()) { with_progress_cnd( purrr_bar(rep(0.25, 5), .purrr = purrr::map, .f = Sys.sleep, .steps = 5), progress = TRUE ) with_progress_cnd( purrr_bar(rep(0.25, 5), .purrr = purrr::map, .f = Sys.sleep, .steps = 5), progress = FALSE ) # wrap a function with progress bar with another call with progress bar f1 <- function(x, progress = TRUE) { with_progress_cnd( purrr_bar(x, .purrr = purrr::walk, .f = Sys.sleep, .steps = length(x), .progress = progress), progress = progress ) } # inner progress bar blocks outer progress bar progressr::with_progress( purrr_bar( rep(rep(1, 3),3), .purrr = purrr::walk, .f = f1, .steps = 3, .f_args = list(progress = TRUE) ) ) # inner progress bar turned off progressr::with_progress( purrr_bar( rep(list(rep(0.25, 3)), 5), .purrr = purrr::walk, .f = f1, .steps = 5, .f_args = list(progress = FALSE) ) ) }"},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-060","dir":"Changelog","previous_headings":"","what":"simaerep 0.6.0","title":"simaerep 0.6.0","text":"inframe method use table operations , use visit_med75 https://github.com/openpharma/simaerep/issues/59 https://github.com/openpharma/simaerep/issues/16 update vignettes","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-050","dir":"Changelog","previous_headings":"","what":"simaerep 0.5.0","title":"simaerep 0.5.0","text":"CRAN release: 2024-04-03 allow flexible AE rates data simulations add vignette comparing simaerep gsm performance add -reporting probability fix dplyr warnings fix warnings around ggplot cowplot https://github.com/openpharma/simaerep/issues/45","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-043","dir":"Changelog","previous_headings":"","what":"simaerep 0.4.3","title":"simaerep 0.4.3","text":"CRAN release: 2023-03-03 delete performance unit tests (poisson faster bootstrap) accommodate CRAN request","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-042","dir":"Changelog","previous_headings":"","what":"simaerep 0.4.2","title":"simaerep 0.4.2","text":"CRAN release: 2023-02-25 CRAN submission","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-041","dir":"Changelog","previous_headings":"","what":"simaerep 0.4.1","title":"simaerep 0.4.1","text":"validation report","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-040","dir":"Changelog","previous_headings":"","what":"simaerep 0.4.0","title":"simaerep 0.4.0","text":"S3 interface #17 better performance specification #26 meaningful unit test names #24 #30 #33","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-033","dir":"Changelog","previous_headings":"","what":"simaerep 0.3.3","title":"simaerep 0.3.3","text":"better data checks error messages #19, #20, # 21 data check repair check_df_visit() became optionable","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-032","dir":"Changelog","previous_headings":"","what":"simaerep 0.3.2","title":"simaerep 0.3.2","text":"added portfolio performance assessment","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-031","dir":"Changelog","previous_headings":"","what":"simaerep 0.3.1","title":"simaerep 0.3.1","text":"changed MIT License holder openpharma F. Hoffmann-La Roche Ltd simaerep authors","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-030","dir":"Changelog","previous_headings":"","what":"simaerep 0.3.0","title":"simaerep 0.3.0","text":"improved evaluation point visit_med75 allow days instead visit","code":""},{"path":"https://openpharma.github.io/simaerep/news/index.html","id":"simaerep-020","dir":"Changelog","previous_headings":"","what":"simaerep 0.2.0","title":"simaerep 0.2.0","text":"use Benjamin Hochberg procedure alpha error correction fix warnings around parallel processing improved SAS files vignette","code":""}] diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 4c5cb11..1d32714 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -1,186 +1,73 @@ - - - - https://openpharma.github.io/simaerep/404.html - - - https://openpharma.github.io/simaerep/LICENSE-text.html - - - https://openpharma.github.io/simaerep/LICENSE.html - - - https://openpharma.github.io/simaerep/articles/check_poisson.html - - - https://openpharma.github.io/simaerep/articles/do_not_commit.html - - - https://openpharma.github.io/simaerep/articles/gsm_perf.html - - - https://openpharma.github.io/simaerep/articles/index.html - - - https://openpharma.github.io/simaerep/articles/intro.html - - - https://openpharma.github.io/simaerep/articles/over.html - - - https://openpharma.github.io/simaerep/articles/portfolio_perf.html - - - https://openpharma.github.io/simaerep/articles/sas_files.html - - - https://openpharma.github.io/simaerep/articles/usability_limits.html - - - https://openpharma.github.io/simaerep/articles/visit_med75.html - - - https://openpharma.github.io/simaerep/articles/visits_or_days.html - - - https://openpharma.github.io/simaerep/authors.html - - - https://openpharma.github.io/simaerep/index.html - - - https://openpharma.github.io/simaerep/news/index.html - - - https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html - - - https://openpharma.github.io/simaerep/reference/boot_sim_test_data_patient.html - - - https://openpharma.github.io/simaerep/reference/boot_sim_test_data_study.html - - - https://openpharma.github.io/simaerep/reference/check_df_visit.html - - - https://openpharma.github.io/simaerep/reference/eval_sites.html - - - https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html - - - https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html - - - https://openpharma.github.io/simaerep/reference/get_config.html - - - https://openpharma.github.io/simaerep/reference/get_ecd_values.html - - - https://openpharma.github.io/simaerep/reference/get_legend.html - - - https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html - - - https://openpharma.github.io/simaerep/reference/get_portf_perf.html - - - https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html - - - https://openpharma.github.io/simaerep/reference/get_visit_med75.html - - - https://openpharma.github.io/simaerep/reference/index.html - - - https://openpharma.github.io/simaerep/reference/is_orivisit.html - - - https://openpharma.github.io/simaerep/reference/is_simaerep.html - - - https://openpharma.github.io/simaerep/reference/lint_package.html - - - https://openpharma.github.io/simaerep/reference/orivisit.html - - - https://openpharma.github.io/simaerep/reference/pat_aggr.html - - - https://openpharma.github.io/simaerep/reference/pat_pool.html - - - https://openpharma.github.io/simaerep/reference/pipe.html - - - https://openpharma.github.io/simaerep/reference/plot.simaerep.html - - - https://openpharma.github.io/simaerep/reference/plot_dots.html - - - https://openpharma.github.io/simaerep/reference/plot_sim_example.html - - - https://openpharma.github.io/simaerep/reference/plot_sim_examples.html - - - https://openpharma.github.io/simaerep/reference/plot_study.html - - - https://openpharma.github.io/simaerep/reference/plot_visit_med75.html - - - https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html - - - https://openpharma.github.io/simaerep/reference/prep_for_sim.html - - - https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html - - - https://openpharma.github.io/simaerep/reference/purrr_bar.html - - - https://openpharma.github.io/simaerep/reference/sim_after_prep.html - - - https://openpharma.github.io/simaerep/reference/sim_scenario.html - - - https://openpharma.github.io/simaerep/reference/sim_sites.html - - - https://openpharma.github.io/simaerep/reference/sim_studies.html - - - https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html - - - https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html - - - https://openpharma.github.io/simaerep/reference/sim_test_data_study.html - - - https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html - - - https://openpharma.github.io/simaerep/reference/simaerep.html - - - https://openpharma.github.io/simaerep/reference/site_aggr.html - - - https://openpharma.github.io/simaerep/reference/ttest_site_ae_vs_study_ae.html - - - https://openpharma.github.io/simaerep/reference/with_progress_cnd.html - + +https://openpharma.github.io/simaerep/404.html +https://openpharma.github.io/simaerep/LICENSE-text.html +https://openpharma.github.io/simaerep/LICENSE.html +https://openpharma.github.io/simaerep/articles/check_poisson.html +https://openpharma.github.io/simaerep/articles/do_not_commit.html +https://openpharma.github.io/simaerep/articles/funnel_perf.html +https://openpharma.github.io/simaerep/articles/gsm_perf.html +https://openpharma.github.io/simaerep/articles/index.html +https://openpharma.github.io/simaerep/articles/inframe.html +https://openpharma.github.io/simaerep/articles/intro.html +https://openpharma.github.io/simaerep/articles/over.html +https://openpharma.github.io/simaerep/articles/performance.html +https://openpharma.github.io/simaerep/articles/portfolio_perf.html +https://openpharma.github.io/simaerep/articles/sas_files.html +https://openpharma.github.io/simaerep/articles/usability_limits.html +https://openpharma.github.io/simaerep/articles/visit_med75.html +https://openpharma.github.io/simaerep/articles/visits_or_days.html +https://openpharma.github.io/simaerep/authors.html +https://openpharma.github.io/simaerep/index.html +https://openpharma.github.io/simaerep/news/index.html +https://openpharma.github.io/simaerep/reference/aggr_duplicated_visits.html +https://openpharma.github.io/simaerep/reference/boot_sim_test_data_patient.html +https://openpharma.github.io/simaerep/reference/boot_sim_test_data_study.html +https://openpharma.github.io/simaerep/reference/check_df_visit.html +https://openpharma.github.io/simaerep/reference/eval_sites.html +https://openpharma.github.io/simaerep/reference/eval_sites_deprecated.html +https://openpharma.github.io/simaerep/reference/exp_implicit_missing_visits.html +https://openpharma.github.io/simaerep/reference/get_config.html +https://openpharma.github.io/simaerep/reference/get_ecd_values.html +https://openpharma.github.io/simaerep/reference/get_legend.html +https://openpharma.github.io/simaerep/reference/get_pat_pool_config.html +https://openpharma.github.io/simaerep/reference/get_portf_perf.html +https://openpharma.github.io/simaerep/reference/get_site_mean_ae_dev.html +https://openpharma.github.io/simaerep/reference/get_visit_med75.html +https://openpharma.github.io/simaerep/reference/index.html +https://openpharma.github.io/simaerep/reference/is_orivisit.html +https://openpharma.github.io/simaerep/reference/is_simaerep.html +https://openpharma.github.io/simaerep/reference/lint_package.html +https://openpharma.github.io/simaerep/reference/max_rank.html +https://openpharma.github.io/simaerep/reference/orivisit.html +https://openpharma.github.io/simaerep/reference/p_adjust_bh_inframe.html +https://openpharma.github.io/simaerep/reference/pat_aggr.html +https://openpharma.github.io/simaerep/reference/pat_pool.html +https://openpharma.github.io/simaerep/reference/pipe.html +https://openpharma.github.io/simaerep/reference/plot.simaerep.html +https://openpharma.github.io/simaerep/reference/plot_dots.html +https://openpharma.github.io/simaerep/reference/plot_sim_example.html +https://openpharma.github.io/simaerep/reference/plot_sim_examples.html +https://openpharma.github.io/simaerep/reference/plot_study.html +https://openpharma.github.io/simaerep/reference/plot_visit_med75.html +https://openpharma.github.io/simaerep/reference/poiss_test_site_ae_vs_study_ae.html +https://openpharma.github.io/simaerep/reference/prep_for_sim.html +https://openpharma.github.io/simaerep/reference/prob_lower_site_ae_vs_study_ae.html +https://openpharma.github.io/simaerep/reference/prune_to_visit_med75_inframe.html +https://openpharma.github.io/simaerep/reference/purrr_bar.html +https://openpharma.github.io/simaerep/reference/sim_after_prep.html +https://openpharma.github.io/simaerep/reference/sim_inframe.html +https://openpharma.github.io/simaerep/reference/sim_scenario.html +https://openpharma.github.io/simaerep/reference/sim_sites.html +https://openpharma.github.io/simaerep/reference/sim_studies.html +https://openpharma.github.io/simaerep/reference/sim_test_data_patient.html +https://openpharma.github.io/simaerep/reference/sim_test_data_portfolio.html +https://openpharma.github.io/simaerep/reference/sim_test_data_study.html +https://openpharma.github.io/simaerep/reference/sim_ur.html +https://openpharma.github.io/simaerep/reference/sim_ur_scenarios.html +https://openpharma.github.io/simaerep/reference/simaerep.html +https://openpharma.github.io/simaerep/reference/simaerep_inframe.html +https://openpharma.github.io/simaerep/reference/site_aggr.html +https://openpharma.github.io/simaerep/reference/ttest_site_ae_vs_study_ae.html +https://openpharma.github.io/simaerep/reference/with_progress_cnd.html + diff --git a/inst/WORDLIST b/inst/WORDLIST index e4ee4ea..2415cce 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,20 +1,28 @@ AE AEs +Adyanthaya Analytics BDBDBD BH Barmaz +Boeringer Downey's Downey’s +FN FP Hochberg Hoffmann +Inframe +Ingelheim +Innov Lifecycle Modelling Ménard RBQM +Regul Saf TP +Ther Uncompliant Zink ae @@ -24,11 +32,14 @@ al analytics anonymize anonymized +benjamini +coMPany codecov config cowplot csv darkblue +dbplyr df doi dplyr @@ -36,6 +47,7 @@ ecd ecdf et eval +examplary fp fpr frac @@ -43,7 +55,9 @@ furrr ggplot grey gsm +hochberg infer’ +inframe iterable iterables iteratively @@ -58,16 +72,20 @@ orivisit patnum pkgdown poisson +pre progressbar purrr pval pvalue +quALity +reporducibly rmarkdown rnorm rpois sd siamerep -superseeds +sql +tbl thevalidatoR tibble tp @@ -78,3 +96,4 @@ uncompliant underdispersion ur workproduct +zi diff --git a/man/figures/README-unnamed-chunk-2-1.png b/man/figures/README-unnamed-chunk-2-1.png index 688af4b..eda9517 100644 Binary files a/man/figures/README-unnamed-chunk-2-1.png and b/man/figures/README-unnamed-chunk-2-1.png differ diff --git a/man/max_rank.Rd b/man/max_rank.Rd new file mode 100644 index 0000000..9e353f8 --- /dev/null +++ b/man/max_rank.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/inframe.R +\name{max_rank} +\alias{max_rank} +\title{Calculate Max Rank} +\usage{ +max_rank(df, col, col_new) +} +\arguments{ +\item{df}{dataframe} + +\item{col}{character column name to rank y} + +\item{col_new}{character column name for rankings} +} +\description{ +like rank() with ties.method = "max", works on tbl objects +} +\details{ +this is needed for hochberg p value adjustment. We need to assign higher +rank when multiple sites have same p value +} +\examples{ + +df <- tibble::tibble(s = c(1, 2, 2, 2, 5, 10)) \%>\% + dplyr::mutate( + rank = rank(s, ties.method = "max") + ) + +df \%>\% + max_rank("s", "max_rank") +\donttest{ +# Database +con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") + +dplyr::copy_to(con, df, "df") +max_rank(dplyr::tbl(con, "df"), "s", "max_rank") + +DBI::dbDisconnect(con) +} +} diff --git a/man/p_adjust_bh_inframe.Rd b/man/p_adjust_bh_inframe.Rd new file mode 100644 index 0000000..f7ff222 --- /dev/null +++ b/man/p_adjust_bh_inframe.Rd @@ -0,0 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/inframe.R +\name{p_adjust_bh_inframe} +\alias{p_adjust_bh_inframe} +\title{benjamini hochberg p value correction using table operations} +\usage{ +p_adjust_bh_inframe(df_eval, col, suffix) +} +\description{ +benjamini hochberg p value correction using table operations +} +\keyword{internal} diff --git a/man/prune_to_visit_med75_inframe.Rd b/man/prune_to_visit_med75_inframe.Rd new file mode 100644 index 0000000..2a82d9b --- /dev/null +++ b/man/prune_to_visit_med75_inframe.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/inframe.R +\name{prune_to_visit_med75_inframe} +\alias{prune_to_visit_med75_inframe} +\title{prune visits to visit_med75 using table operations} +\usage{ +prune_to_visit_med75_inframe(df_visit, df_site) +} +\arguments{ +\item{df_visit}{Data frame with columns: study_id, site_number, patnum, visit, +n_ae.} + +\item{df_site}{dataframe, as returned by \code{\link[=site_aggr]{site_aggr()}}} +} +\description{ +prune visits to visit_med75 using table operations +} +\keyword{internal} diff --git a/man/sim_inframe.Rd b/man/sim_inframe.Rd new file mode 100644 index 0000000..4649367 --- /dev/null +++ b/man/sim_inframe.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/inframe.R +\name{sim_inframe} +\alias{sim_inframe} +\title{Calculate prob_lower for study sites using table operations} +\usage{ +sim_inframe(df_visit, r = 1000, df_site = NULL) +} +\arguments{ +\item{df_visit}{Data frame with columns: study_id, site_number, patnum, visit, +n_ae.} + +\item{r}{Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.} + +\item{df_site, }{dataframe as returned be \code{\link[=site_aggr]{site_aggr()}}, Will switch to visit_med75. +Default: NULL} +} +\description{ +Calculate prob_lower for study sites using table operations +} +\examples{ +df_visit <- sim_test_data_study( + n_pat = 100, + n_sites = 5, + frac_site_with_ur = 0.4, + ur_rate = 0.6 +) +df_visit$study_id <- "A" + +df_sim <- sim_inframe(df_visit) +df_eval <- eval_sites(df_sim) +df_eval +} diff --git a/man/sim_ur.Rd b/man/sim_ur.Rd new file mode 100644 index 0000000..295c5e0 --- /dev/null +++ b/man/sim_ur.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/simulate_test_data.R +\name{sim_ur} +\alias{sim_ur} +\title{simulate under-reporting} +\usage{ +sim_ur(df_visit, study_id, site_number, ur_rate) +} +\arguments{ +\item{df_visit, }{dataframe} + +\item{study_id, }{character} + +\item{site_number, }{character} + +\item{ur_rate, }{double} +} +\description{ +we remove a fraction of AEs from a specific site +} +\details{ +we determine the absolute number of AEs per patient for removal. +Then them remove them at the first visit. +We intentionally allow fractions +} +\examples{ +df_visit <- sim_test_data_study(n_pat = 100, n_sites = 10, + frac_site_with_ur = 0.4, ur_rate = 0.6) + +df_visit$study_id <- "A" + +df_ur <- sim_ur(df_visit, "A", site_number = "S0001", ur_rate = 0.35) + +# Example cumulated AE for first patient with 35\% under-reporting +df_ur[df_ur$site_number == "S0001" & df_ur$patnum == "P000001",]$n_ae + +# Example cumulated AE for first patient with no under-reporting +df_visit[df_visit$site_number == "S0001" & df_visit$patnum == "P000001",]$n_ae + +} diff --git a/man/sim_ur_scenarios.Rd b/man/sim_ur_scenarios.Rd index 64a972f..963e811 100644 --- a/man/sim_ur_scenarios.Rd +++ b/man/sim_ur_scenarios.Rd @@ -14,7 +14,8 @@ sim_ur_scenarios( parallel = FALSE, progress = TRUE, site_aggr_args = list(), - eval_sites_args = list() + eval_sites_args = list(), + check = TRUE ) } \arguments{ @@ -41,6 +42,8 @@ Default: c(0.25, 0.5)} \item{eval_sites_args}{named list of parameters passed to \code{\link{eval_sites}}, Default: list()} + +\item{check}{logical, perform data check and attempt repair with} } \value{ dataframe with the following columns: diff --git a/man/simaerep.Rd b/man/simaerep.Rd index b291927..2a69c60 100644 --- a/man/simaerep.Rd +++ b/man/simaerep.Rd @@ -2,76 +2,100 @@ % Please edit documentation in R/S3_simaerep.R \name{simaerep} \alias{simaerep} -\title{create simaerep object} +\title{Create simaerep object} \usage{ simaerep( df_visit, + r = 1000, + check = TRUE, + under_only = TRUE, + visit_med75 = TRUE, + inframe = FALSE, + progress = TRUE, param_site_aggr = list(method = "med75_adj", min_pat_pool = 0.2), param_sim_sites = list(r = 1000, poisson_test = FALSE, prob_lower = TRUE), param_eval_sites = list(method = "BH"), - progress = TRUE, - check = TRUE, - env = parent.frame(), - under_only = TRUE + env = parent.frame() ) } \arguments{ -\item{df_visit}{data frame with columns: study_id, site_number, patnum, visit, -n_ae} +\item{df_visit}{Data frame with columns: study_id, site_number, patnum, visit, +n_ae.} -\item{param_site_aggr}{list of parameters passed to \link[=site_aggr]{site_aggr()}, -Default: list(method = "med75_adj", min_pat_pool = 0.2)} +\item{r}{Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.} -\item{param_sim_sites}{list of parameters passed to \link[=sim_sites]{sim_sites()}, -Default: list(r = 1000, poisson_test = FALSE, prob_lower = TRUE)} +\item{check}{Logical, perform data check and attempt repair with +\code{\link[=check_df_visit]{check_df_visit()}}. Computationally expensive on large data sets. Default: +TRUE.} -\item{param_eval_sites}{list of parameters passed to -\link[=eval_sites]{eval_sites()}, Default: list(method = "BH")} +\item{under_only}{Logical, compute under-reporting probabilities only. +Supersedes under_only parameter passed to \code{\link[=eval_sites]{eval_sites()}} and \code{\link[=sim_sites]{sim_sites()}}. +Default: TRUE.} -\item{progress}{logical, display progress bar, Default = TRUE} +\item{visit_med75}{Logical, should evaluation point visit_med75 be used. +Default: TRUE.} -\item{check}{logical, perform data check and attempt repair with -\link[=check_df_visit]{check_df_visit()}, computationally expensive on large data -sets. Default: TRUE} +\item{inframe}{Logical, only table operations to be used; does not require +visit_med75. Compatible with dbplyr supported database backends.} -\item{env}{optional, provide environment of original visit data, Default: -parent.frame()} +\item{progress}{Logical, display progress bar. Default: TRUE.} -\item{under_only, }{logical compute under-reporting probabilities only, -superseeds under_only parameter passed to \link[=eval_sites]{eval_sites()} and -\link[=sim_sites]{sim_sites()}, Default: TRUE} +\item{param_site_aggr}{List of parameters passed to \code{\link[=site_aggr]{site_aggr()}}. Default: +list(method = "med75_adj", min_pat_pool = 0.2).} + +\item{param_sim_sites}{List of parameters passed to \code{\link[=sim_sites]{sim_sites()}}. Default: +list(r = 1000, poisson_test = FALSE, prob_lower = TRUE).} + +\item{param_eval_sites}{List of parameters passed to \code{\link[=eval_sites]{eval_sites()}}. Default: +list(method = "BH").} + +\item{env}{Optional, provide environment of original visit data. Default: +parent.frame().} } \value{ -simaerep object +A simaerep object. } \description{ -simulate AE under-reporting probabilities +Simulate AE under-reporting probabilities. } \details{ -executes \link[=site_aggr]{site_aggr()}, \link[=sim_sites]{sim_sites()} and -\link[=eval_sites]{eval_sites()} on original visit data and stores all -intermediate results. Stores lazy reference to original visit data for -facilitated plotting using generic plot(x). +Executes \code{\link[=site_aggr]{site_aggr()}}, \code{\link[=sim_sites]{sim_sites()}}, and \code{\link[=eval_sites]{eval_sites()}} on original +visit data and stores all intermediate results. Stores lazy reference to +original visit data for facilitated plotting using generic plot(x). } \examples{ - df_visit <- sim_test_data_study( n_pat = 100, n_sites = 5, frac_site_with_ur = 0.4, ur_rate = 0.6 ) - df_visit$study_id <- "A" - aerep <- simaerep(df_visit) - aerep - str(aerep) - +\donttest{ + # In-frame table operations + simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval + simaerep(df_visit, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval + # Database example + con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") + df_r <- tibble::tibble(rep = seq(1, 1000)) + dplyr::copy_to(con, df_visit, "visit") + dplyr::copy_to(con, df_r, "r") + tbl_visit <- dplyr::tbl(con, "visit") + tbl_r <- dplyr::tbl(con, "r") + simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = FALSE, under_only = FALSE)$df_eval + simaerep(tbl_visit, r = tbl_r, inframe = TRUE, visit_med75 = TRUE, under_only = FALSE)$df_eval + DBI::dbDisconnect(con) +} } \seealso{ +\code{\link[=site_aggr]{site_aggr()}}, \code{\link[=sim_sites]{sim_sites()}}, \code{\link[=eval_sites]{eval_sites()}}, \code{\link[=orivisit]{orivisit()}}, +\code{\link[=plot.simaerep]{plot.simaerep()}} + \link[=site_aggr]{site_aggr()}, \link[=sim_sites]{sim_sites()}, \link[=eval_sites]{eval_sites()}, \link[=orivisit]{orivisit()}, \link[=plot.simaerep]{plot.simaerep()} diff --git a/man/simaerep_inframe.Rd b/man/simaerep_inframe.Rd new file mode 100644 index 0000000..aebd3e0 --- /dev/null +++ b/man/simaerep_inframe.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/S3_simaerep.R +\name{simaerep_inframe} +\alias{simaerep_inframe} +\title{simulate in dataframe} +\usage{ +simaerep_inframe( + df_visit, + r = 1000, + under_only = FALSE, + visit_med75 = FALSE, + check = TRUE, + param_site_aggr = list(method = "med75_adj", min_pat_pool = 0.2), + param_eval_sites = list(method = "BH"), + env = parent.frame() +) +} +\arguments{ +\item{df_visit}{Data frame with columns: study_id, site_number, patnum, visit, +n_ae.} + +\item{r}{Integer or tbl_object, number of repetitions for bootstrap +simulation. Pass a tbl object referring to a table with one column and as +many rows as desired repetitions. Default: 1000.} + +\item{under_only}{Logical, compute under-reporting probabilities only. +Supersedes under_only parameter passed to \code{\link[=eval_sites]{eval_sites()}} and \code{\link[=sim_sites]{sim_sites()}}. +Default: TRUE.} + +\item{visit_med75}{Logical, should evaluation point visit_med75 be used. +Default: TRUE.} + +\item{check}{Logical, perform data check and attempt repair with +\code{\link[=check_df_visit]{check_df_visit()}}. Computationally expensive on large data sets. Default: +TRUE.} + +\item{param_site_aggr}{List of parameters passed to \code{\link[=site_aggr]{site_aggr()}}. Default: +list(method = "med75_adj", min_pat_pool = 0.2).} + +\item{param_eval_sites}{List of parameters passed to \code{\link[=eval_sites]{eval_sites()}}. Default: +list(method = "BH").} + +\item{env}{Optional, provide environment of original visit data. Default: +parent.frame().} +} +\description{ +simulate in dataframe +} +\examples{ +df_visit <- sim_test_data_study( + n_pat = 100, + n_sites = 5, + frac_site_with_ur = 0.4, + ur_rate = 0.6 +) +df_visit$study_id <- "A" + +simaerep_inframe(df_visit) +simaerep_inframe(df_visit, visit_med75 = TRUE)$df_eval +\donttest{ +# Database +con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") +df_r <- tibble::tibble(rep = seq(1, 1000)) + +dplyr::copy_to(con, df_visit, "visit") +dplyr::copy_to(con, df_r, "r") + +tbl_visit <- dplyr::tbl(con, "visit") +tbl_r <- dplyr::tbl(con, "r") + +simaerep_inframe(tbl_visit, r = tbl_r)$df_eval +simaerep_inframe(tbl_visit, r = tbl_r, visit_med75 = TRUE)$df_eval + +DBI::dbDisconnect(con) +} +} +\keyword{internal} diff --git a/tests/testthat/_snaps/S3_simaerep.md b/tests/testthat/_snaps/S3_simaerep.md index 146d15b..081d469 100644 --- a/tests/testthat/_snaps/S3_simaerep.md +++ b/tests/testthat/_snaps/S3_simaerep.md @@ -4,6 +4,6 @@ print(aerep_test) Output simaerep object: - Check aerep$df_eval prob_low_prob_ur column for AE under-reporting probabilites. + Check aerep$df_eval prob_low_prob_ur column for under-reporting probabililty. Plot results using plot() generic. diff --git a/tests/testthat/_snaps/eval_sites.md b/tests/testthat/_snaps/eval_sites.md index a446ccd..2edd990 100644 --- a/tests/testthat/_snaps/eval_sites.md +++ b/tests/testthat/_snaps/eval_sites.md @@ -3,16 +3,18 @@ Code df_eval <- eval_sites(df_sim_sites, r_sim_sites = 100) Condition - Warning in `eval_sites()`: + Warning in `warning_na()`: - study_id: C, site_number: a. pval == NA - study_id: C, site_number: b. pval == NA - study_id: C, site_number: c. pval == NA - Warning in `eval_sites()`: + study_id: C, site_number: a, pval== NA - study_id: C, site_number: a, prob_low == NA + study_id: C, site_number: b, pval== NA - study_id: C, site_number: b, prob_low == NA + study_id: C, site_number: c, pval== NA + Warning in `warning_na()`: - study_id: C, site_number: c, prob_low == NA + study_id: C, site_number: a, prob_low== NA + + study_id: C, site_number: b, prob_low== NA + + study_id: C, site_number: c, prob_low== NA diff --git a/tests/testthat/_snaps/validation/study-025.svg b/tests/testthat/_snaps/validation/study-025.svg index 9ebbb19..14a03db 100644 --- a/tests/testthat/_snaps/validation/study-025.svg +++ b/tests/testthat/_snaps/validation/study-025.svg @@ -89,13 +89,12 @@ - + -170/200 0.0 @@ -153,7 +152,7 @@ - + 9/10 74 % @@ -195,7 +194,7 @@ - + 8/10 55.2 % @@ -237,7 +236,7 @@ - + 9/10 55.2 % @@ -279,7 +278,7 @@ - + 8/10 55.2 % @@ -321,7 +320,7 @@ - + 9/10 55.2 % diff --git a/tests/testthat/_snaps/validation/study-050.svg b/tests/testthat/_snaps/validation/study-050.svg index bfd6a66..40c9892 100644 --- a/tests/testthat/_snaps/validation/study-050.svg +++ b/tests/testthat/_snaps/validation/study-050.svg @@ -89,14 +89,13 @@ - + -182/200 0.0 @@ -151,7 +150,7 @@ - + 9/10 100 % @@ -190,7 +189,7 @@ - + 8/10 91.7 % @@ -229,7 +228,7 @@ - + 10/10 98 % @@ -268,7 +267,7 @@ - + 9/10 91.7 % @@ -307,7 +306,7 @@ - + 9/10 96 % @@ -346,7 +345,7 @@ - + 10/10 91.7 % diff --git a/tests/testthat/_snaps/validation/study-075.svg b/tests/testthat/_snaps/validation/study-075.svg index 53c3186..36325a8 100644 --- a/tests/testthat/_snaps/validation/study-075.svg +++ b/tests/testthat/_snaps/validation/study-075.svg @@ -89,14 +89,13 @@ - + -180/200 0.0 @@ -151,7 +150,7 @@ - + 9/10 100 % @@ -190,7 +189,7 @@ - + 9/10 100 % @@ -229,7 +228,7 @@ - + 9/10 100 % @@ -268,7 +267,7 @@ - + 10/10 99.7 % @@ -307,7 +306,7 @@ - + 10/10 100 % @@ -346,7 +345,7 @@ - + 9/10 99.7 % diff --git a/tests/testthat/_snaps/validation/study-100.svg b/tests/testthat/_snaps/validation/study-100.svg index 4e84a6e..46a4333 100644 --- a/tests/testthat/_snaps/validation/study-100.svg +++ b/tests/testthat/_snaps/validation/study-100.svg @@ -89,14 +89,13 @@ - + -156/200 0 @@ -150,7 +149,7 @@ - + 10/10 100 % @@ -187,7 +186,7 @@ - + 8/10 100 % @@ -224,7 +223,7 @@ - + 7/10 100 % @@ -261,7 +260,7 @@ - + 8/10 100 % @@ -298,7 +297,7 @@ - + 9/10 100 % @@ -335,7 +334,7 @@ - + 8/10 100 % diff --git a/tests/testthat/test_S3_simaerep.R b/tests/testthat/test_S3_simaerep.R index 5d110e4..7a59881 100644 --- a/tests/testthat/test_S3_simaerep.R +++ b/tests/testthat/test_S3_simaerep.R @@ -10,16 +10,66 @@ test_that("is_simaerep returns TRUE", { }) test_that("simaerep must retrieve original visit data from parent environment", { - df_vs_env <- as.data.frame(aerep_test$visit) + aerep_new <- simaerep(df_visit_test, inframe = FALSE, visit_med75 = TRUE) + df_vs_env <- as.data.frame(aerep_new$visit) + expect_equal(df_vs_env, df_visit_test) + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE) + df_vs_env <- as.data.frame(aerep_new$visit) + expect_equal(df_vs_env, df_visit_test) + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE) + df_vs_env <- as.data.frame(aerep_new$visit) expect_equal(df_vs_env, df_visit_test) }) +test_that("simaerep - original dataframe unretrievable when called with slice", { + aerep_new <- simaerep( + df_visit_test[df_visit_test$site_number != "S0001", ], + inframe = FALSE, + visit_med75 = TRUE + ) + + expect_error(as.data.frame(aerep_new$visit), "Could not find original visit data") + + aerep_new <- simaerep( + df_visit_test[df_visit_test$site_number != "S0001", ], + inframe = TRUE, + visit_med75 = TRUE + ) + + expect_error(as.data.frame(aerep_new$visit), "Could not find original visit data") + + aerep_new <- simaerep( + df_visit_test[df_visit_test$site_number != "S0001", ], + inframe = TRUE, + visit_med75 = FALSE + ) + + expect_error(as.data.frame(aerep_new$visit), "Could not find original visit data") +}) + + test_that("plot.simaerep with what='ur'", { - expect_s3_class(plot(aerep_test, what = "ur", study = "A"), "ggplot") + aerep_new <- simaerep(df_visit_test, inframe = FALSE, visit_med75 = TRUE) + expect_s3_class(plot(aerep_new, what = "ur", study = "A"), "ggplot") + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE) + expect_s3_class(plot(aerep_new, what = "ur", study = "A"), "ggplot") + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE) + expect_s3_class(plot(aerep_new, what = "ur", study = "A"), "ggplot") }) test_that("plot.simaerep with what='med75'", { - expect_s3_class(plot(aerep_test, what = "med75", study = "A", verbose = FALSE), "ggplot") + aerep_new <- simaerep(df_visit_test, inframe = FALSE, visit_med75 = TRUE) + expect_s3_class(plot(aerep_new, what = "med75", study = "A", verbose = FALSE), "ggplot") + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE) + expect_s3_class(plot(aerep_new, what = "med75", study = "A", verbose = FALSE), "ggplot") + + aerep_new <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE) + expect_s3_class(plot(aerep_new, what = "med75", study = "A", verbose = FALSE), "ggplot") }) diff --git a/tests/testthat/test_check_df_visit.R b/tests/testthat/test_check_df_visit.R index 3e6cb52..ede601e 100644 --- a/tests/testthat/test_check_df_visit.R +++ b/tests/testthat/test_check_df_visit.R @@ -27,7 +27,7 @@ test_that("check_df_visit() must thrown an error when n_ae and visit columns are mutate_at(vars(n_ae, visit), as.character) %>% check_df_visit() }, - regexp = "n_ae and vist columns must be numeric" + regexp = "n_ae and visit columns must be numeric" ) }) diff --git a/tests/testthat/test_data.R b/tests/testthat/test_data.R index 78b5a3b..fde64e5 100644 --- a/tests/testthat/test_data.R +++ b/tests/testthat/test_data.R @@ -2,13 +2,17 @@ test_that("check S3 test data reproducibility", { aerep_check <- simaerep( + r = 100, df_visit_test, param_sim_sites = list( r = 100 ) ) - expect_equal(aerep_check, aerep_test) + aerep_test$df_eval <- arrange(aerep_test$df_eval, study_id, site_number) + + expect_equal(aerep_check$df_sim_sites, aerep_test$df_sim_sites) + expect_equal(aerep_check$df_eval, aerep_test$df_eval) visit_check <- orivisit(df_visit_test) @@ -38,6 +42,7 @@ test_that("check standard test data reproducibility", { df_visit_check <- dplyr::bind_rows(df_visit1, df_visit2) aerep <- simaerep( + r = 100, df_visit_check, param_sim_sites = list( poisson_test = TRUE, @@ -48,7 +53,7 @@ test_that("check standard test data reproducibility", { expect_equal(df_visit_test, df_visit_check) expect_equal(df_site_test, aerep$df_site) expect_equal(df_sim_sites_test, aerep$df_sim_sites) - expect_equal(df_eval_test, aerep$df_eval) + expect_equal(arrange(df_eval_test, study_id, site_number), aerep$df_eval) }) @@ -88,6 +93,104 @@ test_that("check portfolio performance test data reproducibility", { expect_equal(df_site_max_test, df_site_max_check) expect_equal(df_config_test, df_config_check) expect_equal(df_portf_test, df_portf_check) - expect_equal(df_scen_test, df_scen_check) - expect_equal(df_perf_test, df_perf_check) + + # we have fixed a mistake in sim_ur_scenarios therefore + # the results have diverged and will not match anyomore + # at this point we do not want to update the test data + # to demonstrate consistency over major releases + + # expect_equal(df_scen_test, df_scen_check) #nolint + # expect_equal(df_perf_test, df_perf_check) #nolint +}) + + +test_that("sim_ur() ae_count as expected for max patient visits", { + + n_ae_test <- df_visit_test %>% + filter(visit == max(visit), .by = c("study_id", "site_number", "patnum")) %>% + filter(site_number == "S0001", study_id == "A") %>% + pull(n_ae) %>% + sum() + + n_ae_0p5 <- df_visit_test %>% + sim_ur(study_id = "A", site_number = "S0001", ur_rate = 0.5) %>% + filter(visit == max(visit), .by = c("study_id", "site_number", "patnum")) %>% + filter(site_number == "S0001", study_id == "A") %>% + pull(n_ae) %>% + sum() + + expect_true(n_ae_test == n_ae_0p5 * 2) + +}) + + +test_that("sim_ur() and sim_ur_scenario() must give similar results", { + + df_sim_ur_scenarios <- sim_ur_scenarios( + df_visit_test, + extra_ur_sites = 0, + ur_rate = c(0.5, 1), + parallel = FALSE, + poisson = FALSE, + prob_lower = TRUE, + progress = FALSE, + check = FALSE, + site_aggr_args = list(method = "med75_adj") + ) %>% + arrange(study_id, site_number, ur_rate) + + # sim_ur ------------------------------------- + + perf <- function(df_visit, study_id, site_number, ur_rate) { + df_vs_study <- df_visit %>% + sim_ur(study_id, site_number, ur_rate) + + df_vs_study %>% + simaerep(under_only = TRUE, progress = FALSE, check = FALSE) %>% + .$df_eval %>% + filter(.data$site_number == .env$site_number) + } + + df_grid <- df_visit_test %>% + distinct(study_id, site_number) %>% + mutate(ur_rate = list(c(0, 0.5, 1))) %>% + unnest(ur_rate) + + df_sim_ur <- df_grid %>% + mutate( + perf = purrr::pmap( + list(study_id, site_number, ur_rate), + function(x, y, z) perf(df_visit_test, x, y, z) + ) + ) %>% + select(- study_id, - site_number) %>% + unnest(perf) %>% + arrange(study_id, site_number, ur_rate) + + # compare ------------------------------------ + + cols_equal <- c( + "study_id", + "site_number", + "ur_rate", + "visit_med75", + "n_pat_with_med75", + "mean_ae_study_med75" + ) + + expect_equal( + df_sim_ur_scenarios[, cols_equal], + df_sim_ur[, cols_equal] + ) + + # sim_ur calculates the number of AEs to be removed based + # on tha latest visit of each patient and removes them from + # the beginning. This can give smaller AE counts than removing + # a fraction of AEs from visit_med75. + + expect_true(all( + round(df_sim_ur$mean_ae_site_med75, 5) <= + round(df_sim_ur_scenarios$mean_ae_site_med75, 5) + )) + }) diff --git a/tests/testthat/test_inframe.R b/tests/testthat/test_inframe.R new file mode 100644 index 0000000..bdfa112 --- /dev/null +++ b/tests/testthat/test_inframe.R @@ -0,0 +1,219 @@ +# test data is automatically loaded, check ./data-raw/generate_test_data.R + +test_that("inframe with and w/0 visit_med75 and default algorithm must flag same sites", { + + df_eval_med75 <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE)$df_eval + df_eval <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE)$df_eval + + expect_equal( + arrange(df_eval_test, study_id, site_number)$visit_med75, + df_eval_med75$visit_med75 + ) + + expect_equal( + arrange(df_eval_test, study_id, site_number)$n_pat_with_med75, + df_eval_med75$n_pat_with_med75 + ) + + expect_equal( + df_eval_med75 %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + df_eval_test %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + + expect_equal( + df_eval %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + df_eval_test %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + +}) + +test_that("inframe with visit_med75 same probabilities as default algorithm, tolerance 0.1", { + df_eval_inframe <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE)$df_eval + df_eval_default <- simaerep(df_visit_test, inframe = FALSE, visit_med75 = TRUE, under_only = FALSE)$df_eval + + df_eval_inframe <- df_eval_inframe %>% + mutate( + mean_ae_site_med75 = events / n_pat_with_med75 + ) + + cols_equal <- c( + "study_id", + "site_number", + "visit_med75", + "n_pat_with_med75", + "mean_ae_site_med75" + ) + + expect_equal(df_eval_inframe[, cols_equal], df_eval_default[, cols_equal]) + + expect_true( + all(near( + df_eval_inframe$prob_low, + df_eval_default$prob_low, + 0.1 + )) + ) + + expect_true( + all(near( + df_eval_inframe$prob_low_prob_ur, + df_eval_default$prob_low_prob_ur, + 0.1 + )) + ) + +}) + + +test_that("simaerep_inframe and simaerep_visit_med75 must have similar results", { + + df_eval_med75 <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE)$df_eval + df_eval <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE)$df_eval + + expect_equal( + arrange(df_eval_test, study_id, site_number)$visit_med75, + df_eval_med75$visit_med75 + ) + + expect_equal( + arrange(df_eval_test, study_id, site_number)$n_pat_with_med75, + df_eval_med75$n_pat_with_med75 + ) + + expect_equal( + df_eval_med75 %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + df_eval_test %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + + expect_equal( + df_eval %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + df_eval_test %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + +}) + + +test_that("simaerep_inframe must have identical counts and flags with duckdb backend", { + + df_eval_med75 <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = TRUE)$df_eval + df_eval <- simaerep(df_visit_test, inframe = TRUE, visit_med75 = FALSE)$df_eval + + con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") + df_r <- tibble(rep = seq(1, 1000)) + + dplyr::copy_to(con, df_visit_test, "visit") + dplyr::copy_to(con, df_r, "r") + + tbl_visit <- tbl(con, "visit") + tbl_r <- tbl(con, "r") + + tbl_eval <- simaerep(tbl_visit, r = tbl_r, visit_med75 = FALSE)$df_eval + tbl_eval_med75 <- simaerep(tbl_visit, r = tbl_r, visit_med75 = TRUE)$df_eval + + cols_identical <- c("study_id", "site_number", "events", "visits", "events_per_visit_site") + + expect_equal( + df_eval %>% + select(all_of(cols_identical)), + tbl_eval %>% + dplyr::collect() %>% + arrange(study_id, site_number) %>% + select(all_of(cols_identical)) + ) + + expect_equal( + df_eval_med75 %>% + select(all_of(cols_identical)), + tbl_eval_med75 %>% + dplyr::collect() %>% + arrange(study_id, site_number) %>% + select(all_of(cols_identical)) + ) + + expect_equal( + df_eval_med75 %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + tbl_eval_med75 %>% + dplyr::collect() %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + + expect_equal( + df_eval %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number), + tbl_eval %>% + dplyr::collect() %>% + arrange(study_id, site_number) %>% + filter(prob_low_prob_ur >= 0.95) %>% + pull(site_number) + ) + + DBI::dbDisconnect(con) + +}) + +test_that("p.adjust result near p_adjust_bh_inframe", { + x <- rnorm(50, mean = c(rep(0, 500), rep(3, 500))) + p <- 2 * pnorm(sort(-abs(x))) + + df <- tibble( + study_id = "A", + p = p + ) %>% + mutate( + pbase = p.adjust(p, method = "BH") + ) %>% + p_adjust_bh_inframe("p", "_simaerep") + + expect_true(all(near(df$pbase, df$p_adj, 5))) + +}) + +test_that("p.adjust result near p_adjust_bh_inframe with duckdb", { + x <- rnorm(50, mean = c(rep(0, 500), rep(3, 500))) + p <- 2 * pnorm(sort(-abs(x))) + + df <- tibble( + study_id = "A", + p = p + ) %>% + mutate( + pbase = p.adjust(p, method = "BH") + ) + + con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") + dplyr::copy_to(con, df, "df") + tbl_df <- dplyr::tbl(con, "df") + + tbl_df <- tbl_df %>% + p_adjust_bh_inframe("p", "_simaerep") + + df <- collect(tbl_df) + + expect_true(all(near(df$pbase, df$p_adj, 5))) + +}) diff --git a/tests/testthat/test_site_aggr.R b/tests/testthat/test_site_aggr.R index 7eb59e8..a150307 100644 --- a/tests/testthat/test_site_aggr.R +++ b/tests/testthat/test_site_aggr.R @@ -27,7 +27,7 @@ test_that("site_aggr() must return different results for method = 'med75_adj' an df_site_old <- site_aggr(df_visit_test, method = "med75") expect_false(identical(df_site_adj, df_site_old)) - expect_true(identical(df_site_adj, df_site_test)) + expect_equal(df_site_adj, df_site_test) }) diff --git a/vignettes/_portfolio_perf.Rmd b/vignettes/_portfolio_perf.Rmd index cd2c7db..49468ca 100644 --- a/vignettes/_portfolio_perf.Rmd +++ b/vignettes/_portfolio_perf.Rmd @@ -26,7 +26,7 @@ suppressPackageStartupMessages(library(simaerep)) # RAM ~26 GB # plan 4GB per core -plan(multisession, workers = 6) +plan(multisession, workers = 4) ``` # Introduction @@ -38,7 +38,7 @@ We want to define minimal requirements for simulating test data that reflects re These simulations take some time to run and require multiple cores and appropriate memory. Rendering articles in {pkgdown} can be a bit unstable so we recommend to render first using pure {rmarkdown} to generate the intermediate csv files. ```{r perf, eval = FALSE} -rmarkdown::render("vignettes/_portfolio_perf.Rmd", knit_root_dir = "/home/koneswab/simaerep") +rmarkdown::render("vignettes/_portfolio_perf.Rmd", knit_root_dir = paste0(getwd(), "/vignettes")) ``` @@ -484,12 +484,15 @@ wr <- function(df) { parallel = FALSE, poisson = TRUE, prob_lower = TRUE, - progress = FALSE) + progress = FALSE, + check = FALSE) return(df_scen) } df_prep <- df_config %>% select(- max_visit_sd, - max_visit_mean, - ae_per_visit_mean) %>% + # we exclude studies running on average over 5 years + filter(mean(max_days_mean) <= 5 * 365, .by = study_id) %>% rename(max_visit_sd = max_days_sd, max_visit_mean = max_days_mean, ae_per_visit_mean = ae_per_day_mean) %>% diff --git a/vignettes/gsm_perf.Rmd b/vignettes/funnel_perf.Rmd similarity index 55% rename from vignettes/gsm_perf.Rmd rename to vignettes/funnel_perf.Rmd index 0de56d7..8d5fbb4 100644 --- a/vignettes/gsm_perf.Rmd +++ b/vignettes/funnel_perf.Rmd @@ -1,5 +1,5 @@ --- -title: "Comparing {simaerep} and {gsm} Performance" +title: "Comparing {simaerep} and Funnel Plot Performance" output: html_document: toc: true @@ -30,7 +30,7 @@ plan(multisession, workers = 6) # Install {gsm} ```{r eval = FALSE} -devtools::install_github("Gilead-BioStats/gsm", ref = "main") +devtools::install_github("Gilead-BioStats/gsm@v1.9.2", ref = "main") ``` @@ -119,11 +119,11 @@ We can confirm that the AE rates in the "flexible" portfolio are not constant. M we see that the AE rate profile is very unique for each study. -# Apply {gsm} +# Funnel Plots {gsm} ## Example -Here we demonstrate how to use the {gsm} package on our simulated portfolios. +Here we demonstrate how to use the {gsm} package on our simulated portfolios so that we can get a good visualization of the funnel plot. ```{r} @@ -137,7 +137,7 @@ get_SUBJ <- function(df_portf) { get_AE <- function(df_portf) { - df_portf_fix %>% + df_portf %>% select(study_id, subjid = patnum, n_ae) %>% summarise(n_ae = max(n_ae), .by = c(study_id, subjid)) %>% filter(n_ae > 0) %>% @@ -182,68 +182,70 @@ chart ## Simulate UR -We write a function that removes a given ratio of AEs from one site in the data set and returns its z-score. -```{r} -sim_site_ur_gsm <- function(site, ur_rate, dfTransformed) { - dfTransformed <- dfTransformed %>% - mutate( - Numerator = ifelse(GroupID == site, Numerator * (1 - ur_rate), Numerator), - Metric = Numerator / Denominator - ) - - gsm::Analyze_NormalApprox(dfTransformed) %>% - filter(GroupID == site) %>% - pull(Score) -} +# UR Funnel -sim_site_ur_gsm("4747", ur_rate = 0.75, dfTransformed) -``` - -We write another function that systematically applies this `sim_site_ur_gsm` across all sites in all studies across a range of under-reporting ratios. +We write out own funnel function as adapted from {gsm} ```{r} +funnel_ur <- function(df, site, ur_rate) { + df %>% + filter(visit == max(visit), .by = patnum) %>% + summarise( + Metric = sum(.data$n_ae) / sum(.data$visit), + n_ae = sum(n_ae), + visit = sum(visit), + .by = "site_number" + ) %>% + mutate( + n_ae = ifelse(site_number == site, n_ae * (1 - ur_rate), n_ae), + Metric = n_ae / visit + ) %>% + mutate( + vMu = sum(.data$n_ae) / sum(.data$visit), + z_0 = ifelse(.data$vMu == 0, + 0, + (.data$Metric - .data$vMu) / + sqrt(.data$vMu / .data$visit) + ), + phi = mean(.data$z_0^2), + z_i = ifelse(.data$vMu == 0 | .data$phi == 0, + 0, + (.data$Metric - .data$vMu) / + sqrt(.data$phi * .data$vMu / .data$visit) + ) + ) %>% + filter(site_number == site) %>% + pull(z_i) +} -sim_ur_gsm <- function(dfSUBJ, dfAE) { - dfSUBJ %>% - inner_join(dfAE, by = "study_id") %>% +sim_ur_funnel <- function(df) { + df %>% + group_by(study_id) %>% + nest() %>% ungroup() %>% mutate( - trans = map2(data.x, data.y, ~ gsm::AE_Map_Raw(list(dfSUBJ = .x, dfAE = .y))), - trans = map(trans, ~ gsm::Transform_Rate(., strNumeratorCol = "Count", strDenominatorCol = "Exposure")), - sites = map(data.x, ~ distinct(., siteid)) + sites = map(data, ~ distinct(., site_number)) ) %>% - select(- starts_with("data.")) %>% unnest(sites) %>% mutate(ur = list(tibble(ur_rate = c(0, 0.1, 0.25, 0.5, 0.75, 1)))) %>% unnest(ur) %>% mutate( - score = pmap_dbl(list(siteid, ur_rate, trans), sim_site_ur_gsm, .progress = TRUE) + score = pmap_dbl(list(data, site_number, ur_rate), funnel_ur, .progress = TRUE) ) } -df_sim_gsm_fix <- sim_ur_gsm(dfSUBJ_fix, dfAE_fix) +df_sim_ur_funnel_flex <- sim_ur_funnel(df_portf_flex) ``` ```{r} -df_sim_gsm_fix -``` - -We repeat the same steps for the portfolio with the flexible AE rates. - -```{r} - -dfSUBJ_flex <- get_SUBJ(df_portf_flex) -dfAE_flex<- get_AE(df_portf_flex) - - -df_sim_gsm_flex <- sim_ur_gsm(dfSUBJ_flex, dfAE_flex) +df_sim_ur_funnel_fix <- sim_ur_funnel(df_portf_fix) ``` # UR {simaerep} -We simulate under-reporting for both portfolios using {simaerep} +We simulate under-reporting for both portfolios using {simaerep} using `sim_ur_scenarios()`. ```{r} df_sim_simaerep_fix <- sim_ur_scenarios( @@ -273,38 +275,149 @@ df_sim_simaerep_flex <- sim_ur_scenarios( ## Combine Results +As the funnel plot score does not use multiplicity correction, we also compare the funnel plot score against the {simaerep} score w/o multiplicity correction. + ```{r} -df_sim_gsm_fix$ae_rate <- "AE rate: fix" -df_sim_gsm_flex$ae_rate <- "AE rate: flexible" df_sim_simaerep_fix$ae_rate <- "AE rate: fix" df_sim_simaerep_flex$ae_rate <- "AE rate: flexible" +df_sim_ur_funnel_fix$ae_rate <- "AE rate: fix" +df_sim_ur_funnel_flex$ae_rate <- "AE rate: flexible" + + -df_sim_thresh2 <- bind_rows(df_sim_gsm_fix, df_sim_gsm_flex) %>% +df_sim_fun_thresh2 <- bind_rows(df_sim_ur_funnel_fix, df_sim_ur_funnel_flex) %>% mutate( - is_ur = score <= -2, - type = "{gsm} - thresh: -2", - site_number = siteid + type = "funnel", ) %>% - select(type, ae_rate, study_id, site_number, ur_rate, is_ur, score) + select(type, ae_rate, study_id, site_number, ur_rate, score) df_sim_simaerep_threshp95 <- bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>% mutate( - is_ur = prob_low_prob_ur >= 0.95, - type = "{simaerep} - thresh: 0.95" + type = "{simaerep}" ) %>% - select(type, ae_rate, study_id, site_number, ur_rate, is_ur, score = prob_low_prob_ur) + select(type, ae_rate, study_id, site_number, ur_rate, score = prob_low_prob_ur) + +df_sim_simaerep_threshp95_no_mult <- bind_rows(df_sim_simaerep_fix, df_sim_simaerep_flex) %>% + mutate( + type = "{simaerep} no mult" + ) %>% + mutate( + score = 1 - prob_low + ) %>% + select(type, ae_rate, study_id, site_number, ur_rate, score) df_eval <- bind_rows( - df_sim_thresh2, df_sim_simaerep_threshp95, + df_sim_simaerep_threshp95_no_mult, + df_sim_fun_thresh2 ) ``` +## AUC -## Aggregate +We continue by comparing the ROC-AUC. + +```{r} + +get_roc <- function(df_ur, df_nr) { + df <- bind_rows(df_ur, df_nr) + pROC::roc(df, response = "is_ur", predictor = "score", quiet = TRUE) +} + +# use 0 scenario to mix with ur scenario and calculate auc from scores +df_nr <- df_eval %>% + filter(ur_rate == 0) %>% + mutate(is_ur = "no") %>% + select(- ur_rate) %>% + group_by(type, study_id, ae_rate) %>% + nest() %>% + ungroup() %>% + rename(data_nr = data) + + +df_ur <- df_eval %>% + filter(ur_rate > 0) %>% + mutate(is_ur = "yes") %>% + group_by(type, study_id, ur_rate, ae_rate) %>% + nest() %>% + ungroup() %>% + rename(data_ur = data) + + +df_auc <- df_ur %>% + left_join( + df_nr, + by = c("type", "study_id", "ae_rate") + ) %>% + mutate( + roc = map2(data_ur, data_nr, get_roc, .progress = TRUE), + auc = map_dbl(roc, pROC::auc, .progress = TRUE) + ) %>% + select(type, study_id, ur_rate, ae_rate, roc, auc) + +``` + +### Table + +```{r} +df_auc %>% + summarise( + sd_auc = sd(.data$auc), + auc = mean(.data$auc), + .by = c(type, ur_rate, ae_rate) + ) %>% + knitr::kable(digit = 3) +``` + +### Plot + +```{r} + +df_auc %>% + ggplot(aes(type, auc)) + + geom_boxplot(aes(fill = type)) + + facet_grid(ae_rate ~ ur_rate) + + scale_fill_brewer(palette = "Dark2") + + theme(axis.text.x = element_blank()) + +``` + +## Metrics + +### Thresholds + +We set the thresholds for funnel and simaerep w/o multiplicity correction so that we get the same fpr as for simaerep with multiplicity correction and the established threshold of 0.95 + +```{r} + +thresh_default <- 0.95 + +target_fpr <- df_eval %>% + filter(ur_rate == 0, type == "{simaerep}") %>% + summarise(fpr = sum(score >= thresh_default) / n()) %>% + pull(fpr) + +thresh_no_mult <- df_eval %>% + filter(ur_rate == 0, type == "{simaerep} no mult") %>% + pull(score) %>% + quantile(1 - target_fpr) + +thresh_funnel <- df_eval %>% + filter(ur_rate == 0, type == "funnel") %>% + pull(score) %>% + quantile(target_fpr) + +target_fpr + +thresh_no_mult + +thresh_funnel +``` + +### Aggregate ```{r} get_prop_test_ci95 <- function(..., ix) { @@ -319,44 +432,45 @@ get_prop_test_ci95 <- function(..., ix) { aggr_results <- function(df_eval) { -df_perf <- df_eval %>% - summarise( - n = n(), - .by = c(type, ae_rate, ur_rate, is_ur) - ) %>% - pivot_wider( - names_from = is_ur, - values_from = n, - names_prefix = "is_ur_", - values_fill = 0 - ) %>% - mutate( - n_sites = is_ur_TRUE + is_ur_FALSE + is_ur_NA, - ratio = is_ur_TRUE / n_sites, - ratio_type = ifelse(ur_rate == 0, "fpr", "tpr"), - ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)), - ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)) - ) -} + df_perf <- df_eval %>% + mutate( + is_ur = case_when( + type == "{simaerep}" ~ score >= thresh_default, + type == "{simaerep} no mult" ~ score >= thresh_no_mult, + type == "funnel" ~ score <= thresh_funnel + ) + ) %>% + summarise( + n = n(), + .by = c(type, ae_rate, ur_rate, is_ur) + ) %>% + pivot_wider( + names_from = is_ur, + values_from = n, + names_prefix = "is_ur_", + values_fill = 0 + ) %>% + mutate( + n_sites = is_ur_TRUE + is_ur_FALSE, + ratio = is_ur_TRUE / n_sites, + ratio_type = ifelse(ur_rate == 0, "fpr", "tpr"), + ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)), + ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)) + ) + } df_perf <- aggr_results(df_eval) ``` -# Results - -## Table +### Table ```{r} df_perf %>% - knitr::kable(digits = 3) + knitr::kable(digits = 4) ``` - - -## Plot Performance Metrics - -- {gsm} has better performance than {simaerep} when the AE rate is fixed, while {simaerep} greatly outperforms {gsm} when the AE rate is flexible and mimics the AE rates encountered in real study data sets. +### Plot ```{r, plot, fig.width=10, fig.height = 12} @@ -364,25 +478,31 @@ plot_perf <- function(df_perf) { df_perf %>% mutate(ur_rate = paste0("under-reporting rate: ", ur_rate, " - ", ratio_type), - ur_rate = ifelse(str_detect(ur_rate, "fpr"), "fpr", ur_rate)) %>% + ur_rate = ifelse(str_detect(ur_rate, "fpr"), "fpr", ur_rate), + ae_rate = forcats::fct_rev(factor(ae_rate))) %>% group_by(ur_rate) %>% ggplot(aes(type, ratio)) + - geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type), linewidth = 1) + - facet_grid(ur_rate ~ ae_rate) + + geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type, linetype = ae_rate), linewidth = 1) + + facet_wrap(~ ur_rate, ncol = 1) + coord_flip() + theme(legend.position = "bottom") + labs( x = "", y = "CI95 Performance Ratio", - title = "{simaerep} vs {gsm} Performance" + title = "{simaerep} vs Funnel-Plot Performance" ) + - scale_color_manual(values = c("#5491CC", "#F46626")) + scale_color_brewer(palette = "Dark2") } plot_perf(df_perf) ``` +# Summary + +- Funnel plot expects constant event rates over time. Performance decrease when event rates are flexible. +- {simaerep} performance is unaffected by flexible event rates. +- {simaerep} detection rates with comparable false positive rates are greater than funnel plot detection rates when event rates are flexible. ```{r close} plan(sequential) diff --git a/vignettes/inframe.Rmd b/vignettes/inframe.Rmd new file mode 100644 index 0000000..afd8855 --- /dev/null +++ b/vignettes/inframe.Rmd @@ -0,0 +1,203 @@ +--- +title: "Inframe Simulation Using Table Operations" +output: + html_document: + toc: true + toc_depth: 3 + toc_float: true + number_sections: true + code_folding: show + collapse: false +editor_options: + chunk_output_type: console +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE, cache = FALSE, message = FALSE) +``` + +# Load +```{r load} +suppressPackageStartupMessages(library(dplyr)) +suppressPackageStartupMessages(library(tibble)) +suppressPackageStartupMessages(library(tidyr)) +suppressPackageStartupMessages(library(simaerep)) +``` + +# Introduction + +With the latest `0.6.0` release we have added an alternative version of the {simaerep} algorithm that was coded using solely `dbplyr` compatible table operations. + +- expand the patients for each site `r` times +- join each patient with a random eligible patient from same study +- for each replicate calculate event per visit rate per site +- calculate the ratio of having a lower event per visit rate than actually observed + +This comes with the following advantages and disadvantages: + +- Patients are individually matched with patients that have reached the same visit in the study. No need to pick visit_med75 as an evaluation point. +- `dbplyr` compatibility means that code execution can be done in a database back-end as opposed to in-memory. +- Matching patients individually is more costly, this increases in-memory computation time +- Limited patient sample pool for patients that have more visits than other patients in study. + +# Sample Data + +```{r} +set.seed(1) + +df_visit <- sim_test_data_study( + n_pat = 1000, # number of patients in study + n_sites = 100, # number of sites in study + frac_site_with_ur = 0.05, # fraction of sites under-reporting + ur_rate = 0.4, # rate of under-reporting + ae_per_visit_mean = 0.5 # mean AE per patient visit +) + +df_visit$study_id <- "A" +``` + +# Patient-Level Matching + +Here we use the standard version of the algorithm. + +```{r} +aerep_trad <- simaerep(df_visit) +plot(aerep_trad) +``` + +To use the patient-level matching algorithm we set `inframe=TRUE` and `visit_med75=FALSE`. + +The original algorithm uses fixed seeds before sampling while the inframe method does not. In order to obtain consistent results we need to manually set a seed. + +```{r} +set.seed(1) + +aerep_inframe <- simaerep( + df_visit, + inframe = TRUE, + visit_med75 = FALSE +) +``` + +The plot shows that for all sites 10/10 patients were used and none were excluded. We also observe that +the site average has become more noisy as less patients are used to calculate the averages for the higher +visit numbers. + +```{r} +plot(aerep_inframe) +``` + +The inframe method includes this noisier data but does not compare average event counts but event per visit rates. +We can find `events_per_visit_site` and `events_per_visit_study` in `df_eval`. The latter is the average event rate +obtained in the simulation in which each patient has been resampled according to its maximum visit. + +```{r} +aerep_inframe$df_eval +``` + +We can also force the inframe method to use the visit_med75 this will pre-filter `df_visit`, which adds an extra step +and decreases performance. + +```{r} + +set.seed(1) + +aerep_inframe_visit_med75 <- simaerep( + df_visit, + inframe = TRUE, + visit_med75 = TRUE +) + +plot(aerep_inframe_visit_med75) +``` + + +# DB + +We can demonstrate the database-backend compatibility by using a connection to a in memory `duckdb` database. In order to set the number of replications we need to create a new table in our back-end that has one column with as many rows as the desired replications. + +A lazy reference to this table can then be passed to the `r` parameter. + +```{r} +con <- DBI::dbConnect(duckdb::duckdb(), dbdir = ":memory:") +df_r <- tibble(rep = seq(1, 1000)) + +dplyr::copy_to(con, df_visit, "visit") +dplyr::copy_to(con, df_r, "r") + +tbl_visit <- tbl(con, "visit") +tbl_r <- tbl(con, "r") + + +aerep <- simaerep(tbl_visit, r = tbl_r, visit_med75 = FALSE) +``` + +When inspecting `df_eval` we see that it is still a lazy table object. + +```{r} +aerep$df_eval +``` + +We can convert it to sql code. The `cte` option makes the sql code more readable. + +```{r} +sql_eval <- dbplyr::sql_render(aerep$df_eval, sql_options = dbplyr::sql_options(cte = TRUE)) +stringr::str_trunc(sql_eval, 500) +``` + +We can take that code and wrap it in a `CREATE TABLE` statement + +```{r} +sql_create <- glue::glue("CREATE TABLE eval AS ({sql_eval})") +DBI::dbExecute(con, sql_create) +``` + +Retrieve the new table from the database. + +```{r} +tbl_eval <- tbl(con, "eval") +tbl_eval +``` + +We plot the results from the {simaerep} object. + +```{r} +plot(aerep) +``` + +Or more efficiently by using `plot_study()` when we have already written the `simaerep` results into the database. +Here we avoid that the results are being recalculated just for the sake of creating a plot. However this requires +that we save `df_site` to the database as well. + +```{r} +sql_site <- dbplyr::sql_render(aerep$df_site) +DBI::dbExecute(con, glue::glue("CREATE TABLE site AS ({sql_site})")) +tbl_site <- tbl(con, "site") + +plot_study(tbl_visit, tbl_site, tbl_eval, study = "A") +``` + + +```{r} +DBI::dbDisconnect(con) +``` + + + +# In Memory Calculation Times + +Here we perform some examplary tests to illustrate the increase in-memory calculation time of the inframe calculation method. + +This is the calculation time for the default settings +```{r} +system.time({simaerep(df_visit, inframe = FALSE, visit_med75 = TRUE, under_only = TRUE, progress = FALSE)}) +``` + + +inframe calculation time is higher + +```{r} +system.time({simaerep(df_visit, inframe = TRUE, visit_med75 = FALSE, under_only = TRUE)}) +``` + + diff --git a/vignettes/performance.Rmd b/vignettes/performance.Rmd new file mode 100644 index 0000000..35abc85 --- /dev/null +++ b/vignettes/performance.Rmd @@ -0,0 +1,380 @@ +--- +title: "Statistical Performance" +output: + html_document: + toc: true + toc_depth: 3 + toc_float: true + number_sections: true + code_folding: show + collapse: false +editor_options: + chunk_output_type: console +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE, cache = FALSE, message = FALSE) +``` + +# Load +```{r load} +suppressPackageStartupMessages(library(dplyr)) +suppressPackageStartupMessages(library(tibble)) +suppressPackageStartupMessages(library(tidyr)) +suppressPackageStartupMessages(library(simaerep)) +suppressPackageStartupMessages(library(purrr)) +suppressPackageStartupMessages(library(stringr)) +``` + +# Introduction + + + +# Performance (Statistical) + +```{r} +suppressPackageStartupMessages(library(furrr)) +suppressPackageStartupMessages(library(future)) + +plan(multisession, workers = 6) +``` + + +## Load Portfolio + +```{r eval = FALSE} +df_config <- readr::read_csv("ae_conf_20240220.csv") +df_ae_rates <- readr::read_csv("ae_rates_20240220.csv") + +df_portf_flex <- sim_test_data_portfolio(df_config, df_ae_rates = df_ae_rates, parallel = TRUE, progress = TRUE) + +df_portf_flex %>% + readr::write_csv("portf.csv") +``` + +```{r} +df_portf_flex <- readr::read_csv("portf.csv") +``` + + +## Simulating Under Reporting + +Here we reanalyze performance of {simaerep} integrating the lessons learned from previous versions. We will apply the following. + +- generate portfolio using flexible AE rates +- remove AEs directly from the data set and not from an aggregated metric using `sim_ur()` +- set threshold for confusion matrix so that all methods have similar fpr +- test the following {simaerep} parameters **with and without correcting for multiplicity**: + * default algorithm + * default algorithm with active over-reporting scoring + * inframe algorithm with visit_med75 + * inframe algorithm +- test the following outlier detection methods + * box-plot + * funnel-plot + + +## Functions + + +```{r} + +funnel <- function(df) { + df %>% + filter(visit == max(visit), .by = patnum) %>% + summarise( + Metric = sum(.data$n_ae) / sum(.data$visit), + n_ae = sum(n_ae), + visit = sum(visit), + .by = "site_number" + ) %>% + mutate( + vMu = sum(.data$n_ae) / sum(.data$visit), + z_0 = ifelse(.data$vMu == 0, + 0, + (.data$Metric - .data$vMu) / + sqrt(.data$vMu / .data$visit) + ), + phi = mean(.data$z_0^2), + z_i = ifelse(.data$vMu == 0 | .data$phi == 0, + 0, + (.data$Metric - .data$vMu) / + sqrt(.data$phi * .data$vMu / .data$visit) + ) + ) +} + +box <- function(df) { + df <- df %>% + filter(visit == max(visit), .by = patnum) %>% + summarise( + event_per_visit = sum(.data$n_ae) / sum(.data$visit), + .by = "site_number" + ) + + bx <- boxplot.stats(df$event_per_visit) + + df <- df %>% + mutate( + box_out = event_per_visit < bx$stats[1] + ) + +} + + +perf <- function(df_visit, study_id, site_number, ur_rate) { + df_vs_study <- df_visit %>% + sim_ur(study_id, site_number, ur_rate) + + df_visit_med75 <- df_vs_study %>% + simaerep(under_only = TRUE, progress = FALSE, check = FALSE) %>% + .$df_eval %>% + filter(.data$site_number == .env$site_number) + + df_visit_med75_over <- df_vs_study %>% + simaerep(under_only = FALSE, progress = FALSE, check = FALSE) %>% + .$df_eval %>% + filter(.data$site_number == .env$site_number) + + df_inframe <- df_vs_study %>% + simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = FALSE) %>% + .$df_eval %>% + filter(.data$site_number == .env$site_number) + + df_inframe_visit_med75 <- df_vs_study %>% + simaerep(inframe = TRUE, under_only = TRUE, check = FALSE, visit_med75 = TRUE) %>% + .$df_eval %>% + filter(.data$site_number == .env$site_number) + + funnel_zi <- funnel(df_vs_study) %>% + filter(.data$site_number == .env$site_number) %>% + pull(z_i) + + box_out <- box(df_vs_study) %>% + filter(.data$site_number == .env$site_number) %>% + pull(box_out) + + tibble( + score_visit_med75 = df_visit_med75$prob_low_prob_ur, + score_visit_med75_no_mult = 1 - df_visit_med75$prob_low, + score_visit_med75_over = df_visit_med75_over$prob_low_prob_ur, + score_visit_med75_over_no_mult = 1 - df_visit_med75_over$prob_low, + score_inframe = df_inframe$prob_low_prob_ur, + score_inframe_no_mult = 1 - df_inframe$prob_low, + score_inframe_visit_med75 = df_inframe_visit_med75$prob_low_prob_ur, + score_inframe_visit_med75_no_mult = 1 - df_inframe_visit_med75$prob_low, + score_funnel_zi = funnel_zi, + score_box_out = as.integer(box_out), + stat_visit_med75_visit_med75 = df_visit_med75$visit_med75, + stat_visit_med75_n_pat_with_med75 = df_visit_med75$n_pat_with_med75, + stat_visit_med75_mean_ae_site_med75 = df_visit_med75$mean_ae_site_med75, + stat_visit_med75_mean_ae_study_med75 = df_visit_med75$mean_ae_study_med75, + stat_inframe_visit_med75 = df_inframe_visit_med75$visit_med75, + stat_inframe_visit_med75_n_pat_with_med75 = df_inframe_visit_med75$n_pat_with_med75, + stat_inframe_visit_med75_events_per_visit_site = df_inframe_visit_med75$events_per_visit_site, + stat_inframe_visit_med75_events_per_visit_study = df_inframe_visit_med75$events_per_visit_study, + stat_inframe_n_pat = df_inframe$n_pat, + stat_inframe_events_per_visit_site = df_inframe$events_per_visit_site, + stat_inframe_events_per_visit_study = df_inframe$events_per_visit_study, + ) +} + +# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0) %>% unlist() +# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0.5) %>% unlist() +# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 0.75) %>% unlist() +# perf(df_portf_flex, study_id = "0010", site_number = "15153", ur_rate = 1) %>% unlist() + +``` + + +## Grid + +```{r} +df_grid <- df_portf_flex %>% + distinct(study_id, site_number) %>% + # to reduce calculation time we only take every xth study + filter(dense_rank(study_id)%%5 == 0) %>% + mutate(ur_rate = list(c(0, 0.1, 0.25, 0.5, 0.75, 1))) %>% + unnest(ur_rate) + +df_grid +``` + + + +## Apply + +```{r eval = FALSE} +with_progress_cnd( + df_perf <- df_grid %>% + mutate( + perf = purrr_bar( + list(study_id, site_number, ur_rate), + .purrr = furrr::future_pmap, + .f = function(x, y, z) perf(df_portf_flex, x, y, z), + .purrr_args = list(.options = furrr_options(seed = TRUE)), + .steps = nrow(.) + ) + ) +) + +df_perf %>% + unnest(perf) %>% + readr::write_csv("perf.csv") +``` + + + +```{r} +df_perf <- readr::read_csv("perf.csv", show_col_types = FALSE) +``` + + +```{r} +df_perf_long <- df_perf %>% + pivot_longer(cols = - c(study_id, site_number, ur_rate), names_to = "type", values_to = "score") %>% + filter(startsWith(type, "score_")) %>% + mutate(type = stringr::str_replace(type, "score_", "")) +``` + + +# Evaluation + + +### Thresholds + +We set the thresholds so that we get a fpr of 0.01. + +Note that this results in probability thresholds ~ 0.99 for scores w/o multiplicity correction and in the recommended funnel plot score threshold of -2. + +```{r} + +target_fpr <- 0.01 + +df_thresh <- df_perf_long %>% + group_by(type) %>% + nest() %>% + ungroup() %>% + mutate( + data = map(data, ~ filter(., ur_rate == 0)), + thresh1 = map_dbl(data, ~ quantile(pull(., score), 1 - target_fpr)), + thresh2 = map_dbl(data, ~ quantile(pull(., score), target_fpr)), + thresh = ifelse(type == "funnel_zi", thresh2, thresh1) + ) %>% + select(type, thresh) + +df_thresh +``` + +### Aggregate + +```{r} +get_prop_test_ci95 <- function(..., ix) { + + stopifnot(ix %in% c(1, 2)) + + tryCatch( + prop.test(...)$conf.int[ix], + error = function(cnd) c(NA, NA)[ix] + ) +} + +df_aggr <- df_perf_long %>% + left_join(df_thresh, by = "type") %>% + mutate( + is_ur = ifelse(type == "funnel_zi", score <= thresh, score >= thresh), + is_ur = ifelse(type == "box_out", score == 1, is_ur) + ) %>% + summarise( + n = n(), + .by = c(type, ur_rate, is_ur) + ) %>% + pivot_wider( + names_from = is_ur, + values_from = n, + names_prefix = "is_ur_", + values_fill = 0 + ) %>% + mutate( + n_sites = is_ur_TRUE + is_ur_FALSE, + ratio = is_ur_TRUE / n_sites, + ratio_type = ifelse(ur_rate == 0, "fpr", "tpr"), + ci95_low = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 1)), + ci95_high = map2_dbl(is_ur_TRUE, n_sites, ~ get_prop_test_ci95(.x, .y, ix = 2)), + type_strip = str_replace(type, "_no_mult", ""), + has_mult = ! str_detect(type, "no_mult") & ! type %in% c("funnel_zi", "box_out") + ) +``` + +### Table + +Methods: + +- **visit_med75:** default algorithm +- **visit_med75_over:** default algorithm including over-reporting score +- **inframe:** new algorithm using table operations and no visit_med75 +- **inframe visit_med75:** new algorithm using table operations and visit_med75 +- **funnel_zi:** funnel plot derived outlier detection +- **box_out:** box plot derived outlier detection + +FN: false negatives +TP: true positives + +```{r} +df_aggr %>% + select(method = type_strip, has_mult, ur_rate, FN = is_ur_FALSE, TP = is_ur_TRUE, n_sites, ratio_type, ratio, ci95_low, ci95_high) %>% + knitr::kable(digits = 4) + +``` + + +### Plot + +```{r, plot, fig.width=10, fig.height = 12} + +df_aggr %>% + mutate(ur_rate = paste0("under-reporting rate: ", ur_rate, " - ", ratio_type) + ) %>% + group_by(ur_rate) %>% + ggplot(aes(type, ratio)) + + geom_errorbar(aes(ymin = ci95_low, ymax = ci95_high, color = type_strip, alpha = has_mult), linewidth = 1) + + facet_wrap(~ ur_rate, ncol = 1) + + coord_flip() + + theme( + legend.position = "right", + axis.text.y = element_blank(), + axis.ticks.y = element_blank() + ) + + labs( + x = "", + y = "Ratio (CI95)", + title = "{simaerep} Performance", + color = "Method", + alpha = "Multiplicity Correction" + ) + + scale_color_manual(values = rev(RColorBrewer::brewer.pal(n = 6, name = "Dark2"))) + + scale_alpha_manual(values = c(1, 0.5)) + +``` + + +### Summary + +- new inframe method has slightly better performance than original algorithm + +> The new inframe methods compares event per visit rates without dropping any patients or AEs from the analysis. +This is likely to explain the performance increase. We observed a similar increase when we optimized visit_med75 +in past experiments. + +- multiplicity correction imposes a penalty on the true positive rate + +> This observation was already made by the Boeringer Ingelheim Team during the evaluation of {simaerep}. We can +now reporducibly confirm this. The unaltered probability score as returned by the bootstrap algorithm already provides +very realistic under-reporting probabilities. + +- {simaerep} outperforms simpler methods such as funnel plot and box plot outlier detection. + +> These controls confirm previous observations that were made during the {simaerep} validation. + + + diff --git a/vignettes/portfolio_perf.Rmd b/vignettes/portfolio_perf.Rmd index 777eb85..5d382fb 100644 --- a/vignettes/portfolio_perf.Rmd +++ b/vignettes/portfolio_perf.Rmd @@ -1,5 +1,5 @@ --- -title: "simaerep Portfolio Performance" +title: "(superseded) simaerep Portfolio Performance" output: html_document: toc: true @@ -38,7 +38,7 @@ We want to define minimal requirements for simulating test data that reflects re These simulations take some time to run and require multiple cores and appropriate memory. Rendering articles in {pkgdown} can be a bit unstable so we recommend to render first using pure {rmarkdown} to generate the intermediate csv files. ```{r perf, eval = FALSE} -rmarkdown::render("vignettes/_portfolio_perf.Rmd", knit_root_dir = "/home/koneswab/simaerep") +rmarkdown::render("vignettes/_portfolio_perf.Rmd", knit_root_dir = paste0(getwd(), "/vignettes")) ```